diff --git a/pom.xml b/pom.xml index 1ef93bc4032c17ea4823e838b6812f7cc91c7861..a4ec0570908f26891d75f31c2aea11918ef00c5d 100644 --- a/pom.xml +++ b/pom.xml @@ -102,11 +102,13 @@ **/smp-angular/node_modules/**, **/swagger*.js, **/web.xml, + **/smp-examples/**, </sonar.exclusions> <sonar.coverage.exclusions> **/*Entity.java, **/*RO.java, **/*Exception.java, + **/*Types.java, </sonar.coverage.exclusions> <!-- latest version compatible with SonarQube 5.6 is: 3.3.0.603--> <sonar.maven.plugin.version>3.5.0.1254</sonar.maven.plugin.version> diff --git a/smp-examples/smp-spi-example/pom.xml b/smp-examples/smp-spi-example/pom.xml index f7e28913acc45e83ee808fc4735e725fedc4ada2..91eea6e7d2c8755def075b1c0a5b6d2c5ea2d701 100644 --- a/smp-examples/smp-spi-example/pom.xml +++ b/smp-examples/smp-spi-example/pom.xml @@ -26,7 +26,6 @@ <artifactId>smp-spi-example</artifactId> <name>smp-spi-example</name> - <packaging>jar</packaging> <description>Example of SMP's SPI Payload validation.</description> <dependencies> <dependency> diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/logging/SMPMessageCode.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/logging/SMPMessageCode.java index 7ae1577baf6ec830cb9aa14dbf2e7200c705ffdc..284fe5132d6f94f8b8dd9bf845c652fb1a0282aa 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/logging/SMPMessageCode.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/logging/SMPMessageCode.java @@ -51,6 +51,7 @@ public enum SMPMessageCode implements MessageCode { SEC_USER_NOT_AUTHENTICATED("SEC-007", "User [{}]. Reason: [{}]."), SEC_USER_SUSPENDED("SEC-008", "User [{}] is temporarily suspended."), SEC_INVALID_TOKEN("SEC-009", "User [{}] has invalid token value for token id: [{}]."), + SEC_TRUSTSTORE_CERT_INVALID("SEC-010", "Truststore certificate with alias [{}] is invalid: [{}]."), ; diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreService.java index 7762b65c21074e2a6e7e8faff9364afa9f01a574..3f9c262a3579259b4bf99914b5dee108e7ed9e5a 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreService.java @@ -40,6 +40,7 @@ import java.util.*; import java.util.regex.Pattern; import java.util.stream.Collectors; +import static eu.europa.ec.edelivery.smp.logging.SMPMessageCode.SEC_TRUSTSTORE_CERT_INVALID; import static eu.europa.ec.edelivery.smp.logging.SMPMessageCode.SEC_USER_CERT_INVALID; import static java.util.Collections.list; import static java.util.Locale.US; @@ -146,13 +147,8 @@ public class UITruststoreService { DistinguishedNamesCodingUtil.getCommonAttributesDN()); tmpList.add(subject); hmCertificates.put(alias, x509Certificate); - try { - x509Certificate.checkValidity(); - } catch (CertificateExpiredException | CertificateNotYetValidException ex) { - LOG.warn("Certificate: [{}] from truststore is not valid anymore!", alias); - } + validateAndLogError(x509Certificate, alias); } - } } catch (Exception exception) { LOG.error("Could not load truststore certificates Error: " + ExceptionUtils.getRootCauseMessage(exception), exception); @@ -171,6 +167,14 @@ public class UITruststoreService { certificateROList.clear(); } + protected void validateAndLogError(X509Certificate x509Certificate, String alias){ + try { + x509Certificate.checkValidity(); + } catch (CertificateExpiredException | CertificateNotYetValidException ex) { + LOG.securityWarn(SEC_TRUSTSTORE_CERT_INVALID, alias, ExceptionUtils.getRootCauseMessage(ex)); + } + } + public CertificateRO getCertificateData(byte[] buff) throws CertificateException, IOException { return getCertificateData(buff, false); } diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreServiceTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreServiceTest.java index b3c72085fdf33202199c92cb5cac86aa306d3771..c032ccba16040df21a68f067b5fceee6f580b106 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreServiceTest.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreServiceTest.java @@ -1,5 +1,6 @@ package eu.europa.ec.edelivery.smp.services.ui; +import eu.europa.ec.edelivery.smp.data.model.DBUser; import eu.europa.ec.edelivery.smp.data.ui.CertificateRO; import eu.europa.ec.edelivery.smp.exceptions.CertificateNotTrustedException; import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; @@ -33,6 +34,8 @@ import java.security.cert.*; import java.util.*; import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; @RunWith(SpringJUnit4ClassRunner.class) @@ -449,4 +452,14 @@ public class UITruststoreServiceTest extends AbstractServiceIntegrationTest { MatcherAssert.assertThat(result.getMessage(), CoreMatchers.startsWith("Certificate policy verification failed.")); } + @Test + public void validateCertificateNotUsed() throws CertificateException { + String certId = "cn=test"+UUID.randomUUID().toString()+",o=test,c=eu:123456"; + CertificateRO certificateRO = new CertificateRO(); + certificateRO.setCertificateId(certId); + // when + testInstance.validateCertificateNotUsed(certificateRO); + //then no error is thrown + } + } \ No newline at end of file diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCas20ServiceTicketValidator.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCas20ServiceTicketValidator.java index 2aeb2ee1f93fd259bed39ae1b17b4cb9e939c92f..1a5d96924f14556a316a9d45f545488c86fc4a56 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCas20ServiceTicketValidator.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCas20ServiceTicketValidator.java @@ -25,6 +25,7 @@ public class SMPCas20ServiceTicketValidator extends Cas20ServiceTicketValidator this.urlSuffix = urlSuffix; } + @Override protected String getUrlSuffix() { if (StringUtils.isBlank(urlSuffix)){ LOG.warn("Cas20 ServiceTicketValidator url suffix is not configured. Use default value: [{}]", super.getUrlSuffix()); @@ -33,6 +34,7 @@ public class SMPCas20ServiceTicketValidator extends Cas20ServiceTicketValidator return urlSuffix; } + @Override protected void customParseResponse(final String response, final Assertion assertion) throws TicketValidationException { LOG.debug("Got CAS response: [{}] and test it with assertion [{}]",response,assertion ); diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCasConfigurer.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCasConfigurer.java index dd1275b4abc926a0455126fbd6700273bd42937e..f198922647130ca716a4365db642cd8ba9583c22 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCasConfigurer.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCasConfigurer.java @@ -68,15 +68,15 @@ public class SMPCasConfigurer { * @return */ @Bean - public CasAuthenticationEntryPoint casAuthenticationEntryPoint(@Nullable @Qualifier(SMP_CAS_PROPERTIES_BEAN) ServiceProperties serviceProperties, ConfigurationService configService) { + public CasAuthenticationEntryPoint casAuthenticationEntryPoint(@Nullable @Qualifier(SMP_CAS_PROPERTIES_BEAN) ServiceProperties serviceProperties) { - if (!configService.isSSOEnabledForUserAuthentication()) { - LOG.debug("Bean CasAuthenticationEntryPoint is not configured because SSO CAS authentication is not enabled!", SMP_CAS_PROPERTIES_BEAN); + if (!configurationService.isSSOEnabledForUserAuthentication()) { + LOG.debug("Bean [{}] is not configured because SSO CAS authentication is not enabled!", SMP_CAS_PROPERTIES_BEAN); return null; } - String casUrl = configService.getCasURL().toString(); - String casLoginPath = configService.getCasURLPathLogin(); + String casUrl = configurationService.getCasURL().toString(); + String casLoginPath = configurationService.getCasURLPathLogin(); String casUrlLogin = StringUtils.removeEnd(casUrl, "/") + StringUtils.prependIfMissing(casLoginPath, "/"); CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint(); @@ -87,21 +87,20 @@ public class SMPCasConfigurer { } @Bean - public SMPCas20ServiceTicketValidator ecasServiceTicketValidator(ConfigurationService configService) { - if (!configService.isSSOEnabledForUserAuthentication()) { - LOG.debug("Bean SMPCas20ServiceTicketValidator is not configured because SSO CAS authentication is not enabled!", SMP_CAS_PROPERTIES_BEAN); + public SMPCas20ServiceTicketValidator ecasServiceTicketValidator() { + if (!configurationService.isSSOEnabledForUserAuthentication()) { + LOG.debug("Bean [{}] is not configured because SSO CAS authentication is not enabled!", SMP_CAS_PROPERTIES_BEAN); return null; } - if (configService.getCasURL() == null) { - LOG.error("Bean SMPCas20ServiceTicketValidator is not created! Missing Service parameter [{}]!", SSO_CAS_URL.getProperty()); + if (configurationService.getCasURL() == null) { + LOG.error("Bean [{}] is not created! Missing Service parameter [{}]!", SMP_CAS_PROPERTIES_BEAN, SSO_CAS_URL.getProperty()); return null; } - String casUrl = configService.getCasURL().toString(); - String casTokenValidationSuffix = configService.getCasURLTokenValidation(); - LOG.debug("Create Bean SMPCas20ServiceTicketValidator with cas URL [{}] and token suffix [{}]!", casUrl,casTokenValidationSuffix ); + String casUrl = configurationService.getCasURL().toString(); + String casTokenValidationSuffix = configurationService.getCasURLTokenValidation(); SMPCas20ServiceTicketValidator validator = new SMPCas20ServiceTicketValidator(casUrl, casTokenValidationSuffix); - validator.setCustomParameters(getCustomParameters(configService)); + validator.setCustomParameters(getCustomParameters()); validator.setRenew(false); return validator; } @@ -109,15 +108,14 @@ public class SMPCasConfigurer { /** * Generate properties for SMPCas20ServiceTicketValidator * - * @param configService * @return CAS properties */ - public Map<String, String> getCustomParameters(ConfigurationService configService) { + public Map<String, String> getCustomParameters() { Map<String, String> map = new HashMap<>(); // always return details map.put("userDetails", "true"); - map.putAll(configService.getCasTokenValidationParams()); - List<String> groupList = configService.getCasURLTokenValidationGroups(); + map.putAll(configurationService.getCasTokenValidationParams()); + List<String> groupList = configurationService.getCasURLTokenValidationGroups(); if (!groupList.isEmpty()) { map.put("groups", String.join(",", groupList)); } @@ -133,10 +131,9 @@ public class SMPCasConfigurer { public CasAuthenticationProvider casAuthenticationProvider( @Nullable @Qualifier(SMP_CAS_PROPERTIES_BEAN) ServiceProperties serviceProperties, @Nullable SMPCas20ServiceTicketValidator serviceTicketValidator, - @Nullable SMPCasUserService smpCasUserService, - ConfigurationService configService) { + @Nullable SMPCasUserService smpCasUserService) { - if (!configService.isSSOEnabledForUserAuthentication()) { + if (!configurationService.isSSOEnabledForUserAuthentication()) { LOG.debug("Bean [CasAuthenticationProvider:{}] is not configured because SSO CAS authentication is not enabled!", SMP_CAS_PROPERTIES_BEAN); return null; } @@ -169,7 +166,7 @@ public class SMPCasConfigurer { //filter.setFilterProcessesUrl(SMP_SECURITY_PATH_CAS_AUTHENTICATE); filter.setServiceProperties(casServiceProperties); filter.setAuthenticationManager(authenticationManager); - LOG.info("Created CAS Filter: " + filter.getClass().getSimpleName() + "with the properties: " + casServiceProperties.getArtifactParameter()); + LOG.info("Created CAS Filter: [{}] with the properties: [{}]", filter.getClass().getSimpleName() , casServiceProperties.getArtifactParameter()); return filter; } } diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCas20ServiceTicketValidatorTest.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCas20ServiceTicketValidatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ab69b5bcb9434e6b3264e4eab5192f0a44e6cd5c --- /dev/null +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCas20ServiceTicketValidatorTest.java @@ -0,0 +1,28 @@ +package eu.europa.ec.edelivery.smp.auth.cas; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class SMPCas20ServiceTicketValidatorTest { + + @Test + public void testGetUrlSuffix() { + String casUrl = "https://cas-server.local/cas"; + String casSuffix = "urlSuffix"; + + SMPCas20ServiceTicketValidator testInstance = new SMPCas20ServiceTicketValidator(casUrl, casSuffix); + + assertEquals(casSuffix, testInstance.getUrlSuffix()); + } + + @Test + public void testGetUrlSuffixDefault() { + String casUrl = "https://cas-server.local/cas"; + String casSuffix = null; + + SMPCas20ServiceTicketValidator testInstance = new SMPCas20ServiceTicketValidator(casUrl, casSuffix); + + assertEquals("serviceValidate", testInstance.getUrlSuffix()); + } +} \ No newline at end of file diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCasConfigurerTest.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCasConfigurerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2e24400c71601aab8091e1d0afbac03a8fd1227e --- /dev/null +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCasConfigurerTest.java @@ -0,0 +1,103 @@ +package eu.europa.ec.edelivery.smp.auth.cas; + +import eu.europa.ec.edelivery.smp.controllers.SmpUrlBuilder; +import eu.europa.ec.edelivery.smp.services.ConfigurationService; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.security.cas.ServiceProperties; +import org.springframework.security.cas.authentication.CasAuthenticationProvider; +import org.springframework.security.cas.web.CasAuthenticationEntryPoint; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +public class SMPCasConfigurerTest { + ConfigurationService mockConfigService = Mockito.mock(ConfigurationService.class); + SmpUrlBuilder mockSmpUrlBuilder = Mockito.mock(SmpUrlBuilder.class); + + SMPCasConfigurer testInstance = new SMPCasConfigurer(mockSmpUrlBuilder, mockConfigService); + + @Test + public void serviceProperties() throws MalformedURLException { + String callbackString = "http://callback.local/smp"; + URL callBackURL = new URL(callbackString); + doReturn(callBackURL).when(mockConfigService).getCasCallbackUrl(); + ServiceProperties serviceProperties = testInstance.serviceProperties(); + + assertNotNull(serviceProperties); + assertEquals(callbackString, serviceProperties.getService()); + assertEquals(ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER, serviceProperties.getArtifactParameter()); + assertEquals(true, serviceProperties.isAuthenticateAllArtifacts()); + } + + @Test + public void casAuthenticationEntryPoint() throws MalformedURLException { + String casUrl = "http://cas-server.local/cas"; + String casLoginPath = "login"; + doReturn(true).when(mockConfigService).isSSOEnabledForUserAuthentication(); + doReturn(new URL(casUrl)).when(mockConfigService).getCasURL(); + doReturn(casLoginPath).when(mockConfigService).getCasURLPathLogin(); + ServiceProperties serviceProperties = testInstance.serviceProperties(); + + CasAuthenticationEntryPoint result = testInstance.casAuthenticationEntryPoint(serviceProperties); + assertNotNull(serviceProperties); + assertEquals(casUrl+"/"+casLoginPath,result.getLoginUrl() ); + assertEquals(serviceProperties,result.getServiceProperties() ); + } + + @Test + public void ecasServiceTicketValidator() throws MalformedURLException { + String casUrl = "http://cas-server.local/cas"; + String tokenValidator = "laxValidate"; + + doReturn(true).when(mockConfigService).isSSOEnabledForUserAuthentication(); + doReturn(new URL(casUrl)).when(mockConfigService).getCasURL(); + doReturn(tokenValidator).when(mockConfigService).getCasURLTokenValidation(); + + SMPCas20ServiceTicketValidator result = testInstance.ecasServiceTicketValidator(); + assertNotNull(result); + assertEquals(tokenValidator, result.getUrlSuffix()); + } + + @Test + public void getCustomParameters() { + Map<String, String> testMap = new HashMap<>(); + testMap.put("key1","val1"); + testMap.put("key2","val2"); + List<String> groups = Arrays.asList("list1","list2"); + doReturn(testMap).when(mockConfigService).getCasTokenValidationParams(); + doReturn(groups).when(mockConfigService).getCasURLTokenValidationGroups(); + + Map<String, String> result = testInstance.getCustomParameters(); + + assertEquals(4, result.size()); + assertEquals("true", result.get("userDetails")); + assertEquals(String.join(",", groups), result.get("groups")); + assertEquals("val1", result.get("key1")); + assertEquals("val2", result.get("key2")); + } + + @Test + public void casAuthenticationProvider() { + ServiceProperties serviceProperties = mock(ServiceProperties.class); + SMPCas20ServiceTicketValidator smpCas20ServiceTicketValidator = mock(SMPCas20ServiceTicketValidator.class); + SMPCasUserService smpCasUserService = mock(SMPCasUserService.class); + + doReturn(true).when(mockConfigService).isSSOEnabledForUserAuthentication(); + + + CasAuthenticationProvider provider = testInstance.casAuthenticationProvider(serviceProperties, smpCas20ServiceTicketValidator, smpCasUserService); + + assertNotNull(provider); + + } + +} \ No newline at end of file