From cced86e2524c0cba0c54d220446f120c732b4eee Mon Sep 17 00:00:00 2001 From: Joze RIHTARSIC <joze.RIHTARSIC@ext.ec.europa.eu> Date: Mon, 9 May 2022 06:58:18 +0200 Subject: [PATCH] Fix regression issues for CAS --- .../tomcat-mysql-smp-sml/docker-compose.yml | 1 + .../images/tomcat-mysql-smp-sml/Dockerfile | 8 +- .../images/tomcat-mysql-smp-sml/entrypoint.sh | 10 +- smp-server-library/pom.xml | 5 +- .../smp/auth/SMPAuthenticationToken.java | 45 +++-- .../ec/edelivery/smp/auth/SMPUserDetails.java | 84 +++++++++ .../smp/auth/UILoginAuthenticationToken.java | 18 ++ .../smp/config/PropertyInitialization.java | 13 +- ...09CertificateToCertificateROConverter.java | 5 +- .../ec/edelivery/smp/data/ui/UserRO.java | 29 +-- .../smp/data/ui/auth/SMPAuthority.java | 39 +++- .../ui/databind/SMPAuthorityDeserializer.java | 2 +- .../smp/data/ui/enums/SMPPropertyEnum.java | 7 +- .../smp/services/CRLVerifierService.java | 6 +- .../smp/services/ConfigurationService.java | 12 +- .../smp/services/ui/UIKeystoreService.java | 18 +- .../smp/services/ui/UITruststoreService.java | 23 ++- .../smp/services/ui/UIUserService.java | 9 +- .../smp/utils/SessionSecurityUtils.java | 77 +++++++- .../smp/utils/X509CertificateUtils.java | 175 ------------------ .../smp/utils/SessionSecurityUtilsTest.java | 124 +++++++++++++ .../smp/utils/X509CertificateUtilsTest.java | 7 +- smp-webapp/pom.xml | 4 - .../smp/auth/SMPAuthenticationProvider.java | 56 ++++-- .../auth/SMPAuthenticationProviderForUI.java | 48 ++++- .../smp/auth/SMPAuthenticationService.java | 23 ++- .../smp/auth/SMPAuthorizationService.java | 113 +++++++---- .../smp/auth/cas/SMPCasUserService.java | 22 ++- .../smp/ui/AuthenticationResource.java | 59 ++---- .../smp/ui/external/ServiceGroupResource.java | 39 ++-- .../smp/ui/external/TruststoreResource.java | 13 -- .../ui/internal/TruststoreAdminResource.java | 4 +- .../smp/ui/internal/UserAdminResource.java | 11 +- .../auth/SMPAuthenticationProviderTest.java | 13 +- .../smp/auth/SMPAuthorizationServiceTest.java | 92 ++++++++- .../smp/ui/AuthenticationResourceTest.java | 107 +++++------ .../external/UserResourceIntegrationTest.java | 1 - ...ruststoreAdminResourceIntegrationTest.java | 6 +- 38 files changed, 817 insertions(+), 511 deletions(-) create mode 100644 smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPUserDetails.java create mode 100644 smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/auth/UILoginAuthenticationToken.java delete mode 100644 smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/X509CertificateUtils.java create mode 100644 smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/utils/SessionSecurityUtilsTest.java diff --git a/smp-docker/compose/tomcat-mysql-smp-sml/docker-compose.yml b/smp-docker/compose/tomcat-mysql-smp-sml/docker-compose.yml index 63bc8ddd7..29724dc55 100644 --- a/smp-docker/compose/tomcat-mysql-smp-sml/docker-compose.yml +++ b/smp-docker/compose/tomcat-mysql-smp-sml/docker-compose.yml @@ -21,6 +21,7 @@ services: - "8982:8080" - "6902:6901" - "8953:53" + - "5005:5005" eulogin-mock-server: image: edelivery-docker.devops.tech.ec.europa.eu/eulogin/mockserver:6.2.7 diff --git a/smp-docker/images/tomcat-mysql-smp-sml/Dockerfile b/smp-docker/images/tomcat-mysql-smp-sml/Dockerfile index 76e0e3bbe..133ff6156 100755 --- a/smp-docker/images/tomcat-mysql-smp-sml/Dockerfile +++ b/smp-docker/images/tomcat-mysql-smp-sml/Dockerfile @@ -31,7 +31,10 @@ ENV SMP_HOME=/opt/smp \ # misc variables JACOCO_VERSION=0.8.4 \ LANG=en_US.utf8 \ - LD_LIBRARY_PATH=/usr/local/apr/lib + LD_LIBRARY_PATH=/usr/local/apr/lib \ + # set debug + JPDA_ADDRESS="5005" \ + JPDA_TRANSPORT="dt_socket" # Exposing ports used in entrypoint.sh .. @@ -39,7 +42,8 @@ ENV SMP_HOME=/opt/smp \ # - 6901 JaCoCo port # - 8080 Tomcat port # - 53 dns port -EXPOSE 3306 8080 6901 53 +# - JDPA debug port +EXPOSE 3306 8080 6901 53 5005 diff --git a/smp-docker/images/tomcat-mysql-smp-sml/entrypoint.sh b/smp-docker/images/tomcat-mysql-smp-sml/entrypoint.sh index e5067b1d8..7a4a0f4de 100755 --- a/smp-docker/images/tomcat-mysql-smp-sml/entrypoint.sh +++ b/smp-docker/images/tomcat-mysql-smp-sml/entrypoint.sh @@ -24,10 +24,14 @@ if [ ! -d ${DATA_DIR} ]; then fi init_tomcat() { - # add java code coverage angent to image + # add java code coverage agent to image if [ -e /opt/jacoco/jacoco-agent.jar ]; then JAVA_OPTS="-javaagent:/opt/jacoco/jacoco-agent.jar=output=tcpserver,address=*,port=6901,includes=eu.europa.ec.edelivery.smp.* $JAVA_OPTS" fi + + # for debugging + JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=localhost" + JAVA_OPTS="$JAVA_OPTS -Xms512m -Xmx512m -server " # add allow encoded slashes and disable scheme for proxy JAVA_OPTS="$JAVA_OPTS -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true -Djdk.http.auth.tunneling.disabledSchemes=" # add truststore for eulogin @@ -36,8 +40,10 @@ init_tomcat() { JAVA_OPTS="$JAVA_OPTS -Djavax.net.ssl.trustStore=/tmp/keystores/smp-eulogin-mock.p12 -Djavax.net.ssl.trustStoreType=PKCS12 -Djavax.net.ssl.trustStorePassword=test123" fi + echo "[INFO] init tomcat JAVA_OPTS: $JAVA_OPTS" export JAVA_OPTS + echo "[INFO] init tomcat folders: $tfile" if [ ! -d ${TOMCAT_DIR} ]; then mkdir -p ${TOMCAT_DIR} @@ -248,7 +254,7 @@ echo '[INFO] start running SMP' chmod u+x $SMP_HOME/apache-tomcat-$TOMCAT_VERSION/bin/*.sh cd $SMP_HOME/apache-tomcat-$TOMCAT_VERSION/ # run from this folder in order to be smp log in logs folder -exec ./bin/catalina.sh run +exec ./bin/catalina.sh jpda run diff --git a/smp-server-library/pom.xml b/smp-server-library/pom.xml index 0bffa3e2a..0006fd33f 100644 --- a/smp-server-library/pom.xml +++ b/smp-server-library/pom.xml @@ -69,10 +69,13 @@ <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-cas</artifactId> + </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> - <version>${spring.version}</version> </dependency> <dependency> <groupId>org.freemarker</groupId> diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationToken.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationToken.java index adf1c0621..b15b775cc 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationToken.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationToken.java @@ -1,43 +1,40 @@ package eu.europa.ec.edelivery.smp.auth; -import eu.europa.ec.edelivery.smp.data.model.DBUser; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.edelivery.smp.utils.SecurityUtils; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; -import java.util.Collection; import java.util.Objects; +/** + * UI and web service authentication token. The authentication is created by the authentication provider + * + * @author Joze Rihtarsic + * @since 4.1 + */ public class SMPAuthenticationToken extends UsernamePasswordAuthenticationToken { private static final SMPLogger LOG = SMPLoggerFactory.getLogger(SMPAuthenticationToken.class); - private final DBUser user; - // session encryption key to encrypt sensitive data - // at the moment used for UI sessions - private SecurityUtils.Secret secret = null; + SMPUserDetails userDetails; - public SMPAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) { - this(principal, credentials, authorities, null); + public SMPAuthenticationToken(Object principal, Object credentials, SMPUserDetails userDetails) { + super(principal, credentials, userDetails.getAuthorities()); + setDetails(userDetails); + this.userDetails = userDetails; } - public SMPAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities, DBUser user) { - super(principal, credentials, authorities); - this.user = user; - } + public SecurityUtils.Secret getSecret() { - public DBUser getUser() { - return user; + if (userDetails == null) { + LOG.warn("Can not retrieve security token for session. User details is null!"); + return null; + } + return userDetails.getSessionSecret(); } - public SecurityUtils.Secret getSecret() { - if (secret == null) { - LOG.debug("Secret does not yet exist. Create user session secret!"); - secret = SecurityUtils.generatePrivateSymmetricKey(); - LOG.debug("User session secret created!"); - } - return secret; + public SMPUserDetails getUserDetails() { + return userDetails; } @Override @@ -47,11 +44,11 @@ public class SMPAuthenticationToken extends UsernamePasswordAuthenticationToken if (!super.equals(o)) return false; SMPAuthenticationToken that = (SMPAuthenticationToken) o; // also check super equals (roles..) which is implemented in AbstractAuthenticationToken - return Objects.equals(user, that.user) && super.equals(that); + return Objects.equals(getDetails(), that.getDetails()) && super.equals(that); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), user); + return Objects.hash(super.hashCode(), getDetails()); } } \ No newline at end of file diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPUserDetails.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPUserDetails.java new file mode 100644 index 000000000..6c3629df7 --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPUserDetails.java @@ -0,0 +1,84 @@ +package eu.europa.ec.edelivery.smp.auth; + +import eu.europa.ec.edelivery.smp.data.model.DBUser; +import eu.europa.ec.edelivery.smp.data.ui.auth.SMPAuthority; +import eu.europa.ec.edelivery.smp.utils.SecurityUtils; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Object contains Session details for logged user. For the UI it also generated the session secret for encrypting the + * session sensitive data. + * + * @author Joze Rihtarsic + * @since 4.2 + */ +public class SMPUserDetails implements UserDetails { + final DBUser user; + final SecurityUtils.Secret sessionSecret; + boolean casAuthenticated = false; + List<SMPAuthority> smpAuthorities = new ArrayList<>(); + + public SMPUserDetails(DBUser user, SecurityUtils.Secret sessionSecret, List<SMPAuthority> smpAuthorities) { + this.user = user; + if (smpAuthorities != null) { + this.smpAuthorities.addAll(smpAuthorities); + } + this.sessionSecret = sessionSecret; + } + + public DBUser getUser() { + return user; + } + + public SecurityUtils.Secret getSessionSecret() { + return sessionSecret; + } + + @Override + public Collection<? extends GrantedAuthority> getAuthorities() { + return smpAuthorities; + } + + public boolean isCasAuthenticated() { + return casAuthenticated; + } + + public void setCasAuthenticated(boolean casAuthenticated) { + this.casAuthenticated = casAuthenticated; + } + + @Override + public String getPassword() { + return null; + } + + @Override + public String getUsername() { + return this.user.getUsername(); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return this.user.isActive(); + } +} diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/auth/UILoginAuthenticationToken.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/auth/UILoginAuthenticationToken.java new file mode 100644 index 000000000..c724dd9b5 --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/auth/UILoginAuthenticationToken.java @@ -0,0 +1,18 @@ +package eu.europa.ec.edelivery.smp.auth; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; + +/** + * UI login authentication token. The token is generated by SMPAuthenticationService and is supported by the SMPAuthenticationProviderForUI. + * It is "distinguished" from UsernamePasswordAuthenticationToken, generated by basic authentication, + * which is used for stateless web services invocation using the Personal Access Token credentials. + * + * @author Joze Rihtarsic + * @since 4.2 + */ +public class UILoginAuthenticationToken extends UsernamePasswordAuthenticationToken { + + public UILoginAuthenticationToken(Object principal, Object credentials) { + super(principal, credentials); + } +} \ No newline at end of file diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/config/PropertyInitialization.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/config/PropertyInitialization.java index fa7dd2ce8..0d00d807f 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/config/PropertyInitialization.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/config/PropertyInitialization.java @@ -13,13 +13,13 @@ package eu.europa.ec.edelivery.smp.config; +import eu.europa.ec.edelivery.security.utils.X509CertificateUtils; import eu.europa.ec.edelivery.smp.data.model.DBConfiguration; import eu.europa.ec.edelivery.smp.data.ui.enums.SMPPropertyEnum; import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.edelivery.smp.utils.SecurityUtils; -import eu.europa.ec.edelivery.smp.utils.X509CertificateUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.jndi.JndiObjectFactoryBean; @@ -33,7 +33,6 @@ import javax.sql.DataSource; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.nio.file.Files; import java.security.KeyStore; import java.security.KeyStoreException; @@ -54,6 +53,11 @@ import static eu.europa.ec.edelivery.smp.exceptions.ErrorCode.INTERNAL_ERROR; public class PropertyInitialization { SMPLogger LOG = SMPLoggerFactory.getLogger(PropertyInitialization.class); + // if SMP is initialized without keystore - a demo keystore with test certificate is created + private static final String TEST_CERT_ISSUER_DN = "CN=rootCNTest,OU=B4,O=DIGIT,L=Brussels,ST=BE,C=BE"; + private static final String TEST_CERT_SUBJECT_DN = "CN=SMP_TEST-PRE-SET-EXAMPLE, OU=eDelivery, O=DIGITAL, C=BE"; + private static final String TEST_CERT_ISSUER_ALIAS = "issuer"; + private static final String TEST_CERT_CERT_ALIAS = "sample_key"; protected Properties getDatabaseProperties(Properties fileProperties) { String dialect = fileProperties.getProperty(FileProperty.PROPERTY_DB_DIALECT); @@ -211,7 +215,10 @@ public class PropertyInitialization { newKeystore.load(null, newKeyPassword.toCharArray()); // check if keystore is empty then generate cert for user if (newKeystore.size() == 0) { - X509CertificateUtils.createAndAddTextCertificate("CN=SMP_TEST-PRE-SET-EXAMPLE, OU=eDelivery, O=DIGITAL, C=BE", newKeystore, newKeyPassword); + X509CertificateUtils.createAndStoreCertificateWithChain( + new String []{TEST_CERT_ISSUER_DN, TEST_CERT_SUBJECT_DN}, + new String []{TEST_CERT_ISSUER_ALIAS, TEST_CERT_CERT_ALIAS}, + newKeystore, newKeyPassword); } newKeystore.store(out, newKeyPassword.toCharArray()); } catch (IOException e) { diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/X509CertificateToCertificateROConverter.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/X509CertificateToCertificateROConverter.java index 42173bba2..aa58f5329 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/X509CertificateToCertificateROConverter.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/X509CertificateToCertificateROConverter.java @@ -1,12 +1,12 @@ package eu.europa.ec.edelivery.smp.conversion; import eu.europa.ec.edelivery.security.PreAuthenticatedCertificatePrincipal; +import eu.europa.ec.edelivery.security.utils.X509CertificateUtils; import eu.europa.ec.edelivery.smp.data.ui.CertificateRO; import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; -import eu.europa.ec.edelivery.smp.utils.X509CertificateUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.core.convert.converter.Converter; import org.springframework.stereotype.Component; @@ -23,6 +23,7 @@ import java.util.Base64; /** * @author Joze Rihtarsic + * @since 4.1 */ @Component public class X509CertificateToCertificateROConverter implements Converter<X509Certificate, CertificateRO> { @@ -54,7 +55,7 @@ public class X509CertificateToCertificateROConverter implements Converter<X509Ce cro.setEncodedValue(Base64.getMimeEncoder().encodeToString(cert.getEncoded())); } catch (CertificateEncodingException cex) { throw new SMPRuntimeException(ErrorCode.CERTIFICATE_ERROR, cex, - "Error occured while decoding certificate " + subject, cex.getMessage(), cex); + "Error occurred while decoding certificate " + subject, cex.getMessage(), cex); } // generate clientCertHeader header diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/UserRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/UserRO.java index 47740d866..f5be11549 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/UserRO.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/UserRO.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import eu.europa.ec.edelivery.smp.data.ui.auth.SMPAuthority; import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus; import eu.europa.ec.edelivery.smp.utils.SMPConstants; -import org.springframework.security.core.userdetails.UserDetails; import java.time.OffsetDateTime; import java.util.Collection; @@ -15,12 +14,11 @@ import java.util.Collection; * @author Joze Rihtarsic * @since 4.1 */ -public class UserRO extends BaseRO implements UserDetails { +public class UserRO extends BaseRO { static final long serialVersionUID = 2821447495333163882L; String username; - String password; @JsonFormat(pattern = SMPConstants.JSON_DATETIME_ISO) OffsetDateTime passwordExpireOn; @@ -133,7 +131,6 @@ public class UserRO extends BaseRO implements UserDetails { this.certificate = certificate; } - @Override public Collection<SMPAuthority> getAuthorities() { return authorities; } @@ -173,28 +170,4 @@ public class UserRO extends BaseRO implements UserDetails { public void setCasAuthenticated(boolean casAuthenticated) { this.casAuthenticated = casAuthenticated; } - - @Override - @JsonIgnore - public boolean isAccountNonExpired() { - return true; - } - - @Override - @JsonIgnore - public boolean isAccountNonLocked() { - return true; - } - - @Override - @JsonIgnore - public boolean isCredentialsNonExpired() { - return true; - } - - @Override - @JsonIgnore - public boolean isEnabled() { - return active; - } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/auth/SMPAuthority.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/auth/SMPAuthority.java index 962621e9d..f74ea7de6 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/auth/SMPAuthority.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/auth/SMPAuthority.java @@ -3,9 +3,14 @@ package eu.europa.ec.edelivery.smp.data.ui.auth; import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import eu.europa.ec.edelivery.smp.data.ui.databind.SMPAuthorityDeserializer; +import org.apache.commons.lang3.StringUtils; import org.springframework.security.core.GrantedAuthority; +/** + * @author Joze Rihtarsic + * @since 4.1 + */ @JsonDeserialize(using = SMPAuthorityDeserializer.class) public class SMPAuthority implements GrantedAuthority { @@ -16,7 +21,6 @@ public class SMPAuthority implements GrantedAuthority { public static final String S_AUTHORITY_TOKEN_SYSTEM_ADMIN = "ROLE_SYSTEM_ADMIN"; public static final String S_AUTHORITY_TOKEN_SMP_ADMIN = "ROLE_SMP_ADMIN"; public static final String S_AUTHORITY_TOKEN_SERVICE_GROUP_ADMIN = "ROLE_SERVICE_GROUP_ADMIN"; - public static final String S_AUTHORITY_TOKEN_ROLE_ANONYMOUS = "ROLE_ANONYMOUS"; // static constants for verification... public static final SMPAuthority S_AUTHORITY_SYSTEM_ADMIN = new SMPAuthority(SMPRole.SYSTEM_ADMIN.getCode()); @@ -30,7 +34,7 @@ public class SMPAuthority implements GrantedAuthority { String role; - public SMPAuthority(String role) { + private SMPAuthority(String role) { this.role = role; } @@ -39,4 +43,35 @@ public class SMPAuthority implements GrantedAuthority { public String getAuthority() { return "ROLE_" + role; } + + public String getRole() { + return role; + } + + public static SMPAuthority getAuthorityByRoleName(String name) { + if (StringUtils.isBlank(name)) { + return S_AUTHORITY_ANONYMOUS; + } + SMPRole role = SMPRole.valueOf(name); + return getAuthorityByRole(role); + } + + public static SMPAuthority getAuthorityByRole(SMPRole role) { + switch (role) { + case SMP_ADMIN: + return S_AUTHORITY_SMP_ADMIN; + case SYSTEM_ADMIN: + return S_AUTHORITY_SYSTEM_ADMIN; + case SERVICE_GROUP_ADMIN: + return S_AUTHORITY_SERVICE_GROUP; + case WS_SMP_ADMIN: + return S_AUTHORITY_WS_SMP_ADMIN; + case WS_SERVICE_GROUP_ADMIN: + return S_AUTHORITY_WS_SERVICE_GROUP; + case WS_SYSTEM_ADMIN: + return S_AUTHORITY_WS_SYSTEM_ADMIN; + default: + return S_AUTHORITY_ANONYMOUS; + } + } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/databind/SMPAuthorityDeserializer.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/databind/SMPAuthorityDeserializer.java index 9b38e55eb..aa45aa037 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/databind/SMPAuthorityDeserializer.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/databind/SMPAuthorityDeserializer.java @@ -24,6 +24,6 @@ public class SMPAuthorityDeserializer extends StdDeserializer<SMPAuthority> { public SMPAuthority deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); String text = node.asText(); - return new SMPAuthority(text.substring("ROLE_".length())); + return SMPAuthority.getAuthorityByRoleName(text.substring("ROLE_".length())); } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/enums/SMPPropertyEnum.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/enums/SMPPropertyEnum.java index 8ae09d7b4..4176a2b64 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/enums/SMPPropertyEnum.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/enums/SMPPropertyEnum.java @@ -72,12 +72,15 @@ public enum SMPPropertyEnum { "The error message shown to the user in case the password does not follow the regex put in the domibus.passwordPolicy.pattern property", false, false,false, STRING), PASSWORD_POLICY_VALID_DAYS("smp.passwordPolicy.validDays","90", "Number of days password is valid", false, false,false, INTEGER), - PASSWORD_POLICY_UIWARNING_DAYS_BEFORE_EXPIRE("smp.passwordPolicy.warning.beforeExpiration","15", + PASSWORD_POLICY_WARNING_DAYS_BEFORE_EXPIRE("smp.passwordPolicy.warning.beforeExpiration","15", "How many days before expiration should the UI warn users at login", false, false,false, INTEGER), PASSWORD_POLICY_FORCE_CHANGE_EXPIRED("smp.passwordPolicy.expired.forceChange","true", "Force change password at UI login if expired", false, false,false, BOOLEAN), + USER_LOGIN_FAIL_DELAY("smp.user.login.fail.delay","1000", + "Delay in ms on invalid username or password", false, false,false, INTEGER), + USER_MAX_FAILED_ATTEMPTS("smp.user.login.maximum.attempt","5", "Number of console login attempt before the user is deactivated", false, false,false, INTEGER), USER_SUSPENSION_TIME("smp.user.login.suspension.time","3600", @@ -89,6 +92,8 @@ public enum SMPPropertyEnum { "Number of accessToken login attempt before the accessToken is deactivated", false, false,false, INTEGER), ACCESS_TOKEN_SUSPENSION_TIME("smp.accessToken.login.suspension.time","3600", "Time in seconds for a suspended accessToken to be reactivated. (if 0 the user will not be reactivated)", false, false,false, INTEGER), + ACCESS_TOKEN_FAIL_DELAY("smp.accessToken.login.fail.delay","1000", + "Delay in ms on invalid token id or token", false, false,false, INTEGER), // authentication UI_AUTHENTICATION_TYPES("smp.ui.authentication.types", "PASSWORD", "Set list of '|' separated authentication types: PASSWORD|SSO.", false, false, false, LIST_STRING), diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CRLVerifierService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CRLVerifierService.java index 65d8997e7..56ee3693a 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CRLVerifierService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CRLVerifierService.java @@ -20,12 +20,12 @@ package eu.europa.ec.edelivery.smp.services; */ +import eu.europa.ec.edelivery.security.utils.X509CertificateUtils; import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.edelivery.smp.utils.HttpUtils; -import eu.europa.ec.edelivery.smp.utils.X509CertificateUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.cxf.helpers.IOUtils; @@ -70,7 +70,7 @@ public class CRLVerifierService { ConfigurationService configurationService; - public void verifyCertificateCRLs(X509Certificate cert) throws CertificateRevokedException { + public void verifyCertificateCRLs(X509Certificate cert) throws CertificateRevokedException, CertificateParsingException { List<String> crlDistPoints = X509CertificateUtils.getCrlDistributionPoints(cert); if (crlDistPoints.isEmpty()) { @@ -269,8 +269,6 @@ public class CRLVerifierService { throw new CertificateRevokedException(entry.getRevocationDate(), entry.getRevocationReason() == null ? NULL_CRL_REASON : entry.getRevocationReason(), entry.getCertificateIssuer() == null ? NULL_ISSUER : entry.getCertificateIssuer(), map); - } } - } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ConfigurationService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ConfigurationService.java index 11619ba97..3d26225b4 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ConfigurationService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ConfigurationService.java @@ -65,7 +65,7 @@ public class ConfigurationService { } public Integer getPasswordPolicyUIWarningDaysBeforeExpire() { - return (Integer) configurationDAO.getCachedPropertyValue(PASSWORD_POLICY_UIWARNING_DAYS_BEFORE_EXPIRE); + return (Integer) configurationDAO.getCachedPropertyValue(PASSWORD_POLICY_WARNING_DAYS_BEFORE_EXPIRE); } public Boolean getPasswordPolicyForceChangeIfExpired() { @@ -84,6 +84,11 @@ public class ConfigurationService { return (Integer) configurationDAO.getCachedPropertyValue(USER_SUSPENSION_TIME); } + public Integer getLoginFailDelayInMilliSeconds() { + Integer delay =(Integer) configurationDAO.getCachedPropertyValue(USER_LOGIN_FAIL_DELAY); + return delay==null? 1000:delay; + } + public Integer getAccessTokenLoginMaxAttempts() { return (Integer) configurationDAO.getCachedPropertyValue(ACCESS_TOKEN_MAX_FAILED_ATTEMPTS); } @@ -92,6 +97,11 @@ public class ConfigurationService { return (Integer) configurationDAO.getCachedPropertyValue(ACCESS_TOKEN_SUSPENSION_TIME); } + public Integer getAccessTokenLoginFailDelayInMilliSeconds() { + Integer delay =(Integer) configurationDAO.getCachedPropertyValue(ACCESS_TOKEN_FAIL_DELAY); + return delay==null? 1000:delay; + } + public Integer getHttpHeaderHstsMaxAge() { return (Integer) configurationDAO.getCachedPropertyValue(HTTP_HSTS_MAX_AGE); } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIKeystoreService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIKeystoreService.java index c7213acbc..74d16470f 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIKeystoreService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIKeystoreService.java @@ -7,9 +7,7 @@ import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.edelivery.smp.services.ConfigurationService; import eu.europa.ec.edelivery.smp.utils.SecurityUtils; -import eu.europa.ec.edelivery.smp.utils.X509CertificateUtils; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.ConversionService; import org.springframework.stereotype.Service; @@ -17,7 +15,6 @@ import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.TrustManagerFactory; import java.io.*; import java.security.*; import java.security.cert.Certificate; @@ -28,6 +25,10 @@ import java.util.*; import static java.util.Collections.list; import static org.apache.commons.lang3.StringUtils.isBlank; +/** + * @author Joze Rihtarsic + * @since 4.1 + */ @Service public class UIKeystoreService { @@ -53,7 +54,6 @@ public class UIKeystoreService { public void init() { keystoreKeys = new HashMap(); keystoreCertificates = new HashMap(); - X509CertificateUtils.setupJCEProvider(); refreshData(); } @@ -66,7 +66,7 @@ public class UIKeystoreService { String keystoreSecToken = configurationService.getKeystoreCredentialToken(); // load keystore - File keystoreFile = configurationService.getKeystoreFile(); + File keystoreFile = configurationService.getKeystoreFile(); if (keystoreFile == null) { LOG.error("KeystoreFile: is null! Check the keystore and the configuration!"); return; @@ -101,7 +101,7 @@ public class UIKeystoreService { return; } LOG.debug("Set keystore certificates:"); - hmCertificates.forEach((alias, cert)-> LOG.debug(" - {}, {}", alias, cert.getSubjectDN().toString() )); + hmCertificates.forEach((alias, cert) -> LOG.debug(" - {}, {}", alias, cert.getSubjectDN().toString())); // if got all data from keystore - update data keyManagers = keyManagersTemp; @@ -120,7 +120,7 @@ public class UIKeystoreService { boolean isKeyStoreChanged() { File file = configurationService.getKeystoreFile(); - return file!=null && (!Objects.equals(lastUpdateKeystoreFile, file) || file.lastModified() != lastUpdateKeystoreFileTime); + return file != null && (!Objects.equals(lastUpdateKeystoreFile, file) || file.lastModified() != lastUpdateKeystoreFileTime); } @@ -134,7 +134,7 @@ public class UIKeystoreService { private KeyStore loadKeystore(File keyStoreFile, String keystoreSecToken) { // Load the KeyStore. - if (keyStoreFile!=null && !keyStoreFile.exists()) { + if (keyStoreFile != null && !keyStoreFile.exists()) { LOG.error("Keystore file '{}' does not exists!", keyStoreFile.getAbsolutePath()); return null; } @@ -193,7 +193,7 @@ public class UIKeystoreService { } if (keystoreKeys.isEmpty()) { - throw new SMPRuntimeException(ErrorCode.CONFIGURATION_ERROR, "Could not retrieve key: " + keyAlias +" from empty keystore!" + configurationService.getKeystoreFile() ); + throw new SMPRuntimeException(ErrorCode.CONFIGURATION_ERROR, "Could not retrieve key: " + keyAlias + " from empty keystore!" + configurationService.getKeystoreFile()); } 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 14b50758c..875fa4db9 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 @@ -1,5 +1,6 @@ package eu.europa.ec.edelivery.smp.services.ui; +import eu.europa.ec.edelivery.security.utils.X509CertificateUtils; import eu.europa.ec.edelivery.smp.data.ui.CertificateRO; import eu.europa.ec.edelivery.smp.exceptions.CertificateNotTrustedException; import eu.europa.ec.edelivery.smp.logging.SMPLogger; @@ -7,7 +8,6 @@ import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.edelivery.smp.logging.SMPMessageCode; import eu.europa.ec.edelivery.smp.services.CRLVerifierService; import eu.europa.ec.edelivery.smp.services.ConfigurationService; -import eu.europa.ec.edelivery.smp.utils.X509CertificateUtils; import eu.europa.ec.edelivery.text.DistinguishedNamesCodingUtil; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -41,6 +41,10 @@ import java.util.stream.Collectors; import static java.util.Collections.list; import static java.util.Locale.US; +/** + * @author Joze Rihtarsic + * @since 4.1 + */ @Service public class UITruststoreService { @@ -168,15 +172,26 @@ public class UITruststoreService { /** * Validate certificate! * - * @param buff - bytearray of the certificate (pem of or der) + * @param buff - bytearray of the certificate (pem of or der) * @param validate * @return * @throws CertificateException * @throws IOException */ public CertificateRO getCertificateData(byte[] buff, boolean validate) { - X509Certificate cert = X509CertificateUtils.getX509Certificate(buff); - CertificateRO cro = convertToRo(cert); + X509Certificate cert = null; + CertificateRO cro = null; + try { + cert = X509CertificateUtils.getX509Certificate(buff); + } catch (CertificateException e) { + LOG.warn("Can not parse the certificate with error:[{}]!", ExceptionUtils.getRootCauseMessage(e)); + cro = new CertificateRO(); + cro.setInvalid(true); + cro.setInvalidReason("Can not read the certificate!"); + return cro; + } + + cro = convertToRo(cert); if (validate) { // first expect the worst cro.setInvalid(true); diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java index a59b6a271..88e757865 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java @@ -1,5 +1,6 @@ package eu.europa.ec.edelivery.smp.services.ui; +import eu.europa.ec.edelivery.security.utils.X509CertificateUtils; import eu.europa.ec.edelivery.smp.data.dao.BaseDao; import eu.europa.ec.edelivery.smp.data.dao.UserDao; import eu.europa.ec.edelivery.smp.data.model.DBCertificate; @@ -15,7 +16,6 @@ import eu.europa.ec.edelivery.smp.services.ConfigurationService; import eu.europa.ec.edelivery.smp.utils.BCryptPasswordHash; import eu.europa.ec.edelivery.smp.utils.SecurityUtils; import eu.europa.ec.edelivery.smp.utils.SessionSecurityUtils; -import eu.europa.ec.edelivery.smp.utils.X509CertificateUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -39,6 +39,10 @@ import java.util.Optional; import java.util.regex.Pattern; import java.util.stream.Collectors; +/** + * @author Joze Rihtarsic + * @since 4.1 + */ @Service public class UIUserService extends UIServiceBase<DBUser, UserRO> { @@ -174,9 +178,10 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { CertificateRO certRo = user.getCertificate(); LOG.info(certRo.getEncodedValue()); if (user.getCertificate().getEncodedValue() != null) { - X509Certificate x509Certificate = X509CertificateUtils.getX509Certificate(Base64.getMimeDecoder().decode(certRo.getEncodedValue())); + String certificateAlias; try { + X509Certificate x509Certificate = X509CertificateUtils.getX509Certificate(Base64.getMimeDecoder().decode(certRo.getEncodedValue())); certificateAlias = truststoreService.addCertificate(certRo.getAlias(), x509Certificate); } catch (NoSuchAlgorithmException | KeyStoreException | IOException | CertificateException e) { LOG.error("Error occurred while adding certificate to truststore.", e); diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/SessionSecurityUtils.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/SessionSecurityUtils.java index 6628a098e..ae803480f 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/SessionSecurityUtils.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/SessionSecurityUtils.java @@ -1,15 +1,41 @@ package eu.europa.ec.edelivery.smp.utils; import eu.europa.ec.edelivery.smp.auth.SMPAuthenticationToken; +import eu.europa.ec.edelivery.smp.auth.SMPUserDetails; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; +import org.springframework.security.cas.authentication.CasAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +/** + * Class provides common session security tools to enhance UI security. + * Session is enabled only for UI. To increase security SMP encrypt sensitive data. The + * class provides tools for encrypting, decrypting data for the session. + * + * @author Joze Rihtarsic + * @since 4.2 + */ public class SessionSecurityUtils { private static final SMPLogger LOG = SMPLoggerFactory.getLogger(SessionSecurityUtils.class); + /** + * ' + * Currently authentication tokens supported to create na UI session. + */ + public static final List<Class> sessionAuthenticationClasses = Arrays.asList(SMPAuthenticationToken.class, + CasAuthenticationToken.class); + + /** + * SMP uses entity ids type long. Because the keys are sequence keys, SMP encrypts ids for the User. + * + * @param id + * @return + */ public static String encryptedEntityId(Long id) { if (id == null) { return null; @@ -29,7 +55,7 @@ public class SessionSecurityUtils { return new Long(value); } - public static SecurityUtils.Secret getAuthenticationSecret() { + public static Authentication getSessionAuthentication() { if (SecurityContextHolder.getContext() == null) { LOG.warn("No Security context!"); return null; @@ -39,13 +65,53 @@ public class SessionSecurityUtils { LOG.warn("No active Authentication!"); return null; } - if (!(authentication instanceof SMPAuthenticationToken)) { - LOG.warn("Authentication is not class type: SMPAuthenticationToken!"); + + if (getSessionAuthenticationClasses().contains(authentication.getClass())) { + return authentication; + } + + LOG.warn("Authentication class [{}] is not session enabled class types: [{}]!", authentication.getClass(), + getSessionAuthenticationClasses().stream().map(Class::getName).collect(Collectors.toList())); + return null; + } + + public static SMPUserDetails getSessionUserDetails() { + + Authentication authentication = getSessionAuthentication(); + if (authentication == null) { + LOG.warn("No active SMP session Authentication!"); + return null; + } + + if (authentication instanceof SMPAuthenticationToken) { + LOG.debug("Return user details from SMPAuthenticationToken"); + return ((SMPAuthenticationToken) authentication).getUserDetails(); + } + + if (authentication instanceof CasAuthenticationToken) { + LOG.debug("Return session secret from CasAuthenticationToken"); + return (SMPUserDetails) ((CasAuthenticationToken) authentication).getUserDetails(); + } + + LOG.warn("Authentication class [{}] is not session enabled class types: [{}]!", authentication.getClass(), + getSessionAuthenticationClasses().stream().map(Class::getName).collect(Collectors.toList())); + return null; + } + + public static SecurityUtils.Secret getAuthenticationSecret() { + SMPUserDetails smpUserDetails = getSessionUserDetails(); + if (smpUserDetails == null) { + LOG.warn("No SMPUserDetails object!"); return null; } - return ((SMPAuthenticationToken) authentication).getSecret(); + return smpUserDetails.getSessionSecret(); } + /** + * Method returns authentication name for logging + * + * @return authentication name + */ public static String getAuthenticationName() { if (SecurityContextHolder.getContext() == null) { LOG.debug("No Security context!"); @@ -57,6 +123,9 @@ public class SessionSecurityUtils { return null; } return authentication.getName(); + } + public static List<Class> getSessionAuthenticationClasses() { + return sessionAuthenticationClasses; } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/X509CertificateUtils.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/X509CertificateUtils.java deleted file mode 100644 index bbcedc963..000000000 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/X509CertificateUtils.java +++ /dev/null @@ -1,175 +0,0 @@ -package eu.europa.ec.edelivery.smp.utils; - - -import eu.europa.ec.edelivery.security.PreAuthenticatedCertificatePrincipal; -import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; -import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; -import org.bouncycastle.asn1.ASN1InputStream; -import org.bouncycastle.asn1.ASN1Primitive; -import org.bouncycastle.asn1.DERIA5String; -import org.bouncycastle.asn1.DEROctetString; -import org.bouncycastle.asn1.x500.X500Name; -import org.bouncycastle.asn1.x509.*; -import org.bouncycastle.cert.X509v3CertificateBuilder; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.operator.ContentSigner; -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; - -import javax.security.auth.x500.X500Principal; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.math.BigInteger; -import java.security.*; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Base64; -import java.util.Calendar; -import java.util.List; - -public class X509CertificateUtils { - - private static final String TEST_CERT_ISSUER_DN = "CN=rootCNTest,OU=B4,O=DIGIT,L=Brussels,ST=BE,C=BE"; - - public static void setupJCEProvider() { - Provider[] providerList = Security.getProviders(); - if (providerList == null || providerList.length <= 0 || !(providerList[0] instanceof BouncyCastleProvider)) { - Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1); - } - } - - public static void createAndAddTextCertificate(String subject, KeyStore keystore, String secToken) throws Exception { - setupJCEProvider(); - Calendar from = Calendar.getInstance(); - from.add(Calendar.DAY_OF_MONTH, -1); - Calendar to = Calendar.getInstance(); - to.add(Calendar.YEAR, 5); - - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - keyGen.initialize(2048); - KeyPair key = keyGen.generateKeyPair(); - X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(new X500Name(TEST_CERT_ISSUER_DN),BigInteger.ONE, from.getTime(), to.getTime(), new X500Name(subject), SubjectPublicKeyInfo.getInstance(key.getPublic().getEncoded())); - - ContentSigner sigGen = new JcaContentSignerBuilder("SHA1WithRSAEncryption").setProvider("BC").build(key.getPrivate()); - X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certBuilder.build(sigGen)); - keystore.setKeyEntry("sample_key", key.getPrivate(), secToken.toCharArray(), new X509Certificate[]{cert}); - } - /** - * Extracts all CRL distribution point URLs from the - * "CRL Distribution Point" extension in a X.509 certificate. If CRL - * distribution point extension is unavailable, returns an empty list. - */ - public static List<String> getCrlDistributionPoints(X509Certificate cert) { - byte[] crldpExt = cert.getExtensionValue(Extension.cRLDistributionPoints.getId()); - if (crldpExt == null) { - return new ArrayList<>(); - } - ASN1InputStream oAsnInStream = new ASN1InputStream( - new ByteArrayInputStream(crldpExt)); - ASN1Primitive derObjCrlDP; - try { - derObjCrlDP = oAsnInStream.readObject(); - } catch (IOException e) { - throw new SMPRuntimeException(ErrorCode.CERTIFICATE_ERROR, "Error while extracting CRL distribution point URLs", e); - } - DEROctetString dosCrlDP = (DEROctetString) derObjCrlDP; - byte[] crldpExtOctets = dosCrlDP.getOctets(); - ASN1InputStream oAsnInStream2 = new ASN1InputStream( - new ByteArrayInputStream(crldpExtOctets)); - ASN1Primitive derObj2; - try { - derObj2 = oAsnInStream2.readObject(); - } catch (IOException e) { - throw new SMPRuntimeException(ErrorCode.CERTIFICATE_ERROR, "Error while extracting CRL distribution point URLs", e); - } - CRLDistPoint distPoint = CRLDistPoint.getInstance(derObj2); - List<String> crlUrls = new ArrayList<>(); - for (DistributionPoint dp : distPoint.getDistributionPoints()) { - DistributionPointName dpn = dp.getDistributionPoint(); - // Look for URIs in fullName - if (dpn != null - && dpn.getType() == DistributionPointName.FULL_NAME) { - GeneralName[] genNames = GeneralNames.getInstance( - dpn.getName()).getNames(); - // Look for an URI - for (GeneralName genName : genNames) { - if (genName.getTagNo() == GeneralName.uniformResourceIdentifier) { - String url = DERIA5String.getInstance( - genName.getName()).getString(); - - - crlUrls.add(url); - } - } - } - } - return crlUrls; - } - - public static String getCrlDistributionUrl(X509Certificate cert) { - List<String> list = getCrlDistributionPoints(cert); - return list.isEmpty()?null:extractHttpCrlDistributionPoint(list); - } - - /** - * Method retrieves https. If https does not exist it return http distribution list. - * (LDAP is not allowed (FW OPEN) in targeted network) - * - * @param urlList - * @return - */ - public static String extractHttpCrlDistributionPoint(List<String> urlList) { - String httpsUrl = null; - String httpUrl = null; - for (String url : urlList) { - String newUrl = url.trim(); - if (newUrl.toLowerCase().startsWith("https://")) { - httpsUrl = newUrl; - } else if (newUrl.toLowerCase().startsWith("http://")) { - httpUrl = newUrl; - - } - } - return httpsUrl == null ? httpUrl : httpsUrl; - } - - - - public static PreAuthenticatedCertificatePrincipal extractPrincipalFromCertificate(X509Certificate cert) { - - String subject = cert.getSubjectX500Principal().getName(X500Principal.RFC2253); - String issuer = cert.getIssuerX500Principal().getName(X500Principal.RFC2253); - BigInteger serial = cert.getSerialNumber(); - - return new PreAuthenticatedCertificatePrincipal(subject, issuer, serial, cert.getNotBefore(), cert.getNotAfter()); - } - - public static X509Certificate getX509Certificate(byte[] certBytes) { - try { - InputStream is = new ByteArrayInputStream(certBytes); - return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(is); - } catch (CertificateException exc) { - throw new SMPRuntimeException(ErrorCode.CERTIFICATE_ERROR, String.format("The certificate is not valid - [%s]", exc.getMessage()), exc); - } - } - - public static X509Certificate getX509Certificate(String publicKey) { - // if certificate has begin certificate - then is PEM encoded - if (publicKey.contains("BEGIN CERTIFICATE")) { - return getX509Certificate(publicKey.getBytes()); - } else { - byte[] buff; - // try do decode - try { - buff = Base64.getDecoder().decode(publicKey.getBytes()); - } catch (java.lang.IllegalArgumentException ex) { - buff = publicKey.getBytes(); - } - return getX509Certificate(buff); - } - } - -} diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/utils/SessionSecurityUtilsTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/utils/SessionSecurityUtilsTest.java new file mode 100644 index 000000000..13748c167 --- /dev/null +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/utils/SessionSecurityUtilsTest.java @@ -0,0 +1,124 @@ +package eu.europa.ec.edelivery.smp.utils; + +import eu.europa.ec.edelivery.smp.auth.SMPAuthenticationToken; +import eu.europa.ec.edelivery.smp.auth.SMPUserDetails; +import eu.europa.ec.edelivery.smp.data.ui.auth.SMPAuthority; +import org.jasig.cas.client.validation.Assertion; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.cas.authentication.CasAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * @author Joze Rihtarsic + * @since 4.2 + */ +public class SessionSecurityUtilsTest { + + @After + public void afterUnitTest() { + // clear authentication + SecurityContextHolder.getContext().setAuthentication(null); + } + + @Test + public void encryptedEntityId() { + SMPAuthenticationToken token = setTestSMPAuthenticationToken(); + Long value = Long.valueOf(12332L); + String result = SessionSecurityUtils.encryptedEntityId(value); + + assertNotNull(result); + String decResult = SecurityUtils.decryptUrlSafe(token.getSecret(), result); + assertEquals(value, Long.valueOf(decResult)); + } + + @Test + public void decryptEntityId() { + SMPAuthenticationToken token = setTestSMPAuthenticationToken(); + Long value = Long.valueOf(12332L); + String encValue = SecurityUtils.encryptURLSafe(token.getSecret(), value.toString()); + + Long result = SessionSecurityUtils.decryptEntityId(encValue); + + assertNotNull(result); + assertEquals(value, result); + } + + @Test + public void getAuthenticationSecretFromSMPAuthenticationToken() { + // given + SMPAuthenticationToken token = setTestSMPAuthenticationToken(); + + SecurityUtils.Secret result = SessionSecurityUtils.getAuthenticationSecret(); + assertNotNull(result); + assertEquals(token.getSecret(), result); + } + + @Test + public void getAuthenticationSecretFromCasAuthenticationToken() { + // given + CasAuthenticationToken token = setTestCasAuthenticationToken(); + + + SecurityUtils.Secret result = SessionSecurityUtils.getAuthenticationSecret(); + assertNotNull(result); + assertEquals(((SMPUserDetails) token.getUserDetails()).getSessionSecret(), result); + } + + @Test + public void getAuthenticationSecretNotSupported() { + // given + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(null, null); + SecurityContextHolder.getContext().setAuthentication(token); + + SecurityUtils.Secret result = SessionSecurityUtils.getAuthenticationSecret(); + + assertNull(result); + } + + @Test + public void getAuthenticationName() { + Authentication authentication = Mockito.mock(Authentication.class); + SecurityContextHolder.getContext().setAuthentication(authentication); + String testName = "testName"; + Mockito.doReturn(testName).when(authentication).getName(); + + String result = SessionSecurityUtils.getAuthenticationName(); + + Assert.assertNotNull(result); + Assert.assertEquals(testName, result); + } + + @Test + public void getSessionAuthenticationClasses() { + List<Class> list = SessionSecurityUtils.getSessionAuthenticationClasses(); + Assert.assertEquals(2, list.size()); + Assert.assertTrue(list.contains(SMPAuthenticationToken.class)); + Assert.assertTrue(list.contains(CasAuthenticationToken.class)); + } + + public SMPAuthenticationToken setTestSMPAuthenticationToken() { + SecurityUtils.Secret secret = SecurityUtils.generatePrivateSymmetricKey(); + SMPAuthenticationToken token = new SMPAuthenticationToken(null, null, new SMPUserDetails(null, secret, null)); + SecurityContextHolder.getContext().setAuthentication(token); + return token; + } + + public CasAuthenticationToken setTestCasAuthenticationToken() { + SecurityUtils.Secret secret = SecurityUtils.generatePrivateSymmetricKey(); + List<SMPAuthority> smpAuthorities = Collections.singletonList(SMPAuthority.S_AUTHORITY_SMP_ADMIN); + CasAuthenticationToken token = new CasAuthenticationToken("test", "test", "test", smpAuthorities, + new SMPUserDetails(null, secret, smpAuthorities), Mockito.mock(Assertion.class)); + SecurityContextHolder.getContext().setAuthentication(token); + return token; + } +} \ No newline at end of file diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/utils/X509CertificateUtilsTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/utils/X509CertificateUtilsTest.java index 5166855b9..cc310f525 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/utils/X509CertificateUtilsTest.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/utils/X509CertificateUtilsTest.java @@ -16,10 +16,10 @@ package eu.europa.ec.edelivery.smp.utils; +import eu.europa.ec.edelivery.security.utils.X509CertificateUtils; import junitparams.JUnitParamsRunner; import junitparams.Parameters; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.time.DateUtils; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -36,7 +36,10 @@ import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; - +/** + * @author Joze Rihtarsic + * @since 4.1 + */ @RunWith(JUnitParamsRunner.class) public class X509CertificateUtilsTest { diff --git a/smp-webapp/pom.xml b/smp-webapp/pom.xml index 4658db474..50879cc6a 100644 --- a/smp-webapp/pom.xml +++ b/smp-webapp/pom.xml @@ -103,10 +103,6 @@ <artifactId>hamcrest-junit</artifactId> <scope>test</scope> </dependency> - <dependency> - <groupId>org.springframework.security</groupId> - <artifactId>spring-security-cas</artifactId> - </dependency> </dependencies> <build> diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProvider.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProvider.java index 6dc991bac..fc4fa5f2d 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProvider.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProvider.java @@ -5,6 +5,7 @@ import eu.europa.ec.edelivery.security.cert.CertificateValidator; import eu.europa.ec.edelivery.smp.data.dao.UserDao; import eu.europa.ec.edelivery.smp.data.model.DBCertificate; import eu.europa.ec.edelivery.smp.data.model.DBUser; +import eu.europa.ec.edelivery.smp.data.ui.UserRO; import eu.europa.ec.edelivery.smp.data.ui.auth.SMPAuthority; import eu.europa.ec.edelivery.smp.data.ui.enums.AlertSuspensionMomentEnum; import eu.europa.ec.edelivery.smp.data.ui.enums.CredentialTypeEnum; @@ -19,7 +20,9 @@ import eu.europa.ec.edelivery.smp.services.ui.UITruststoreService; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.convert.ConversionService; import org.springframework.security.authentication.*; +import org.springframework.security.cas.web.CasAuthenticationFilter; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.crypto.bcrypt.BCrypt; @@ -60,12 +63,8 @@ public class SMPAuthenticationProvider implements AuthenticationProvider { */ private static final ThreadLocal<DateFormat> dateFormatLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("MMM d hh:mm:ss yyyy zzz", US)); - // generate dummyPassword hash just to mimic password validation to disable attacker to discover - // usernames because of different response times if password or username is wrong - private final String dummyPasswordHash; - private final String dummyPassword; - final UserDao mUserDao; + final ConversionService conversionService; final CRLVerifierService crlVerifierService; final UITruststoreService truststoreService; final ConfigurationService configurationService; @@ -73,13 +72,13 @@ public class SMPAuthenticationProvider implements AuthenticationProvider { @Autowired public SMPAuthenticationProvider(UserDao mUserDao, + ConversionService conversionService, CRLVerifierService crlVerifierService, UITruststoreService truststoreService, ConfigurationService configurationService, AlertService alertService) { - this.dummyPassword = UUID.randomUUID().toString(); - this.dummyPasswordHash = BCrypt.hashpw(dummyPassword, BCrypt.gensalt()); this.mUserDao = mUserDao; + this.conversionService = conversionService; this.crlVerifierService = crlVerifierService; this.truststoreService = truststoreService; this.configurationService = configurationService; @@ -101,7 +100,8 @@ public class SMPAuthenticationProvider implements AuthenticationProvider { } } else if (authenticationToken instanceof UsernamePasswordAuthenticationToken) { LOG.info("try to authentication Token: [{}] with user:[{}]" , authenticationToken.getClass(), authenticationToken.getPrincipal()); - if ("_cas_stateful_".equalsIgnoreCase((String)authenticationToken.getPrincipal())){ + if (CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER.equalsIgnoreCase((String)authenticationToken.getPrincipal()) + || CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER.equalsIgnoreCase((String)authenticationToken.getPrincipal())){ LOG.debug("Ignore CAS authentication and leave it to cas authentication module"); return null; } @@ -127,7 +127,6 @@ public class SMPAuthenticationProvider implements AuthenticationProvider { */ public Authentication authenticateByCertificateToken(PreAuthenticatedCertificatePrincipal principal) { LOG.info("authenticateByCertificateToken:" + principal.getName()); - KeyStore truststore = truststoreService.getTrustStore(); DBUser user; @@ -209,13 +208,27 @@ public class SMPAuthenticationProvider implements AuthenticationProvider { // get role String role = "WS_" + user.getRole(); LOG.securityInfo(SMPMessageCode.SEC_USER_AUTHENTICATED, userToken, role); - SMPCertificateAuthentication authentication = new SMPCertificateAuthentication(principal, Collections.singletonList(new SMPAuthority(role)), user); + SMPCertificateAuthentication authentication = new SMPCertificateAuthentication(principal, Collections.singletonList( + SMPAuthority.getAuthorityByRoleName(role)), user); authentication.setAuthenticated(true); return authentication; } + public void delayResponse(long startTime) { + int delayInMS = configurationService.getAccessTokenLoginFailDelayInMilliSeconds() - (int) (Calendar.getInstance().getTimeInMillis() - startTime); + if (delayInMS > 0) { + try { + LOG.debug("Delay response for [{}] ms to mask password/username login failures!", delayInMS); + Thread.sleep(delayInMS); + } catch (InterruptedException ie) { + LOG.debug("Thread interrupted during sleep.", ie); + Thread.currentThread().interrupt(); + } + } + } + /** * Method tests if user account Suspended * @@ -264,18 +277,17 @@ public class SMPAuthenticationProvider implements AuthenticationProvider { String authenticationTokenId = auth.getName(); String authenticationTokenValue = auth.getCredentials().toString(); + long startTime = Calendar.getInstance().getTimeInMillis(); DBUser user; try { Optional<DBUser> oUsr = mUserDao.findUserByAuthenticationToken(authenticationTokenId); if (!oUsr.isPresent() || !oUsr.get().isActive()) { LOG.securityWarn(SMPMessageCode.SEC_USER_NOT_EXISTS, authenticationTokenId); - //run validation on dummy password to achieve similar response time - // as it would be if the password is invalid - BCrypt.checkpw(dummyPassword, dummyPasswordHash); //https://www.owasp.org/index.php/Authentication_Cheat_Sheet // Do not reveal the status of an existing account. Not to use UsernameNotFoundException + delayResponse(startTime); throw new BadCredentialsException(LOGIN_FAILED_MESSAGE); } user = oUsr.get(); @@ -292,7 +304,7 @@ public class SMPAuthenticationProvider implements AuthenticationProvider { try { if (!BCrypt.checkpw(authenticationTokenValue, user.getAccessToken())) { - loginAttemptForAccessTokenFailed(user); + loginAttemptForAccessTokenFailed(user, startTime); } user.setSequentialTokenLoginFailureCount(0); user.setLastTokenFailedLoginAttempt(null); @@ -302,14 +314,21 @@ public class SMPAuthenticationProvider implements AuthenticationProvider { LOG.securityWarn(SMPMessageCode.SEC_INVALID_PASSWORD, ex, authenticationTokenId); throw new BadCredentialsException(LOGIN_FAILED_MESSAGE); } - String role = "WS_" + user.getRole(); - SMPAuthenticationToken smpAuthenticationToken = new SMPAuthenticationToken(authenticationTokenId, authenticationTokenValue, Collections.singletonList(new SMPAuthority(role)), user); + // the webservice authentication with corresponding web-service authority; + SMPAuthority authority = SMPAuthority.getAuthorityByRoleName("WS_" + user.getRole()); + // the webservice authentication does not support session set the session secret is null! + SMPUserDetails userDetails = new SMPUserDetails(user, null, Collections.singletonList(authority)); + + SMPAuthenticationToken smpAuthenticationToken = new SMPAuthenticationToken(authenticationTokenId, + authenticationTokenValue, + userDetails); + + LOG.securityInfo(SMPMessageCode.SEC_USER_AUTHENTICATED, authenticationTokenId, authority.getRole()); - LOG.securityInfo(SMPMessageCode.SEC_USER_AUTHENTICATED, authenticationTokenId, role); return smpAuthenticationToken; } - public void loginAttemptForAccessTokenFailed(DBUser user) { + public void loginAttemptForAccessTokenFailed(DBUser user, long startTime) { user.setSequentialTokenLoginFailureCount(user.getSequentialTokenLoginFailureCount() != null ? user.getSequentialTokenLoginFailureCount() + 1 : 1); user.setLastTokenFailedLoginAttempt(OffsetDateTime.now()); @@ -326,6 +345,7 @@ public class SMPAuthenticationProvider implements AuthenticationProvider { } else { alertService.alertCredentialVerificationFailed(user, CredentialTypeEnum.ACCESS_TOKEN); } + delayResponse(startTime); throw new BadCredentialsException(LOGIN_FAILED_MESSAGE); } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProviderForUI.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProviderForUI.java index 4c5509fb4..8409a4fce 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProviderForUI.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProviderForUI.java @@ -13,12 +13,13 @@ import eu.europa.ec.edelivery.smp.services.AlertService; import eu.europa.ec.edelivery.smp.services.CRLVerifierService; import eu.europa.ec.edelivery.smp.services.ConfigurationService; import eu.europa.ec.edelivery.smp.services.ui.UITruststoreService; +import eu.europa.ec.edelivery.smp.utils.SecurityUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.convert.ConversionService; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.crypto.bcrypt.BCrypt; @@ -26,6 +27,7 @@ import org.springframework.stereotype.Component; import java.time.OffsetDateTime; import java.time.temporal.ChronoUnit; +import java.util.Calendar; import java.util.Collections; import java.util.Optional; @@ -41,6 +43,7 @@ public class SMPAuthenticationProviderForUI implements AuthenticationProvider { private static final SMPLogger LOG = SMPLoggerFactory.getLogger(SMPAuthenticationProviderForUI.class); final UserDao mUserDao; + final ConversionService conversionService; final CRLVerifierService crlVerifierService; final UITruststoreService truststoreService; final ConfigurationService configurationService; @@ -49,11 +52,13 @@ public class SMPAuthenticationProviderForUI implements AuthenticationProvider { @Autowired public SMPAuthenticationProviderForUI(UserDao mUserDao, + ConversionService conversionService, CRLVerifierService crlVerifierService, AlertService alertService, UITruststoreService truststoreService, ConfigurationService configurationService) { this.mUserDao = mUserDao; + this.conversionService = conversionService; this.crlVerifierService = crlVerifierService; this.alertService = alertService; this.truststoreService = truststoreService; @@ -67,15 +72,17 @@ public class SMPAuthenticationProviderForUI implements AuthenticationProvider { Authentication authentication = null; // PreAuthentication token for the rest service certificate authentication LOG.debug("Authenticate authentication token type: [{}]", authenticationToken.getClass()); - if (authenticationToken instanceof UsernamePasswordAuthenticationToken) { - authentication = authenticateByUsernamePassword((UsernamePasswordAuthenticationToken) authenticationToken); + if (authenticationToken instanceof UILoginAuthenticationToken) { + authentication = authenticateByUsernamePassword((UILoginAuthenticationToken) authenticationToken); } return authentication; } - public Authentication authenticateByUsernamePassword(UsernamePasswordAuthenticationToken auth) + public Authentication authenticateByUsernamePassword(UILoginAuthenticationToken auth) throws AuthenticationException { + long startTime = Calendar.getInstance().getTimeInMillis(); + String username = auth.getName(); String userCredentialToken = auth.getCredentials().toString(); @@ -84,7 +91,9 @@ public class SMPAuthenticationProviderForUI implements AuthenticationProvider { Optional<DBUser> oUsr = mUserDao.findUserByUsername(username); if (!oUsr.isPresent()) { LOG.debug("User with username does not exists [{}], continue with next authentication provider"); - return null; + LOG.securityWarn(SMPMessageCode.SEC_INVALID_PASSWORD, "Username does not exits", username); + delayResponse(startTime); + throw new BadCredentialsException("Login failed; Invalid userID or password"); } user = oUsr.get(); } catch (AuthenticationException ex) { @@ -99,11 +108,18 @@ public class SMPAuthenticationProviderForUI implements AuthenticationProvider { validateIfUserAccountIsSuspended(user); + SMPAuthority authority = SMPAuthority.getAuthorityByRoleName(user.getRole()); + // the webservice authentication does not support session set the session secret is null! + SMPUserDetails userDetails = new SMPUserDetails(user, + SecurityUtils.generatePrivateSymmetricKey(), + Collections.singletonList(authority)); + String role = user.getRole(); - SMPAuthenticationToken smpAuthenticationToken = new SMPAuthenticationToken(username, userCredentialToken, Collections.singletonList(new SMPAuthority(role)), user); + SMPAuthenticationToken smpAuthenticationToken = new SMPAuthenticationToken(username, userCredentialToken, + userDetails); try { if (!BCrypt.checkpw(userCredentialToken, user.getPassword())) { - loginAttemptForUserFailed(user); + loginAttemptForUserFailed(user, startTime); } user.setSequentialLoginFailureCount(0); user.setLastFailedLoginAttempt(null); @@ -117,7 +133,20 @@ public class SMPAuthenticationProviderForUI implements AuthenticationProvider { return smpAuthenticationToken; } - public void loginAttemptForUserFailed(DBUser user) { + public void delayResponse(long startTime) { + int delayInMS = configurationService.getLoginFailDelayInMilliSeconds() - (int) (Calendar.getInstance().getTimeInMillis() - startTime); + if (delayInMS > 0) { + try { + LOG.debug("Delay response for [{}] ms to mask password/username login failures!", delayInMS); + Thread.sleep(delayInMS); + } catch (InterruptedException ie) { + LOG.debug("Thread interrupted during sleep.", ie); + Thread.currentThread().interrupt(); + } + } + } + + public void loginAttemptForUserFailed(DBUser user, long startTime) { user.setSequentialLoginFailureCount(user.getSequentialLoginFailureCount() != null ? user.getSequentialLoginFailureCount() + 1 : 1); user.setLastFailedLoginAttempt(OffsetDateTime.now()); mUserDao.update(user); @@ -128,6 +157,7 @@ public class SMPAuthenticationProviderForUI implements AuthenticationProvider { } else { alertService.alertCredentialVerificationFailed(user, CredentialTypeEnum.USERNAME_PASSWORD); } + delayResponse(startTime); throw new BadCredentialsException("Login failed; Invalid userID or password"); } @@ -176,7 +206,7 @@ public class SMPAuthenticationProviderForUI implements AuthenticationProvider { @Override public boolean supports(Class<?> auth) { LOG.info("Support authentication: " + auth); - boolean supportAuthentication = auth.equals(UsernamePasswordAuthenticationToken.class); + boolean supportAuthentication = auth.equals(UILoginAuthenticationToken.class); if (!supportAuthentication) { LOG.warn("SMP does not support authentication type: " + auth); } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationService.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationService.java index 992f9d13b..62faadc05 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationService.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationService.java @@ -3,10 +3,8 @@ package eu.europa.ec.edelivery.smp.auth; import eu.europa.ec.edelivery.smp.config.SMPSecurityConstants; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolder; @@ -21,20 +19,28 @@ import javax.servlet.http.HttpServletResponse; import static eu.europa.ec.edelivery.smp.utils.SMPCookieWriter.CSRF_COOKIE_NAME; import static eu.europa.ec.edelivery.smp.utils.SMPCookieWriter.SESSION_COOKIE_NAME; +/** + * The UI authentication services for login ,logout, retrieving current session user etc.. The services are intended for + * stateful UI service calls. + * + * @author Joze Rihtarsic + * @since 4.1 + */ @Service public class SMPAuthenticationService { - private static final SMPLogger LOG = SMPLoggerFactory.getLogger(SMPAuthenticationService.class); - @Autowired - @Qualifier(SMPSecurityConstants.SMP_UI_AUTHENTICATION_MANAGER_BEAN) - private AuthenticationManager authenticationManager; + private final AuthenticationManager authenticationManager; + + public SMPAuthenticationService(@Qualifier(SMPSecurityConstants.SMP_UI_AUTHENTICATION_MANAGER_BEAN) AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + } @Transactional(noRollbackFor = AuthenticationException.class) public Authentication authenticate(String username, String password) { LOG.debug("Authenticate: [{}]", username); - UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password); - SMPAuthenticationToken authentication = (SMPAuthenticationToken) authenticationManager.authenticate(token); + UILoginAuthenticationToken token = new UILoginAuthenticationToken(username, password); + Authentication authentication = authenticationManager.authenticate(token); SecurityContextHolder.getContext().setAuthentication(authentication); return authentication; } @@ -45,7 +51,6 @@ public class SMPAuthenticationService { LOG.debug("Cannot perform logout: no user is authenticated"); return; } - LOG.info("Logging out user [{}]", auth.getName()); new CookieClearingLogoutHandler(SESSION_COOKIE_NAME, CSRF_COOKIE_NAME).logout(request, response, null); LOG.info("Cleared cookies"); diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationService.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationService.java index 3ac7d6209..3620970e1 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationService.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationService.java @@ -1,26 +1,31 @@ package eu.europa.ec.edelivery.smp.auth; +import eu.europa.ec.edelivery.smp.data.dao.UserDao; +import eu.europa.ec.edelivery.smp.data.model.DBUser; import eu.europa.ec.edelivery.smp.data.ui.UserRO; import eu.europa.ec.edelivery.smp.data.ui.auth.SMPAuthority; import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; +import eu.europa.ec.edelivery.smp.services.ConfigurationService; import eu.europa.ec.edelivery.smp.services.ServiceGroupService; import eu.europa.ec.edelivery.smp.utils.SessionSecurityUtils; import org.apache.commons.lang3.StringUtils; +import org.springframework.core.convert.ConversionService; import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.cas.authentication.CasAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.session.SessionAuthenticationException; import org.springframework.stereotype.Service; +import java.time.OffsetDateTime; import java.util.stream.Collectors; import static eu.europa.ec.edelivery.smp.data.ui.auth.SMPAuthority.*; /** * @author Sebastian-Ion TINCU + * @since 4.1 */ @Service("smpAuthorizationService") public class SMPAuthorizationService { @@ -28,27 +33,36 @@ public class SMPAuthorizationService { private static final SMPLogger LOG = SMPLoggerFactory.getLogger(SMPAuthorizationService.class); final private ServiceGroupService serviceGroupService; - - public SMPAuthorizationService(ServiceGroupService serviceGroupService) { + final private ConversionService conversionService; + final private ConfigurationService configurationService; + final private UserDao userDao; + + public SMPAuthorizationService(ServiceGroupService serviceGroupService, + ConversionService conversionService, + ConfigurationService configurationService, + UserDao userDao) { this.serviceGroupService = serviceGroupService; + this.conversionService = conversionService; + this.configurationService=configurationService; + this.userDao = userDao; } public boolean isSystemAdministrator() { - SMPAuthenticationToken authentication = getAndValidateSessionAuthentication(); - boolean hasSystemRole = hasSessionUserRole(S_AUTHORITY_TOKEN_SYSTEM_ADMIN, authentication); - LOG.debug("Logged user [{}] is system administrator role [{}]", authentication.getUser().getUsername(), hasSystemRole); + SMPUserDetails userDetails = getAndValidateUserDetails(); + boolean hasSystemRole = hasSessionUserRole(S_AUTHORITY_TOKEN_SYSTEM_ADMIN, userDetails); + LOG.debug("Logged user [{}] is system administrator role [{}]", userDetails.getUsername(), hasSystemRole); return hasSystemRole; } public boolean isSMPAdministrator() { - SMPAuthenticationToken authentication = getAndValidateSessionAuthentication(); - boolean hasSystemRole = hasSessionUserRole(S_AUTHORITY_TOKEN_SMP_ADMIN, authentication); - LOG.debug("Logged user [{}] is SMP administrator role [{}]", authentication.getUser().getUsername(), hasSystemRole); - return hasSystemRole; + SMPUserDetails userDetails = getAndValidateUserDetails(); + boolean hasRole = hasSessionUserRole(S_AUTHORITY_TOKEN_SMP_ADMIN, userDetails); + LOG.debug("Logged user [{}] is SMP administrator role [{}]", userDetails.getUsername(), hasRole); + return hasRole; } public boolean isCurrentlyLoggedIn(String userId) { - SMPAuthenticationToken authentication = getAndValidateSessionAuthentication(); + SMPUserDetails userDetails = getAndValidateUserDetails(); Long entityId; try { entityId = SessionSecurityUtils.decryptEntityId(userId); @@ -56,26 +70,32 @@ public class SMPAuthorizationService { LOG.error("Error occurred while decrypting user-id:[" + userId + "]", ex); throw new BadCredentialsException("Login failed; Invalid userID or password"); } - Long loggedUserId = authentication.getUser().getId(); - return entityId.equals(loggedUserId); + return entityId.equals(userDetails.getUser().getId()); } public boolean isAuthorizedForManagingTheServiceMetadataGroup(Long serviceMetadataId) { - SMPAuthenticationToken authentication = getAndValidateSessionAuthentication(); - if (hasSessionUserRole(S_AUTHORITY_TOKEN_SMP_ADMIN, authentication)) { + SMPUserDetails userDetails = getAndValidateUserDetails(); + if (hasSessionUserRole(S_AUTHORITY_TOKEN_SMP_ADMIN, userDetails)) { LOG.debug("SMP admin is authorized to manage service metadata: [{}]" + serviceMetadataId); return true; } - if (!hasSessionUserRole(S_AUTHORITY_TOKEN_SERVICE_GROUP_ADMIN, authentication)) { + if (!hasSessionUserRole(S_AUTHORITY_TOKEN_SERVICE_GROUP_ADMIN, userDetails)) { LOG.debug("User is Service group admin nor SMP admin. User is not allowed to manage service metadata: [{}]" + serviceMetadataId); return false; } - Long userId = authentication.getUser().getId(); + Long userId = userDetails.getUser().getId(); return serviceGroupService.isServiceGroupOwnerForMetadataID(userId, serviceMetadataId); } + + private boolean hasSessionUserRole(String role, SMPUserDetails userDetails) { + return userDetails.getAuthorities().stream().anyMatch(grantedAuthority -> + StringUtils.equals(role, grantedAuthority.getAuthority()) + ); + } + /** * Returns a user resource with password credentials removed and authorities populated for use in the front-end. * @@ -86,41 +106,52 @@ public class SMPAuthorizationService { userRO.setPassword(""); Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication!=null ){ + if (authentication != null) { userRO.setAuthorities(authentication.getAuthorities().stream().map(val -> (SMPAuthority) val).collect(Collectors.toList())); } return userRO; } - private Authentication getSessionAuthentication() { - if (SecurityContextHolder.getContext() == null) { - LOG.warn("No users is logged-in! Session security context is null!"); - return null; - } - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication == null || !authentication.isAuthenticated()) { - LOG.warn("No users is logged-in! Authentication is null or not authenticated!"); - return null; + public SMPUserDetails getAndValidateUserDetails() { + SMPUserDetails userDetails = SessionSecurityUtils.getSessionUserDetails(); + if (userDetails == null) { + throw new SessionAuthenticationException(ERR_INVALID_OR_NULL); } - if (!(authentication instanceof SMPAuthenticationToken - || authentication instanceof CasAuthenticationToken)) { - LOG.warn("User is logged and authenticated with not supported Authentication [{}]!", authentication.getClass()); + return userDetails; + } + + public UserRO getLoggedUserData() { + SMPUserDetails userDetails = getAndValidateUserDetails(); + // refresh data from database! + DBUser dbUser =userDao.find(userDetails.getUser().getId()); + if (dbUser == null || !dbUser.isActive()) { + LOG.warn("User: [{}] with id [{}] does not exists anymore or is not active.", + userDetails.getUser().getId(), + userDetails.getUser().getUsername()); return null; } - return authentication; + return getUserData(userDetails.getUser()); } - private Authentication getAndValidateSessionAuthentication() { - Authentication authentication = getSessionAuthentication(); - if (authentication == null) { - throw new SessionAuthenticationException(ERR_INVALID_OR_NULL); - } - return authentication; + public UserRO getUserData(DBUser user) { + UserRO userRO = conversionService.convert(user, UserRO.class); + return getUpdatedUserData(userRO); } - private boolean hasSessionUserRole(String role, SMPAuthenticationToken authentication) { - return authentication.getAuthorities().stream().anyMatch(grantedAuthority -> - StringUtils.equals(role, grantedAuthority.getAuthority()) - ); + /** + * Method updates data with "show expire dialog" flag, forces the password change flag and + * sanitize ui data/ + * + * @param userRO + * @return updated user data according to SMP configuration + */ + protected UserRO getUpdatedUserData(UserRO userRO) { + userRO.setShowPasswordExpirationWarning(userRO.getPasswordExpireOn() != null && + OffsetDateTime.now() + .minusDays(configurationService.getPasswordPolicyUIWarningDaysBeforeExpire()) + .isBefore(userRO.getPasswordExpireOn())); + + userRO.setForceChangePassword(userRO.isPasswordExpired() && configurationService.getPasswordPolicyForceChangeIfExpired()); + return sanitize(userRO); } } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCasUserService.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCasUserService.java index eab8b8425..a853e34f5 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCasUserService.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/cas/SMPCasUserService.java @@ -1,10 +1,12 @@ package eu.europa.ec.edelivery.smp.auth.cas; +import eu.europa.ec.edelivery.smp.auth.SMPUserDetails; import eu.europa.ec.edelivery.smp.data.model.DBUser; -import eu.europa.ec.edelivery.smp.data.ui.UserRO; + import eu.europa.ec.edelivery.smp.data.ui.auth.SMPAuthority; import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import eu.europa.ec.edelivery.smp.services.ui.UIUserService; +import eu.europa.ec.edelivery.smp.utils.SecurityUtils; import org.jasig.cas.client.authentication.AttributePrincipal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,23 +49,25 @@ public class SMPCasUserService implements AuthenticationUserDetailsService<CasAs public UserDetails loadUserDetails(CasAssertionAuthenticationToken token) throws UsernameNotFoundException { AttributePrincipal principal = token.getAssertion().getPrincipal(); + // the cas id must match with username String username = principal.getName(); - LOG.info("Principal name: "+username); - LOG.info("Principal: "+principal); + LOG.debug("Got CAS user with principal name: [{}]", username); Map<String, Object> attributes = principal.getAttributes(); for(Map.Entry<String, Object> attribute : attributes.entrySet()) { - LOG.info("Principal attribute "+attribute.getKey()+"="+attribute.getValue()); + LOG.debug("Principal attribute [{}]=[{}] ", attribute.getKey(), attribute.getValue()); } + DBUser dbuser; try { dbuser = uiUserService.findUserByUsername(username); } catch (SMPRuntimeException ex) { throw new UsernameNotFoundException("User with the username ["+username+"] is not registered in SMP", ex); } - UserRO userRo = uiUserService.convertToRo(dbuser); - userRo.setCasAuthenticated(true); - userRo.setPassword(null); - userRo.setAuthorities(Collections.singletonList(new SMPAuthority(userRo.getRole()))); - return userRo; + SMPAuthority authority = SMPAuthority.getAuthorityByRoleName(dbuser.getRole()); + // generate secret for the session + SMPUserDetails smpUserDetails = new SMPUserDetails(dbuser, SecurityUtils.generatePrivateSymmetricKey(),Collections.singletonList(authority)); + smpUserDetails.setCasAuthenticated(true); + LOG.info("Return authenticated user details for username: [{}]", username); + return smpUserDetails; } } \ No newline at end of file diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/AuthenticationResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/AuthenticationResource.java index 11a02caad..6aaebf73a 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/AuthenticationResource.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/AuthenticationResource.java @@ -4,6 +4,7 @@ package eu.europa.ec.edelivery.smp.ui; import eu.europa.ec.edelivery.smp.auth.SMPAuthenticationService; import eu.europa.ec.edelivery.smp.auth.SMPAuthenticationToken; import eu.europa.ec.edelivery.smp.auth.SMPAuthorizationService; +import eu.europa.ec.edelivery.smp.auth.SMPUserDetails; import eu.europa.ec.edelivery.smp.data.model.DBUser; import eu.europa.ec.edelivery.smp.data.ui.LoginRO; import eu.europa.ec.edelivery.smp.data.ui.UserRO; @@ -17,6 +18,7 @@ import org.springframework.core.convert.ConversionService; import org.springframework.security.access.annotation.Secured; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.csrf.CsrfTokenRepository; import org.springframework.transaction.annotation.Transactional; @@ -39,6 +41,7 @@ import static eu.europa.ec.edelivery.smp.utils.SMPCookieWriter.SESSION_COOKIE_NA public class AuthenticationResource { private static final SMPLogger LOG = SMPLoggerFactory.getLogger(AuthenticationResource.class); + public static final String RELATIVE_BASE_ENTRY="../../../#/"; private UIUserService uiUserService; @@ -46,8 +49,6 @@ public class AuthenticationResource { protected SMPAuthorizationService authorizationService; - private ConversionService conversionService; - private ConfigurationService configurationService; private CsrfTokenRepository csrfTokenRepository; @@ -57,14 +58,12 @@ public class AuthenticationResource { @Autowired public AuthenticationResource(SMPAuthenticationService authenticationService , SMPAuthorizationService authorizationService - , ConversionService conversionService , ConfigurationService configurationService , SMPCookieWriter smpCookieWriter , CsrfTokenRepository csrfTokenRepository , UIUserService uiUserService) { this.authenticationService = authenticationService; this.authorizationService = authorizationService; - this.conversionService = conversionService; this.configurationService = configurationService; this.smpCookieWriter = smpCookieWriter; this.csrfTokenRepository = csrfTokenRepository; @@ -80,9 +79,11 @@ public class AuthenticationResource { CsrfToken csfrToken = csrfTokenRepository.generateToken(request); csrfTokenRepository.saveToken(csfrToken, request, response); - SMPAuthenticationToken authentication = (SMPAuthenticationToken) authenticationService.authenticate(loginRO.getUsername(), loginRO.getPassword()); - DBUser user = authentication.getUser(); - return getUserData(user); + SMPAuthenticationToken authentication = (SMPAuthenticationToken) authenticationService.authenticate(loginRO.getUsername(), + loginRO.getPassword()); + SMPUserDetails user = authentication.getUserDetails(); + + return authorizationService.getUserData(user.getUser()); } @DeleteMapping(value = "authentication") @@ -103,50 +104,15 @@ public class AuthenticationResource { public RedirectView authenticateCAS() { LOG.debug("Authenticating cas"); // if user was able to access resource - redirect back to main page - return new RedirectView("../../../#/"); + return new RedirectView(RELATIVE_BASE_ENTRY); } @GetMapping(value = "user") @Secured({S_AUTHORITY_TOKEN_SYSTEM_ADMIN, S_AUTHORITY_TOKEN_SMP_ADMIN, S_AUTHORITY_TOKEN_SERVICE_GROUP_ADMIN}) - public UserRO getUser(HttpServletRequest request, HttpServletResponse response) { - - Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - if (principal instanceof UserRO) { - return getUpdatedUserData((UserRO) principal); - } - - String username = (String) principal; - LOG.debug("get user: [{}]", username); - DBUser user = uiUserService.findUserByUsername(username); - - if (user == null || !user.isActive()) { - LOG.warn("User: [{}] does not exists anymore or is not active.", username); - return null; - } - return getUserData(user); - } - - protected UserRO getUserData(DBUser user) { - UserRO userRO = conversionService.convert(user, UserRO.class); - return getUpdatedUserData(userRO); + public UserRO getUser() { + return authorizationService.getLoggedUserData(); } - /** - * Method updates data with "show expire dialog" flag, forces the password change flag and - * sanitize ui data/ - * - * @param userRO - * @return updated user data according to SMP configuration - */ - protected UserRO getUpdatedUserData(UserRO userRO) { - userRO.setShowPasswordExpirationWarning(userRO.getPasswordExpireOn() != null && - OffsetDateTime.now() - .minusDays(configurationService.getPasswordPolicyUIWarningDaysBeforeExpire()) - .isBefore(userRO.getPasswordExpireOn())); - - userRO.setForceChangePassword(userRO.isPasswordExpired() && configurationService.getPasswordPolicyForceChangeIfExpired()); - return authorizationService.sanitize(userRO); - } /** * set cookie parameters https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie @@ -160,7 +126,8 @@ public class AuthenticationResource { // String sessionId = request.changeSessionId(); smpCookieWriter.writeCookieToResponse(SESSION_COOKIE_NAME, sessionId, - configurationService.getSessionCookieSecure(), configurationService.getSessionCookieMaxAge(), + configurationService.getSessionCookieSecure(), + configurationService.getSessionCookieMaxAge(), configurationService.getSessionCookiePath(), configurationService.getSessionCookieSameSite(), request, response diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/ServiceGroupResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/ServiceGroupResource.java index 23e59f234..f0b73d770 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/ServiceGroupResource.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/ServiceGroupResource.java @@ -1,24 +1,22 @@ package eu.europa.ec.edelivery.smp.ui.external; -import eu.europa.ec.edelivery.smp.auth.SMPAuthenticationToken; +import eu.europa.ec.edelivery.smp.auth.SMPAuthorizationService; +import eu.europa.ec.edelivery.smp.auth.SMPUserDetails; import eu.europa.ec.edelivery.smp.data.dao.DomainDao; -import eu.europa.ec.edelivery.smp.data.model.DBUser; +import eu.europa.ec.edelivery.smp.data.dao.UserDao; import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupRO; import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupValidationRO; import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; import eu.europa.ec.edelivery.smp.data.ui.auth.SMPAuthority; -import eu.europa.ec.edelivery.smp.data.ui.auth.SMPRole; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.edelivery.smp.services.ui.UIServiceGroupService; import eu.europa.ec.edelivery.smp.services.ui.filters.ServiceGroupFilter; import eu.europa.ec.edelivery.smp.ui.ResourceConstants; +import eu.europa.ec.edelivery.smp.utils.SessionSecurityUtils; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.annotation.Secured; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.util.MimeTypeUtils; import org.springframework.web.bind.annotation.*; @@ -33,22 +31,27 @@ import static eu.europa.ec.edelivery.smp.ui.ResourceConstants.*; * @author Joze Rihtarsic * @since 4.1 */ - @RestController @RequestMapping(value = ResourceConstants.CONTEXT_PATH_PUBLIC_SERVICE_GROUP) public class ServiceGroupResource { private static final SMPLogger LOG = SMPLoggerFactory.getLogger(ServiceGroupResource.class); - @Autowired - private UIServiceGroupService uiServiceGroupService; - @Autowired - private DomainDao domainDao; + final private UIServiceGroupService uiServiceGroupService; + final private DomainDao domainDao; + final private UserDao userDao; + final private SMPAuthorizationService authorizationService; + + public ServiceGroupResource(UIServiceGroupService uiServiceGroupService, DomainDao domainDao, UserDao userDao, SMPAuthorizationService authorizationService) { + this.uiServiceGroupService = uiServiceGroupService; + this.domainDao = domainDao; + this.userDao = userDao; + this.authorizationService = authorizationService; + } @GetMapping(produces = MimeTypeUtils.APPLICATION_JSON_VALUE) @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SMP_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SERVICE_GROUP_ADMIN}) public ServiceResult<ServiceGroupRO> getServiceGroupList( - HttpServletRequest request, @RequestParam(value = PARAM_PAGINATION_PAGE, defaultValue = "0") int page, @RequestParam(value = PARAM_PAGINATION_PAGE_SIZE, defaultValue = "10") int pageSize, @RequestParam(value = PARAM_PAGINATION_ORDER_BY, required = false) String orderBy, @@ -70,19 +73,17 @@ public class ServiceGroupResource { sgf.setDomain(domainDao.validateDomainCode(domainCodeDecoded)); // check if logged user is ServiceGroup admin if yes return only his servicegroups - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - // show all service groups only for SMP Admin // SMP admin can edit all service groups. For others return only services groups they own. - if (!request.isUserInRole(SMPRole.SMP_ADMIN.getCode())) { - SMPAuthenticationToken authToken = (SMPAuthenticationToken) authentication; - DBUser user = authToken.getUser(); - sgf.setOwner(user); + if (!authorizationService.isSMPAdministrator()) { + authorizationService.getAndValidateUserDetails(); + SMPUserDetails user = SessionSecurityUtils.getSessionUserDetails(); + sgf.setOwner(userDao.find(user.getUser().getId())); } return uiServiceGroupService.getTableList(page, pageSize, orderBy, orderType, sgf); } - @GetMapping( path = "{serviceGroupId}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE) + @GetMapping(path = "{serviceGroupId}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE) @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SMP_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SERVICE_GROUP_ADMIN}) public ServiceGroupRO getServiceGroupById(@PathVariable Long serviceGroupId) { LOG.info("Get service group [{}]", serviceGroupId); diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/TruststoreResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/TruststoreResource.java index 99c6290e5..dcea2481e 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/TruststoreResource.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/TruststoreResource.java @@ -1,29 +1,16 @@ package eu.europa.ec.edelivery.smp.ui.external; import eu.europa.ec.edelivery.smp.data.ui.CertificateRO; -import eu.europa.ec.edelivery.smp.data.ui.KeystoreImportResult; -import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; -import eu.europa.ec.edelivery.smp.data.ui.auth.SMPAuthority; -import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.edelivery.smp.services.PayloadValidatorService; import eu.europa.ec.edelivery.smp.services.ui.UITruststoreService; import eu.europa.ec.edelivery.smp.ui.ResourceConstants; -import eu.europa.ec.edelivery.smp.utils.X509CertificateUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.annotation.Secured; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.util.MimeTypeUtils; import org.springframework.web.bind.annotation.*; import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.List; /** * @author Joze Rihtarsic diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminResource.java index 59d138eb1..8c325dd3d 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminResource.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminResource.java @@ -1,5 +1,6 @@ package eu.europa.ec.edelivery.smp.ui.internal; +import eu.europa.ec.edelivery.security.utils.X509CertificateUtils; import eu.europa.ec.edelivery.smp.data.ui.auth.SMPAuthority; import eu.europa.ec.edelivery.smp.data.ui.CertificateRO; import eu.europa.ec.edelivery.smp.data.ui.KeystoreImportResult; @@ -10,7 +11,6 @@ import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.edelivery.smp.services.PayloadValidatorService; import eu.europa.ec.edelivery.smp.services.ui.UITruststoreService; import eu.europa.ec.edelivery.smp.ui.ResourceConstants; -import eu.europa.ec.edelivery.smp.utils.X509CertificateUtils; import org.springframework.security.access.annotation.Secured; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.util.MimeTypeUtils; @@ -73,7 +73,7 @@ public class TruststoreAdminResource { CertificateRO certificateRO=null; try { x509Certificate = X509CertificateUtils.getX509Certificate(fileBytes); - } catch (SMPRuntimeException e) { + } catch (SMPRuntimeException | CertificateException e) { LOG.error("Error occurred while parsing certificate.", e); return certificateRO; } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/UserAdminResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/UserAdminResource.java index 259c012a1..ed946f78d 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/UserAdminResource.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/UserAdminResource.java @@ -2,6 +2,7 @@ package eu.europa.ec.edelivery.smp.ui.internal; import eu.europa.ec.edelivery.smp.auth.SMPAuthenticationToken; import eu.europa.ec.edelivery.smp.auth.SMPAuthorizationService; +import eu.europa.ec.edelivery.smp.auth.SMPUserDetails; import eu.europa.ec.edelivery.smp.data.model.DBUser; import eu.europa.ec.edelivery.smp.data.ui.DeleteEntityValidation; import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; @@ -73,10 +74,10 @@ public class UserAdminResource { @PostMapping(value = "validate-delete", produces = MimeTypeUtils.APPLICATION_JSON_VALUE) @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN}) public DeleteEntityValidation validateDeleteUsers(@RequestBody List<String> queryEncIds) { - DBUser user = getCurrentUser(); + SMPUserDetails userDetails = getLoggedUserData(); List<Long> query = queryEncIds.stream().map(SessionSecurityUtils::decryptEntityId).collect(Collectors.toList()); DeleteEntityValidation dres = new DeleteEntityValidation(); - if (query.contains(user.getId())) { + if (query.contains(userDetails.getUser().getId())) { dres.setValidOperation(false); dres.setStringMessage("Could not delete logged user!"); return dres; @@ -85,9 +86,7 @@ public class UserAdminResource { return uiUserService.validateDeleteRequest(dres); } - private DBUser getCurrentUser() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - SMPAuthenticationToken authToken = (SMPAuthenticationToken) authentication; - return authToken.getUser(); + private SMPUserDetails getLoggedUserData() { + return authorizationService.getAndValidateUserDetails(); } } diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProviderTest.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProviderTest.java index 5ab349dfd..6761cdcda 100644 --- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProviderTest.java +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProviderTest.java @@ -9,6 +9,7 @@ import eu.europa.ec.edelivery.smp.services.ui.UITruststoreService; import org.hamcrest.Matchers; import org.junit.Test; import org.mockito.Mockito; +import org.springframework.core.convert.ConversionService; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -21,15 +22,21 @@ import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; +/** + * @author Joze Rihtarsic + * @since 4.2 + */ public class SMPAuthenticationProviderTest { UserDao mockUserDao = Mockito.mock(UserDao.class); + ConversionService mockConversionService = Mockito.mock(ConversionService.class); CRLVerifierService mockCrlVerifierService = Mockito.mock(CRLVerifierService.class); UITruststoreService mockTruststoreService = Mockito.mock(UITruststoreService.class); ConfigurationService mockConfigurationService = Mockito.mock(ConfigurationService.class); AlertService mocAlertService = Mockito.mock(AlertService.class); SMPAuthenticationProvider testInstance = new SMPAuthenticationProvider(mockUserDao, + mockConversionService, mockCrlVerifierService, mockTruststoreService, mockConfigurationService, @@ -44,9 +51,11 @@ public class SMPAuthenticationProviderTest { user.setAccessTokenIdentifier("User"); user.setAccessToken(BCrypt.hashpw("InvalidPassword", BCrypt.gensalt())); user.setRole("MY_ROLE"); + doReturn(500).when(mockConfigurationService).getAccessTokenLoginFailDelayInMilliSeconds(); + doReturn(Optional.of(user)).when(mockUserDao).findUserByIdentifier(any()); - int count = 100; + int count = 10; long averageExists = 0; long averageNotExist = 0; for (int i = 0; i < count; i++) { @@ -70,7 +79,7 @@ public class SMPAuthenticationProviderTest { // the average should be the same! assertThat("average difference between failed login must be less than 10ms", Math.abs(averageExists - averageNotExist), - Matchers.lessThan(1000L)); + Matchers.lessThan(20L)); } } \ No newline at end of file diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationServiceTest.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationServiceTest.java index 29a5eef11..ff29e08e4 100644 --- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationServiceTest.java +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationServiceTest.java @@ -1,44 +1,62 @@ package eu.europa.ec.edelivery.smp.auth; +import eu.europa.ec.edelivery.smp.data.dao.UserDao; import eu.europa.ec.edelivery.smp.data.model.DBUser; +import eu.europa.ec.edelivery.smp.data.ui.UserRO; import eu.europa.ec.edelivery.smp.data.ui.auth.SMPAuthority; +import eu.europa.ec.edelivery.smp.services.ConfigurationService; import eu.europa.ec.edelivery.smp.services.ServiceGroupService; import eu.europa.ec.edelivery.smp.utils.SessionSecurityUtils; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; import org.mockito.Mockito; +import org.springframework.core.convert.ConversionService; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; +import java.time.OffsetDateTime; import java.util.Collections; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; + public class SMPAuthorizationServiceTest { - DBUser user = null; + UserRO user = null; SecurityContext mockSecurityContextSystemAdmin = null; SecurityContext mockSecurityContextSMPAdmin = null; SecurityContext mockSecurityContextSGAdmin = null; ServiceGroupService serviceGroupService = Mockito.mock(ServiceGroupService.class); + ConversionService conversionService = Mockito.mock(ConversionService.class); + ConfigurationService configurationService = Mockito.mock(ConfigurationService.class); + UserDao userDao = Mockito.mock(UserDao.class); - SMPAuthorizationService testInstance = new SMPAuthorizationService(serviceGroupService); + SMPAuthorizationService testInstance = new SMPAuthorizationService(serviceGroupService, conversionService, + configurationService, userDao); @Before public void setup() { - user = new DBUser(); - user.setId((long) 10); - + user = new UserRO(); + SMPUserDetails sysUserDetails = new SMPUserDetails(new DBUser() {{ + setId(10L); + setUsername("sys_admin"); + }}, null, Collections.singletonList(SMPAuthority.S_AUTHORITY_SYSTEM_ADMIN)); + SMPUserDetails smpUserDetails = new SMPUserDetails(new DBUser() {{ + setUsername("smp_admin"); + }}, null, Collections.singletonList(SMPAuthority.S_AUTHORITY_SMP_ADMIN)); + SMPUserDetails sgUserDetails = new SMPUserDetails(new DBUser() {{ + setUsername("smp_admin"); + }}, null, Collections.singletonList(SMPAuthority.S_AUTHORITY_SERVICE_GROUP)); mockSecurityContextSystemAdmin = new SecurityContext() { - SMPAuthenticationToken smpa = new SMPAuthenticationToken("sys_admin", "test123", Collections.singletonList(SMPAuthority.S_AUTHORITY_SYSTEM_ADMIN), user); + SMPAuthenticationToken smpa = new SMPAuthenticationToken("sg_admin", "test123", sysUserDetails); @Override public Authentication getAuthentication() { @@ -50,7 +68,7 @@ public class SMPAuthorizationServiceTest { } }; mockSecurityContextSMPAdmin = new SecurityContext() { - SMPAuthenticationToken smpa = new SMPAuthenticationToken("smp_admin", "test123", Collections.singletonList(SMPAuthority.S_AUTHORITY_SMP_ADMIN), user); + SMPAuthenticationToken smpa = new SMPAuthenticationToken("smp_admin", "test123", smpUserDetails); @Override public Authentication getAuthentication() { @@ -62,7 +80,7 @@ public class SMPAuthorizationServiceTest { } }; mockSecurityContextSGAdmin = new SecurityContext() { - SMPAuthenticationToken smpa = new SMPAuthenticationToken("sg_admin", "test123", Collections.singletonList(SMPAuthority.S_AUTHORITY_SERVICE_GROUP), user); + SMPAuthenticationToken smpa = new SMPAuthenticationToken("sg_admin", "test123", sgUserDetails); @Override public Authentication getAuthentication() { @@ -128,4 +146,60 @@ public class SMPAuthorizationServiceTest { assertFalse(bVal); } + @Test + public void testGetUpdatedUserData() { + UserRO user = new UserRO(); + user.setPasswordExpireOn(OffsetDateTime.now().minusDays(1)); + Mockito.doReturn(10).when(configurationService).getPasswordPolicyUIWarningDaysBeforeExpire(); + Mockito.doReturn(false).when(configurationService).getPasswordPolicyForceChangeIfExpired(); + + user = testInstance.getUpdatedUserData(user); + + Assert.assertTrue(user.isShowPasswordExpirationWarning()); + Assert.assertFalse(user.isForceChangeExpiredPassword()); + Assert.assertFalse(user.isPasswordExpired()); + } + + @Test + public void testGetUpdatedUserDataDoNotShowWarning() { + UserRO user = new UserRO(); + user.setPasswordExpireOn(OffsetDateTime.now().minusDays(11)); + Mockito.doReturn(10).when(configurationService).getPasswordPolicyUIWarningDaysBeforeExpire(); + Mockito.doReturn(false).when(configurationService).getPasswordPolicyForceChangeIfExpired(); + + user = testInstance.getUpdatedUserData(user); + + Assert.assertFalse(user.isShowPasswordExpirationWarning()); + Assert.assertFalse(user.isForceChangeExpiredPassword()); + Assert.assertFalse(user.isPasswordExpired()); + } + + @Test + public void testGetUpdatedUserDataForceChange() { + UserRO user = new UserRO(); + user.setPasswordExpireOn(OffsetDateTime.now().plusDays(1)); + user.setPasswordExpired(true); + Mockito.doReturn(10).when(configurationService).getPasswordPolicyUIWarningDaysBeforeExpire(); + Mockito.doReturn(true).when(configurationService).getPasswordPolicyForceChangeIfExpired(); + + user = testInstance.getUpdatedUserData(user); + + Assert.assertTrue(user.isForceChangeExpiredPassword()); + Assert.assertTrue(user.isPasswordExpired()); + } + + @Test + public void testGetUpdatedUserDataForceChangeFalse() { + UserRO user = new UserRO(); + user.setPasswordExpireOn(OffsetDateTime.now().plusDays(1)); + user.setPasswordExpired(true); + Mockito.doReturn(10).when(configurationService).getPasswordPolicyUIWarningDaysBeforeExpire(); + Mockito.doReturn(false).when(configurationService).getPasswordPolicyForceChangeIfExpired(); + + user = testInstance.getUpdatedUserData(user); + + Assert.assertFalse(user.isForceChangeExpiredPassword()); + Assert.assertTrue(user.isPasswordExpired()); + } + } \ No newline at end of file diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/AuthenticationResourceTest.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/AuthenticationResourceTest.java index 122f1cd94..25a298825 100644 --- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/AuthenticationResourceTest.java +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/AuthenticationResourceTest.java @@ -1,7 +1,9 @@ package eu.europa.ec.edelivery.smp.ui; import eu.europa.ec.edelivery.smp.auth.SMPAuthenticationService; +import eu.europa.ec.edelivery.smp.auth.SMPAuthenticationToken; import eu.europa.ec.edelivery.smp.auth.SMPAuthorizationService; +import eu.europa.ec.edelivery.smp.auth.SMPUserDetails; import eu.europa.ec.edelivery.smp.data.ui.UserRO; import eu.europa.ec.edelivery.smp.services.ConfigurationService; import eu.europa.ec.edelivery.smp.services.ui.UIUserService; @@ -9,85 +11,84 @@ import eu.europa.ec.edelivery.smp.utils.SMPCookieWriter; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; -import org.springframework.core.convert.ConversionService; +import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.csrf.CsrfTokenRepository; +import org.springframework.web.servlet.view.RedirectView; -import java.time.OffsetDateTime; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import static eu.europa.ec.edelivery.smp.utils.SMPCookieWriter.SESSION_COOKIE_NAME; public class AuthenticationResourceTest { SMPAuthenticationService authenticationService = Mockito.mock(SMPAuthenticationService.class); SMPAuthorizationService authorizationService = Mockito.mock(SMPAuthorizationService.class); - ConversionService conversionService = Mockito.mock(ConversionService.class); ConfigurationService configurationService = Mockito.mock(ConfigurationService.class); SMPCookieWriter smpCookieWriter = Mockito.mock(SMPCookieWriter.class); CsrfTokenRepository csrfTokenRepository = Mockito.mock(CsrfTokenRepository.class); UIUserService uiUserService = Mockito.mock(UIUserService.class); - AuthenticationResource testInstance= new AuthenticationResource(authenticationService, + AuthenticationResource testInstance = new AuthenticationResource(authenticationService, authorizationService, - conversionService, configurationService, smpCookieWriter, csrfTokenRepository, uiUserService); + @Test - public void testGetUpdatedUserData() { - UserRO user = new UserRO(); - user.setPasswordExpireOn(OffsetDateTime.now().minusDays(1)); - Mockito.doReturn(10).when(configurationService).getPasswordPolicyUIWarningDaysBeforeExpire(); - Mockito.doReturn(false).when(configurationService).getPasswordPolicyForceChangeIfExpired(); - Mockito.doReturn(user).when(authorizationService).sanitize(Mockito.any()); - - user = testInstance.getUpdatedUserData(user); - - Assert.assertTrue(user.isShowPasswordExpirationWarning()); - Assert.assertFalse(user.isForceChangeExpiredPassword()); - Assert.assertFalse(user.isPasswordExpired()); + public void logout() { + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + HttpServletResponse response = Mockito.mock(HttpServletResponse.class); + Mockito.doNothing().when(authenticationService).logout(Mockito.any(), Mockito.any()); + testInstance.logout(request, response); + + Mockito.verify(authenticationService, Mockito.times(1)).logout(request, response); } @Test - public void testGetUpdatedUserDataDoNotShowWarning() { - UserRO user = new UserRO(); - user.setPasswordExpireOn(OffsetDateTime.now().minusDays(11)); - Mockito.doReturn(10).when(configurationService).getPasswordPolicyUIWarningDaysBeforeExpire(); - Mockito.doReturn(false).when(configurationService).getPasswordPolicyForceChangeIfExpired(); - Mockito.doReturn(user).when(authorizationService).sanitize(Mockito.any()); - - user = testInstance.getUpdatedUserData(user); - - Assert.assertFalse(user.isShowPasswordExpirationWarning()); - Assert.assertFalse(user.isForceChangeExpiredPassword()); - Assert.assertFalse(user.isPasswordExpired()); + public void authenticateCAS() { + + RedirectView result = testInstance.authenticateCAS(); + Assert.assertNotNull(result); + Assert.assertEquals("../../../#/", result.getUrl()); } @Test - public void testGetUpdatedUserDataForceChange() { - UserRO user = new UserRO(); - user.setPasswordExpireOn(OffsetDateTime.now().plusDays(1)); - user.setPasswordExpired(true); - Mockito.doReturn(10).when(configurationService).getPasswordPolicyUIWarningDaysBeforeExpire(); - Mockito.doReturn(true).when(configurationService).getPasswordPolicyForceChangeIfExpired(); - Mockito.doReturn(user).when(authorizationService).sanitize(Mockito.any()); - - user = testInstance.getUpdatedUserData(user); - - Assert.assertTrue(user.isForceChangeExpiredPassword()); - Assert.assertTrue(user.isPasswordExpired()); + public void getUser() { + UserRO user = new UserRO(); + Mockito.doReturn(user).when(authorizationService).getLoggedUserData(); + UserRO result = testInstance.getUser(); + Assert.assertEquals(user, result); } @Test - public void testGetUpdatedUserDataForceChangeFalse() { - UserRO user = new UserRO(); - user.setPasswordExpireOn(OffsetDateTime.now().plusDays(1)); - user.setPasswordExpired(true); - Mockito.doReturn(10).when(configurationService).getPasswordPolicyUIWarningDaysBeforeExpire(); - Mockito.doReturn(false).when(configurationService).getPasswordPolicyForceChangeIfExpired(); - Mockito.doReturn(user).when(authorizationService).sanitize(Mockito.any()); - - user = testInstance.getUpdatedUserData(user); - - Assert.assertFalse(user.isForceChangeExpiredPassword()); - Assert.assertTrue(user.isPasswordExpired()); + public void recreatedSessionCookie() { + String cookieName = SESSION_COOKIE_NAME; + String cookieValue = "CookieValue"; + boolean sessionCookieSecure = true; + String sessionCookiePath = "getSessionCookiePath"; + String sessionCookieSameSite = "getSessionCookieSameSite"; + Integer sessionCookieMaxAge = 12; + + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + HttpServletResponse response = Mockito.mock(HttpServletResponse.class); + HttpSession session = Mockito.mock(HttpSession.class); + Mockito.doReturn(session).when(request).getSession(Mockito.anyBoolean()); + Mockito.doReturn(cookieValue).when(session).getId(); + Mockito.doReturn(sessionCookieSecure).when(configurationService).getSessionCookieSecure(); + Mockito.doReturn(sessionCookieMaxAge).when(configurationService).getSessionCookieMaxAge(); + Mockito.doReturn(sessionCookiePath).when(configurationService).getSessionCookiePath(); + Mockito.doReturn(sessionCookieSameSite).when(configurationService).getSessionCookieSameSite(); + + + Mockito.doNothing().when(smpCookieWriter).writeCookieToResponse(cookieName, + cookieValue, sessionCookieSecure, sessionCookieMaxAge, sessionCookiePath, sessionCookieSameSite, request, response); + + testInstance.recreatedSessionCookie(request, response); + + Mockito.verify(smpCookieWriter, Mockito.times(1)).writeCookieToResponse(cookieName, + cookieValue, sessionCookieSecure, sessionCookieMaxAge, sessionCookiePath, sessionCookieSameSite, request, response); } } diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/external/UserResourceIntegrationTest.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/external/UserResourceIntegrationTest.java index ea4bfc715..e12901913 100644 --- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/external/UserResourceIntegrationTest.java +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/external/UserResourceIntegrationTest.java @@ -62,7 +62,6 @@ public class UserResourceIntegrationTest { @Test public void getUserList() throws Exception { - MockHttpSession session = loginWithSystemAdmin(mvc); MvcResult result = mvc.perform(get(CONTEXT_PATH_INTERNAL_USER) .session(session) diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminResourceIntegrationTest.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminResourceIntegrationTest.java index fb62cc4be..f6041f33e 100644 --- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminResourceIntegrationTest.java +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminResourceIntegrationTest.java @@ -58,9 +58,9 @@ public class TruststoreAdminResourceIntegrationTest { @Before public void setup() throws IOException { - X509CertificateTestUtils.reloadKeystores(); mvc = initializeMockMvc(webAppContext); uiTruststoreService.refreshData(); + X509CertificateTestUtils.reloadKeystores(); } @@ -78,8 +78,8 @@ public class TruststoreAdminResourceIntegrationTest { .session(session) .with(csrf()) .content(buff)) - .andExpect(status().is5xxServerError()) - .andExpect(content().string(CoreMatchers.containsString(" The certificate is not valid"))); + .andExpect(status().isOk()) + .andExpect(content().string(CoreMatchers.containsString("Can not read the certificate!"))); } @Test -- GitLab