updated code

This commit is contained in:
rajesh
2024-09-23 13:31:02 +05:30
parent 398fb7cc43
commit d2df445edf
6 changed files with 232 additions and 24 deletions

View File

@@ -1,6 +1,18 @@
package net.gepafin.tendermanagement.config; package net.gepafin.tendermanagement.config;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import org.bouncycastle.util.io.pem.PemReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
@@ -14,6 +26,11 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt
import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.saml2.core.Saml2X509Credential;
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
@@ -23,6 +40,8 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter; import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector; import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityRequirement;
@@ -30,24 +49,29 @@ import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server; import io.swagger.v3.oas.models.servers.Server;
import net.gepafin.tendermanagement.config.jwt.JWTFilter; import net.gepafin.tendermanagement.config.jwt.JWTFilter;
import net.gepafin.tendermanagement.config.jwt.TokenProvider; import net.gepafin.tendermanagement.config.jwt.TokenProvider;
import net.gepafin.tendermanagement.entities.SamlResponseLogEntity;
import net.gepafin.tendermanagement.repositories.SamlResponseLogRepository;
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true) @EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig { public class SecurityConfig {
private final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
private final TokenProvider tokenProvider; private final TokenProvider tokenProvider;
@Value("${base-url}")
String baseUrl;
@Autowired
private SamlResponseLogRepository samlResponseLogRepository;
@Autowired @Autowired
public SecurityConfig(TokenProvider tokenProvider) { public SecurityConfig(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider; this.tokenProvider = tokenProvider;
} }
@Bean @Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager(); return config.getAuthenticationManager();
} }
@Bean @Bean
public PasswordEncoder passwordEncoder() { public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); return new BCryptPasswordEncoder();
@@ -75,8 +99,8 @@ public class SecurityConfig {
CorsConfiguration config = new CorsConfiguration(); CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*"); config.addAllowedOrigin("*");
config.addAllowedMethod("*"); config.addAllowedMethod("*");
config.addAllowedHeader("*"); config.addAllowedHeader("*");
config.setMaxAge(3600l); config.setMaxAge(3600l);
if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) { if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) {
@@ -86,24 +110,91 @@ public class SecurityConfig {
} }
return new CorsFilter(source); return new CorsFilter(source);
} }
@Bean @Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception { public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http http.csrf(AbstractHttpConfigurer::disable).authorizeHttpRequests(auth -> auth
.csrf(AbstractHttpConfigurer::disable) // Allow public access to the login endpoints
.authorizeHttpRequests(auth -> auth .requestMatchers("/v1/user/login").permitAll() // JWT-based login
.requestMatchers(mvc.pattern(HttpMethod.POST, "/v1/user/login")).permitAll() .requestMatchers("/v1/saml/**").permitAll() // JWT-based login
.requestMatchers("/swagger-ui/**").permitAll() .requestMatchers("/saml2/**").permitAll() // SAML login initiation
.requestMatchers("/v1/api-docs/**").permitAll() .requestMatchers("/swagger-ui/**").permitAll() // Swagger docs
.anyRequest().authenticated() .requestMatchers("/v1/api-docs/**").permitAll() // API docs
) .anyRequest().authenticated())
.sessionManagement(session -> session .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.addFilterBefore(corsFilter(), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(corsFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JWTFilter(tokenProvider), UsernamePasswordAuthenticationFilter.class); .addFilterBefore(new JWTFilter(tokenProvider), UsernamePasswordAuthenticationFilter.class)
// Add SAML2 login configuration (for BENEFICIARI)
/*
* .saml2Login(saml -> saml.loginPage("/saml/login") // Entry point for SAML
* login .defaultSuccessUrl("/") // Redirect after successful SAML login );
*/
.saml2Login(saml ->
saml.defaultSuccessUrl("/")
.successHandler((request, response, authentication) -> {
logger.error("SAML success login");
SamlResponseLogEntity samlResponseLogEntity = new SamlResponseLogEntity();
samlResponseLogEntity.setRequest(request.toString());
samlResponseLogEntity.setResponse(response.toString());
samlResponseLogEntity.setAuthenticationObject(authentication.toString());
samlResponseLogRepository.save(samlResponseLogEntity);
try {
ObjectMapper objectMapper = new ObjectMapper();
// Create a new SAML log entity
SamlResponseLogEntity samlResponseLogEntity1 = new SamlResponseLogEntity();
// Convert request, response, and authentication to JSON format
String requestJson = objectMapper.writeValueAsString(request.getParameterMap()); // Assuming request params to JSON
String responseJson = objectMapper.writeValueAsString(response); // This may need to be adapted based on your response object
String authenticationJson = objectMapper.writeValueAsString(authentication); // Authentication object to JSON
// Set the JSON strings in the entity
samlResponseLogEntity1.setRequest(requestJson);
samlResponseLogEntity1.setResponse(responseJson);
samlResponseLogEntity1.setAuthenticationObject(authenticationJson);
samlResponseLogRepository.save(samlResponseLogEntity1);
logger.info("SAML Request: " + requestJson);
logger.info("SAML Response: " + responseJson);
logger.info("Authentication Details: " + authenticationJson);
}catch(Exception e) {
logger.info("Exception object" + e);
}
// samlResponseLogRepository
logger.info("SAML login successful for user: " + authentication.getName());
response.sendRedirect("http://gepafin-staging-fe.s3-website.eu-central-1.amazonaws.com/");
}).failureHandler((request, response, exception) -> {
logger.error("SAML login failed: " + exception.getMessage());
SamlResponseLogEntity samlResponseLogEntity = new SamlResponseLogEntity();
samlResponseLogEntity.setRequest(request.toString());
samlResponseLogEntity.setResponse(response.toString());
samlResponseLogEntity.setExceptionObject(exception.toString());
samlResponseLogRepository.save(samlResponseLogEntity);
try {
ObjectMapper objectMapper = new ObjectMapper();
// Create a new SAML log entity
SamlResponseLogEntity samlResponseLogEntity1 = new SamlResponseLogEntity();
// Convert request, response, and authentication to JSON format
String requestJson = objectMapper.writeValueAsString(request.getParameterMap()); // Assuming request params to JSON
String responseJson = objectMapper.writeValueAsString(response); // This may need to be adapted based on your response object
String exceptionJson = objectMapper.writeValueAsString(exception); // Authentication object to JSON
// Set the JSON strings in the entity
samlResponseLogEntity1.setRequest(requestJson);
samlResponseLogEntity1.setResponse(responseJson);
samlResponseLogEntity1.setAuthenticationObject(exceptionJson);
samlResponseLogRepository.save(samlResponseLogEntity1);
logger.info("SAML Request: " + requestJson);
logger.info("SAML Response: " + responseJson);
logger.info("exception Details: " + exceptionJson);
}catch(Exception e) {
logger.info("Exception object" + e);
}
response.sendRedirect("http://gepafin-staging-fe.s3-website.eu-central-1.amazonaws.com/login");
}));
return http.build(); return http.build();
} }
@@ -116,4 +207,51 @@ public class SecurityConfig {
new SecurityScheme().type(SecurityScheme.Type.HTTP) new SecurityScheme().type(SecurityScheme.Type.HTTP)
.scheme("bearer").bearerFormat("JWT"))); .scheme("bearer").bearerFormat("JWT")));
} }
@Bean
public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {
String entityId = baseUrl + "/v1/saml/gw/metadata";
String acsUrl = baseUrl + "/login/saml2/sso/loginumbria";
RelyingPartyRegistration registration = RelyingPartyRegistration.withRegistrationId("loginumbria")
.entityId(entityId)
.signingX509Credentials(credentials -> {
try {
credentials.add(Saml2X509Credential.signing(readPrivateKey(), readCertificate()));
} catch (Exception e) {
e.printStackTrace();
}
})
.assertionConsumerServiceLocation(acsUrl)
.assertingPartyDetails(details -> details.entityId("https://federatest.umbriadigitale.it/gw/metadata")
.singleSignOnServiceLocation("https://federatest.umbriadigitale.it/gw/SSOProxy/SAML2")
.singleSignOnServiceBinding(Saml2MessageBinding.POST).wantAuthnRequestsSigned(false)
)
.build();
return new InMemoryRelyingPartyRegistrationRepository(registration);
}
public PrivateKey readPrivateKey() throws Exception {
// Path to your private key PEM file
File privateKeyFile = new File("src/main/resources/dev/saml/private-key.pem");
try (PemReader pemReader = new PemReader(new FileReader(privateKeyFile))) {
// Read the PEM content
byte[] pemContent = pemReader.readPemObject().getContent();
// Decode the PEM content
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pemContent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA"); // Use RSA algorithm
// Generate and return the PrivateKey
return keyFactory.generatePrivate(keySpec);
}
}
public X509Certificate readCertificate() throws Exception {
// Path to your certificate PEM fileFile
File certFile = new File("src/main/resources/dev/saml/public-cert.pem");
try (InputStream inStream = new FileInputStream(certFile)) {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
return (X509Certificate) certFactory.generateCertificate(inStream);
}
}
} }

View File

@@ -0,0 +1,26 @@
package net.gepafin.tendermanagement.entities;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.Data;
@Entity
@Table(name = "SAML_RESPONSE_LOG")
@Data
public class SamlResponseLogEntity extends BaseEntity{
@Column(name = "REQUEST")
private String request;
@Column(name = "RESPONSE")
private String response;
@Column(name = "AUTHENTICATION_OBJECT")
private String authenticationObject;
@Column(name = "EXCEPTION_OBJECT")
private String exceptionObject;
}

View File

@@ -0,0 +1,11 @@
package net.gepafin.tendermanagement.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import net.gepafin.tendermanagement.entities.SamlResponseLogEntity;
@Repository
public interface SamlResponseLogRepository extends JpaRepository<SamlResponseLogEntity, Long> {
}

View File

@@ -1,5 +1,7 @@
package net.gepafin.tendermanagement.web.rest.api.impl; package net.gepafin.tendermanagement.web.rest.api.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@@ -12,17 +14,21 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import net.gepafin.tendermanagement.config.SecurityConfig;
import net.gepafin.tendermanagement.web.rest.api.SamlApi; import net.gepafin.tendermanagement.web.rest.api.SamlApi;
@RestController @RestController
@RequestMapping("${openapi.gepafin.base-path:/v1/saml}") @RequestMapping("${openapi.gepafin.base-path:/v1/saml}")
public class SamlApiController implements SamlApi{ public class SamlApiController implements SamlApi{
private final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
@Autowired @Autowired
private RelyingPartyRegistrationRepository relyingPartyRegistrationRepository; private RelyingPartyRegistrationRepository relyingPartyRegistrationRepository;
@Override @Override
public ResponseEntity<String> getMetadata(HttpServletRequest request) { public ResponseEntity<String> getMetadata(HttpServletRequest request) {
logger.info("get SP metadata");
Saml2MetadataResolver metadataResolver = new OpenSamlMetadataResolver(); Saml2MetadataResolver metadataResolver = new OpenSamlMetadataResolver();
RelyingPartyRegistration registration = relyingPartyRegistrationRepository.findByRegistrationId("loginumbria"); RelyingPartyRegistration registration = relyingPartyRegistrationRepository.findByRegistrationId("loginumbria");
return ResponseEntity.status(HttpStatus.OK).header("Content-Type", MediaType.APPLICATION_XML_VALUE) return ResponseEntity.status(HttpStatus.OK).header("Content-Type", MediaType.APPLICATION_XML_VALUE)

View File

@@ -35,6 +35,7 @@ aws.s3.url.folder=gepafin
# Ensure these values match your expectations # Ensure these values match your expectations
security.authentication.jwt.secret=my-secret-token-to-change-in-prod-environment-your-super-secure-randomly-generated-key security.authentication.jwt.secret=my-secret-token-to-change-in-prod-environment-your-super-secure-randomly-generated-key
security.authentication.jwt.token-validity-in-seconds=86400 security.authentication.jwt.token-validity-in-seconds=86400
base-url=https://api-dev-gepafin.memento.credit
spring.main.allow-circular-references=true spring.main.allow-circular-references=true

View File

@@ -710,4 +710,30 @@
<sqlFile dbms="postgresql" <sqlFile dbms="postgresql"
path="classpath:db/dump/updated_form_field_data_16-09-2024.sql" /> path="classpath:db/dump/updated_form_field_data_16-09-2024.sql" />
</changeSet> </changeSet>
<changeSet id="23-09-2024_1" author="Rajesh Khore">
<createTable tableName="saml_response_log">
<column name="id" type="INTEGER" autoIncrement="true">
</column>
<column name="request" type="TEXT">
</column>
<column name="response" type="TEXT">
</column>
<column name="authentication_object" type="TEXT">
</column>
<column name="exception_object" type="TEXT">
</column>
<column name="created_date" type="TIMESTAMP WITHOUT TIME ZONE">
</column>
<column name="updated_date" type="TIMESTAMP WITHOUT TIME ZONE">
</column>
</createTable>
</changeSet>
</databaseChangeLog> </databaseChangeLog>