Code development platform for open source projects from the European Union institutions :large_blue_circle: EU Login authentication by SMS will be completely phased out by mid-2025. To see alternatives please check here

Skip to content
Snippets Groups Projects
Commit 4abaf778 authored by Joze RIHTARSIC's avatar Joze RIHTARSIC
Browse files

Update SMPAuthenticationProvider to authenticate user and cert token

parent b593c956
No related branches found
No related tags found
No related merge requests found
......@@ -45,29 +45,26 @@ public class DBCertificate extends BaseEntity {
@Column(name = "VALID_TO")
@ColumnDescription(comment = "Certificate valid to date.")
private LocalDateTime validTo;
@Column(name = "SUBJECT", length = CommonColumnsLengths.MAX_MEDIUM_TEXT_LENGTH)
@ColumnDescription(comment = "Certificate subject (canonical form)" )
private String subject;
@ColumnDescription(comment = "Certificate subject (canonical form)")
private String subject;
@Column(name = "ISSUER", length = CommonColumnsLengths.MAX_MEDIUM_TEXT_LENGTH)
@ColumnDescription(comment = "Certificate issuer (canonical form)" )
private String issuer;
@ColumnDescription(comment = "Certificate issuer (canonical form)")
private String issuer;
@Column(name = "SERIALNUMBER", length = CommonColumnsLengths.MAX_TEXT_LENGTH_128)
@ColumnDescription(comment = "Certificate serial number" )
private String serialNumber;
@ColumnDescription(comment = "Certificate serial number")
private String serialNumber;
@Column(name = "pem_encoding")
@ColumnDescription(comment = "PEM encoding for the certificate")
@Column(name = "PEM_ENCODED_CERT")
@ColumnDescription(comment = "PEM encoded certificate")
@Lob
private String pemEncoding;
@Column(name = "crl_url", length = CommonColumnsLengths.MAX_FREE_TEXT_LENGTH)
@Column(name = "CRL_URL", length = CommonColumnsLengths.MAX_FREE_TEXT_LENGTH)
@ColumnDescription(comment = "URL to the certificate revocation list (CRL)")
private String crlUrl;
@Column(name = "CREATED_ON" , nullable = false)
@Column(name = "CREATED_ON", nullable = false)
LocalDateTime createdOn;
@Column(name = "LAST_UPDATED_ON", nullable = false)
LocalDateTime lastUpdatedOn;
......@@ -179,7 +176,7 @@ public class DBCertificate extends BaseEntity {
@PrePersist
public void prePersist() {
if(createdOn == null) {
if (createdOn == null) {
createdOn = LocalDateTime.now();
}
lastUpdatedOn = LocalDateTime.now();
......
package eu.europa.ec.edelivery.smp.auth;
import eu.europa.ec.edelivery.security.PreAuthenticatedCertificatePrincipal;
import eu.europa.ec.edelivery.smp.data.dao.UserDao;
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.logging.SMPMessageCode;
import org.springframework.beans.factory.annotation.Autowired;
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.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Optional;
import static java.util.Locale.US;
public class SMPAuthenticationProvider implements AuthenticationProvider {
private static final SMPLogger LOG = SMPLoggerFactory.getLogger(AuthenticationProvider.class);
/**
* thread safe validator
*/
private static final ThreadLocal<DateFormat> dateFormatLocal = ThreadLocal.withInitial( () -> {
return new SimpleDateFormat("MMM d hh:mm:ss yyyy zzz", US);
} );
@Autowired
UserDao mUserDao;
@Override
public Authentication authenticate(Authentication auth)
public Authentication authenticate(Authentication authenticationToken)
throws AuthenticationException {
Authentication authentication = null;
// PreAuthentication token for the rest service certificate authentication
if (authenticationToken instanceof PreAuthenticatedAuthenticationToken) {
Object principal = authenticationToken.getPrincipal();
if (principal instanceof PreAuthenticatedCertificatePrincipal) {
authentication = authenticateByCertificateToken((PreAuthenticatedCertificatePrincipal) principal);
} else {
LOG.warn("Unknown or null PreAuthenticatedAuthenticationToken principal type: " + principal);
}
} else if (authenticationToken instanceof UsernamePasswordAuthenticationToken) {
authentication = authenticateByUsernameToken((UsernamePasswordAuthenticationToken)authenticationToken);
}
// set anonymous token
if (authentication == null) {
authentication = new AnonymousAuthenticationToken(authenticationToken.toString(), authenticationToken.getPrincipal(),
Collections.singleton(SMPAuthority.S_AUTHORITY_ANONYMOUS));
authentication.setAuthenticated(false);
}
/*
if (principal instanceof PreAuthenticatedCertificatePrincipal) {
// get principal
LOG.info("Authenticate: PreAuthenticatedCertificatePrincipal");
authentication = authenticateCertificate((PreAuthenticatedCertificatePrincipal) principal);
} else if (principal instanceof PreAuthenticatedTokenPrincipal) {
authentication = authenticateSecurityToken((PreAuthenticatedTokenPrincipal) principal);
} else if (principal instanceof PreAuthenticatedAnonymousPrincipal) {
authentication = new UnsecureAuthentication();
authentication.setAuthenticated(configurationBusiness.isUnsecureLoginEnabled());
}
else {
// unknown principal type
authentication = new UnsecureAuthentication();
authentication.setAuthenticated(false);
}*/
return authentication;
}
/**
* Authenticate by certificate token got by BlueCoat or X509Certificate authentication)
* @param principal - certificate principal
* @return authentication value.
*/
public Authentication authenticateByCertificateToken(PreAuthenticatedCertificatePrincipal principal) {
mUserDao.findUserByCertificateId(principal.getName());
DBUser user;
String userToken = principal.getName();
try {
Optional<DBUser> oUsr = mUserDao.findUserByCertificateId(userToken);
if (!oUsr.isPresent()) {
LOG.securityWarn(SMPMessageCode.SEC_USER_NOT_EXISTS, userToken);
//https://www.owasp.org/index.php/Authentication_Cheat_Sheet
// Do not reveal the status of an existing account. Not to use UsernameNotFoundException
throw new BadCredentialsException("Login failed; Invalid userID or password");
}
user = oUsr.get();
} catch (AuthenticationException ex) {
throw ex;
} catch (RuntimeException ex) {
LOG.error("Database connection error", ex);
throw new AuthenticationServiceException("Internal server error occurred while user authentication!");
}
// check if certificate is valid
Date currentDate = Calendar.getInstance().getTime();
// validate dates
if (principal.getNotBefore().after(currentDate)) {
throw new AuthenticationServiceException("Invalid certificate: NotBefore: " + dateFormatLocal.get().format(principal.getNotBefore()));
} else if (principal.getNotAfter().before(currentDate)) {
throw new AuthenticationServiceException("Invalid certificate: NotAfter: " + dateFormatLocal.get().format(principal.getNotAfter()));
}
// check if issuer is on trust list.
// Check crl list
String url = user.getCertificate().getCrlUrl();
if (url!= null) {
}
// get role
String role = user.getRole();
LOG.securityInfo(SMPMessageCode.SEC_USER_AUTHENTICATED, userToken, role);
SMPCertificateAuthentication authentication = new SMPCertificateAuthentication(principal, Collections.singletonList(new SMPAuthority(role)), user);
authentication.setAuthenticated(true);
return authentication;
}
public Authentication authenticateByUsernameToken(UsernamePasswordAuthenticationToken auth)
throws AuthenticationException {
// get user
// test credentials
// get and return user roles.
String username = auth.getName();
String password = auth.getCredentials().toString();
......@@ -72,6 +192,12 @@ public class SMPAuthenticationProvider implements AuthenticationProvider {
@Override
public boolean supports(Class<?> auth) {
return auth.equals(UsernamePasswordAuthenticationToken.class) || auth.equals(SMPAuthenticationToken.class);
LOG.info("Support authentication: " + auth);
boolean supportAuthentication = auth.equals(UsernamePasswordAuthenticationToken.class) || auth.equals(PreAuthenticatedAuthenticationToken.class);
if (!supportAuthentication) {
LOG.warn("SMP does not support authentication type: " + auth);
}
return supportAuthentication;
}
}
......@@ -9,12 +9,13 @@ 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());
public static final SMPAuthority S_AUTHORITY_SMP_ADMIN = new SMPAuthority(SMPRole.SMP_ADMIN.getCode());
public static final SMPAuthority S_AUTHORITY_SERVICE_GROUP = new SMPAuthority(SMPRole.SERVICE_GROUP_ADMIN.getCode());
public static final SMPAuthority S_AUTHORITY_SYSTEM_ADMIN = new SMPAuthority(SMPRole.SYSTEM_ADMIN.getCode());
public static final SMPAuthority S_AUTHORITY_SMP_ADMIN = new SMPAuthority(SMPRole.SMP_ADMIN.getCode());
public static final SMPAuthority S_AUTHORITY_SERVICE_GROUP = new SMPAuthority(SMPRole.SERVICE_GROUP_ADMIN.getCode());
public static final SMPAuthority S_AUTHORITY_ANONYMOUS = new SMPAuthority(SMPRole.ANONYMOUS.getCode());
String role;
......@@ -24,6 +25,6 @@ public class SMPAuthority implements GrantedAuthority {
@Override
public String getAuthority() {
return "ROLE_"+role;
return "ROLE_" + role;
}
}
/**
* (C) Copyright 2018 - European Commission | CEF eDelivery
* <p>
* Licensed under the EUPL, Version 1.2 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* \BDMSL\bdmsl-parent-pom\LICENSE-EUPL-v1.2.pdf or https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl_v1.2_en.pdf
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package eu.europa.ec.edelivery.smp.auth;
import eu.europa.ec.edelivery.security.PreAuthenticatedCertificatePrincipal;
import eu.europa.ec.edelivery.smp.data.model.DBUser;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class SMPCertificateAuthentication implements Authentication {
PreAuthenticatedCertificatePrincipal principal;
DBUser dbUser;
List<GrantedAuthority> listAuthorities = new ArrayList<>();
boolean isAuthenticated;
private static final int SERIAL_PADDING_SIZE =16;
public SMPCertificateAuthentication(PreAuthenticatedCertificatePrincipal principal, List<GrantedAuthority> listAuthorities, DBUser user) {
this.principal = principal;
this.listAuthorities.addAll(listAuthorities);
this.dbUser = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return listAuthorities;
}
@Override
public Object getCredentials() {
return this.principal!=null?this.principal.getCredentials():null;
}
@Override
public Object getDetails() {
return this.principal;
}
@Override
public Object getPrincipal() {
return this.principal;
}
@Override
public boolean isAuthenticated() {
return isAuthenticated;
}
@Override
public void setAuthenticated(boolean b) throws IllegalArgumentException {
isAuthenticated = b;
}
@Override
public String getName() {
return principal.getName(SERIAL_PADDING_SIZE);
}
@Override
public String toString() {
return getName();
}
}
......@@ -2,22 +2,18 @@ package eu.europa.ec.edelivery.smp.auth;
public enum SMPRole {
ANONYMOUS("ANONYMOUS"),
SMP_ADMIN("SMP_ADMIN"),
SERVICE_GROUP_ADMIN("SERVICE_GROUP_ADMIN"),
SYSTEM_ADMIN("SYSTEM_ADMIN");
String code;
SMPRole(String code){
SMPRole(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}
......@@ -38,26 +38,9 @@
<authentication-manager alias="smpAuthenticationManager">
<authentication-provider ref="smpAuthProvider"/>
<authentication-provider ref="preauthAuthProvider"/>
</authentication-manager>
<!-- user detail service is used only in preAhtProviders for cert authentication that is why search is only on cert table-->
<!-- database Cert ID search must be case insensitive -->
<jdbc-user-service id="smpJdbcUserDetailsService"
data-source-ref="dataSource"
users-by-username-query="SELECT c.CERTIFICATE_ID AS USERNAME, 'dummy' AS PASWORD, u.ACTIVE FROM SMP_CERTIFICATE c INNER JOIN SMP_USER u ON (u.id = c.id) WHERE lower(c.CERTIFICATE_ID) = lower(?)"
authorities-by-username-query="SELECT c.CERTIFICATE_ID AS USERNAME, u.ROLE FROM SMP_CERTIFICATE c INNER JOIN SMP_USER u ON (u.id = c.id) WHERE lower(c.CERTIFICATE_ID) = lower(?)"/>
<b:bean id="preauthAuthProvider"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<b:property name="preAuthenticatedUserDetailsService">
<b:bean id="userDetailsServiceWrapper"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<b:property name="userDetailsService" ref="smpJdbcUserDetailsService"/>
</b:bean>
</b:property>
</b:bean>
<b:bean id="smpAuthProvider" class="eu.europa.ec.edelivery.smp.auth.SMPAuthenticationProvider" />
<b:bean id="blueCoatReverseProxyAuthFilter"
class="eu.europa.ec.edelivery.security.BlueCoatAuthenticationFilter">
......@@ -70,15 +53,13 @@
<b:property name="authenticationManager" ref="smpAuthenticationManager"/>
</b:bean>
<!-- Slashes in participant or document identifiers are disallowed by default -->
<!-- encoded Slashes are disallowed by default but SMP is using
them in participant or document identifiers -->
<http-firewall ref="httpFirewall"/>
<b:bean id="httpFirewall" class="org.springframework.security.web.firewall.DefaultHttpFirewall">
<b:property name="allowUrlEncodedSlash" value="${encodedSlashesAllowedInUrl}"/>
</b:bean>
<b:bean id="smpAuthProvider" class="eu.europa.ec.edelivery.smp.auth.SMPAuthenticationProvider">
</b:bean>
</b:beans>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment