Skip to content
This repository has been archived by the owner on Aug 10, 2021. It is now read-only.

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
priitr committed Jul 8, 2020
2 parents 27029b8 + d041240 commit 526b536
Show file tree
Hide file tree
Showing 22 changed files with 270 additions and 54 deletions.
16 changes: 8 additions & 8 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<groupId>ee.ria.tara</groupId>
<artifactId>tara-server</artifactId>
<packaging>war</packaging>
<version>1.5.0</version>
<version>1.5.1</version>

<properties>
<cas.version>5.3.15.1</cas.version>
Expand Down Expand Up @@ -42,7 +42,7 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.5.22.RELEASE</version>
<version>2.3.1.RELEASE</version>
<configuration>
<mainClass>org.springframework.boot.loader.WarLauncher</mainClass>
<addResources>true</addResources>
Expand Down Expand Up @@ -79,7 +79,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.3</version>
<version>3.3.0</version>
<configuration>
<warName>cas</warName>
<failOnMissingWebXml>false</failOnMissingWebXml>
Expand Down Expand Up @@ -138,14 +138,14 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.10.4</version>
<version>2.9.10.5</version>
</dependency>

<!-- Override dependency needed for mid-rest-java-client -->
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>2.28</version>
<version>2.31</version>
</dependency>

<dependency>
Expand Down Expand Up @@ -299,7 +299,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.8</version>
<version>42.2.14</version>
</dependency>

<!-- cas cluster support -->
Expand Down Expand Up @@ -354,7 +354,7 @@
<dependency>
<groupId>ee.sk.smartid</groupId>
<artifactId>smart-id-java-client</artifactId>
<version>1.5</version>
<version>1.6</version>
</dependency>

<!-- test -->
Expand All @@ -372,7 +372,7 @@
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-standalone</artifactId>
<version>2.25.1</version>
<version>2.26.3</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down
1 change: 1 addition & 0 deletions src/main/java/ee/ria/sso/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public final class Constants {
public static final String TARA_OIDC_SESSION_REDIRECT_URI = "taraOidcSessionRedirectUri";
public static final String TARA_OIDC_SESSION_AUTH_METHODS = "taraOidcSessionAllowedAuthMethods";
public static final String TARA_OIDC_SESSION_LOA = "taraOidcSessionLoA";
public static final String TARA_OIDC_SESSION_STATE = "taraOidcSessionState";

public static final String TARA_OIDC_DYNAMIC_CLIENT_REGISTRATION_ENDPOINT_ENABLED = "oidc.dynamic-client-registration.enabled";
public static final String TARA_OIDC_PROFILE_ENDPOINT_ENABLED = "oidc.profile-endpoint.enabled";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import ee.ria.sso.config.TaraProperties;
import ee.ria.sso.service.eidas.EidasCredential;
import ee.ria.sso.service.idcard.IdCardCredential;
import ee.ria.sso.service.mobileid.MobileIDCredential;
import ee.ria.sso.utils.EstonianIdCodeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apereo.cas.authentication.AuthenticationHandlerExecutionResult;
Expand Down Expand Up @@ -50,6 +51,9 @@ protected AuthenticationHandlerExecutionResult doAuthentication(Credential crede
} else if (credential instanceof EidasCredential) {
principalAttributes.put(DATE_OF_BIRTH.name(), ((EidasCredential)taraCredential).getDateOfBirth());
principalAttributes.put(ACR.name(),((EidasCredential)taraCredential).getLevelOfAssurance().getAcrName());
} else if (credential instanceof MobileIDCredential && ((MobileIDCredential)taraCredential).getPhoneNumber() != null) {
principalAttributes.put(PHONE_NUMBER.name(), ((MobileIDCredential)taraCredential).getPhoneNumber());
principalAttributes.put(PHONE_NUMBER_VERIFIED.name(), ((MobileIDCredential)taraCredential).getPhoneNumberVerified());
}

return this.createHandlerResult(credential, this.principalFactory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public enum Attribute {
AMR,
EMAIL,
EMAIL_VERIFIED,
PHONE_NUMBER,
PHONE_NUMBER_VERIFIED,
ACR;
}

Expand Down
42 changes: 36 additions & 6 deletions src/main/java/ee/ria/sso/flow/ThymeleafSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import ee.ria.sso.config.TaraProperties;
import ee.ria.sso.oidc.TaraScope;
import ee.ria.sso.service.manager.ManagerService;
import ee.ria.sso.utils.RedirectUrlUtil;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.utils.URIBuilder;
Expand All @@ -19,10 +21,13 @@
import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import static java.nio.charset.StandardCharsets.UTF_8;

@Slf4j
@AllArgsConstructor
public class ThymeleafSupport {
Expand Down Expand Up @@ -91,17 +96,28 @@ public String getBackUrl(String url, Locale locale) throws URISyntaxException {
}

public String getHomeUrl() {
final Object redirectUri = RequestContextHolder.getRequestContext()
final String redirectUri = RequestContextHolder.getRequestContext()
.getExternalContext()
.getSessionMap()
.getString(Constants.TARA_OIDC_SESSION_REDIRECT_URI);

final String clientId = RequestContextHolder.getRequestContext()
.getExternalContext()
.getSessionMap()
.get(Constants.TARA_OIDC_SESSION_REDIRECT_URI);
.getString(Constants.TARA_OIDC_SESSION_CLIENT_ID);

String informationUrl = getHomeUrl(clientId);

if (StringUtils.isNotBlank(informationUrl)) {
return informationUrl;
}

return getHomeUrl(redirectUri == null ? null : (String) redirectUri);
return getUserCancelUrl(redirectUri);
}

public String getHomeUrl(String redirectUri) {
if (StringUtils.isNotBlank(redirectUri)) {
return this.managerService.getServiceByID(redirectUri)
public String getHomeUrl(String clientId) {
if (StringUtils.isNotBlank(clientId)) {
return this.managerService.getServiceByName(clientId)
.orElse(new OidcRegisteredService() {
@Override
public String getInformationUrl() {
Expand All @@ -115,6 +131,20 @@ public String getInformationUrl() {
}
}

public String getUserCancelUrl(String redirectUri) {
final String sessionState = RequestContextHolder.getRequestContext()
.getExternalContext()
.getSessionMap()
.getString(Constants.TARA_OIDC_SESSION_STATE);

return getUserCancelUrl(redirectUri, sessionState);
}

@SneakyThrows
public String getUserCancelUrl(String redirectUri, String sessionState) {
return RedirectUrlUtil.createRedirectUrl(redirectUri, "user_cancel", "User canceled the login process", sessionState);
}

public String getCurrentRequestIdentifier(HttpServletRequest request) {
try {
return (String)request.getAttribute(Constants.MDC_ATTRIBUTE_REQUEST_ID);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import ee.ria.sso.authentication.LevelOfAssurance;
import ee.ria.sso.config.TaraProperties;
import ee.ria.sso.config.eidas.EidasConfigurationProvider;
import ee.ria.sso.utils.RedirectUrlUtil;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -78,6 +79,9 @@ private void saveOidcRequestParametersToSession(final HttpServletRequest request
session.setAttribute(Constants.TARA_OIDC_SESSION_REDIRECT_URI,
request.getParameter(OidcAuthorizeRequestParameter.REDIRECT_URI.getParameterKey())
);
session.setAttribute(Constants.TARA_OIDC_SESSION_STATE,
request.getParameter(OidcAuthorizeRequestParameter.STATE.getParameterKey()));

LevelOfAssurance requestedLoa = getLevelOfAssurance(request);
if (requestedLoa != null) {
session.setAttribute(Constants.TARA_OIDC_SESSION_LOA, requestedLoa);
Expand All @@ -98,17 +102,9 @@ private String getRedirectUrlToRelyingParty(HttpServletRequest request, OidcAuth
String redirectUri = request.getParameter(OidcAuthorizeRequestParameter.REDIRECT_URI.getParameterKey());
Assert.notNull(redirectUri, "redirect_uri is required");

StringBuilder sb = new StringBuilder();
sb.append(redirectUri);
sb.append(redirectUri.contains("?") ? "&" : "?");
sb.append(String.format("error=%s", URLEncoder.encode(e.getErrorCode(), UTF_8.name())));
sb.append(String.format("&error_description=%s", URLEncoder.encode(e.getErrorDescription(), UTF_8.name())));
String state = request.getParameter(OidcAuthorizeRequestParameter.STATE.getParameterKey());
if (StringUtils.isNotBlank(state)) {
sb.append(String.format("&state=%s", URLEncoder.encode(state, UTF_8.name())));
}

return sb.toString();
return RedirectUrlUtil.createRedirectUrl(redirectUri, e.getErrorCode(), e.getErrorDescription(), state);
}

private List<AuthenticationType> getAllowedAuthenticationTypes(List<TaraScope> taraScopes, LevelOfAssurance requestedLoa) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public class TaraOidcIdTokenGeneratorService extends OidcIdTokenGeneratorService
public static final String CLAIM_PROFILE_ATTRIBUTES = "profile_attributes";
public static final String CLAIM_EMAIL = "email";
public static final String CLAIM_EMAIL_VERIFIED = "email_verified";
public static final String CLAIM_PHONE_NUMBER = "phone_number";
public static final String CLAIM_PHONE_NUMBER_VERIFIED = "phone_number_verified";

public static final List<TaraPrincipal.Attribute> validProfileAttributesToClaimsList = Collections.unmodifiableList(Arrays.asList(
FAMILY_NAME, GIVEN_NAME, DATE_OF_BIRTH
Expand Down Expand Up @@ -129,6 +131,11 @@ private void setTaraClaims(AccessToken accessToken, JwtClaims claims) {
claims.setClaim(CLAIM_EMAIL_VERIFIED, getAttributeValue(EMAIL_VERIFIED, taraPrincipal, Boolean.class));
}

if (taraPrincipal.getAttributes().containsKey(PHONE_NUMBER.name()) && taraPrincipal.getAttributes().containsKey(PHONE_NUMBER_VERIFIED.name())) {
claims.setStringClaim(CLAIM_PHONE_NUMBER, getAttributeValue(PHONE_NUMBER, taraPrincipal));
claims.setClaim(CLAIM_PHONE_NUMBER_VERIFIED, getAttributeValue(PHONE_NUMBER_VERIFIED, taraPrincipal, Boolean.class));
}

claims.setClaim(CLAIM_PROFILE_ATTRIBUTES, getProfileAttributesMap(taraPrincipal));
claims.setStringListClaim(OidcConstants.AMR, getAttributeValue(AMR, taraPrincipal, List.class));

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/ee/ria/sso/oidc/TaraScope.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public enum TaraScope {
BANKLINK("banklink"),
SMARTID("smartid"),
EIDASONLY("eidasonly"),
EMAIL("email");
EMAIL("email"),
PHONE("phone");

public static final List<TaraScope> SUPPORTS_AUTHENTICATION_METHOD_SELECTION = Collections.unmodifiableList(Arrays.asList(IDCARD, MID, BANKLINK, EIDAS, SMARTID, EIDASONLY));

Expand Down
6 changes: 3 additions & 3 deletions src/main/java/ee/ria/sso/service/manager/ManagerService.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package ee.ria.sso.service.manager;

import java.util.Optional;

import org.apereo.cas.services.OidcRegisteredService;

import java.util.Optional;

/**
* Created by Janar Rahumeel (CGI Estonia)
*/

public interface ManagerService {

Optional<OidcRegisteredService> getServiceByID(String serviceID);
Optional<OidcRegisteredService> getServiceByName(String serviceName);

}
25 changes: 22 additions & 3 deletions src/main/java/ee/ria/sso/service/manager/ManagerServiceImpl.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package ee.ria.sso.service.manager;

import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apereo.cas.services.OidcRegisteredService;
import org.apereo.cas.services.RegisteredService;
import org.apereo.cas.services.ServicesManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -26,11 +32,22 @@ public ManagerServiceImpl(ServicesManager servicesManager) {
}

@Override
public Optional<OidcRegisteredService> getServiceByID(String serviceID) {
this.log.debug("Searching OIDC service by <{}>", serviceID);
public Optional<OidcRegisteredService> getServiceByName(String serviceName) {
this.log.debug("Searching OIDC service by <{}>", serviceName);
Optional<OidcRegisteredService> service;
try {
service = Optional.ofNullable(this.servicesManager.findServiceBy(serviceID, OidcRegisteredService.class));
Collection<RegisteredService> allRegisteredServices = this.servicesManager.getAllServices();
List<OidcRegisteredService> services = allRegisteredServices.stream()
.filter(r -> r instanceof OidcRegisteredService)
.filter(i -> ((OidcRegisteredService) i).getClientId().equals(serviceName))
.map(s -> (OidcRegisteredService) s)
.collect(Collectors.toList());

if (services.size() != 1) {
throw new IllegalArgumentException("Duplicate OIDC Client ID");
}

service = Optional.ofNullable(services.get(0));
} catch (RuntimeException e) {
this.log.error("Internal CAS error", e);
service = Optional.empty();
Expand All @@ -39,4 +56,6 @@ public Optional<OidcRegisteredService> getServiceByID(String serviceID) {
return service;
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import ee.ria.sso.authentication.credential.PreAuthenticationCredential;
import ee.ria.sso.authentication.credential.TaraCredential;
import ee.ria.sso.config.mobileid.MobileIDConfigurationProvider;
import ee.ria.sso.oidc.TaraScope;
import ee.ria.sso.service.AbstractService;
import ee.ria.sso.service.UserAuthenticationFailedException;
import ee.ria.sso.service.mobileid.rest.MobileIDErrorMessage;
Expand All @@ -19,6 +20,8 @@
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;

import java.util.List;

import static ee.ria.sso.statistics.StatisticsOperation.START_AUTH;
import static ee.ria.sso.statistics.StatisticsOperation.SUCCESSFUL_AUTH;
import static org.springframework.util.Assert.notNull;
Expand Down Expand Up @@ -48,7 +51,7 @@ public Event startLoginByMobileID(RequestContext context) {
final PreAuthenticationCredential credential = context.getFlowExecutionContext().getActiveSession().getScope().get("credential", PreAuthenticationCredential.class);
notNull(credential, "PreAuthenticationCredential is missing!");

String mobileNumber = StringUtils.isBlank(credential.getMobileNumber()) ? credential.getMobileNumber() : confProvider.getAreaCode() + credential.getMobileNumber();
String mobileNumber = getPhoneNumber(credential);
log.info("Starting Mobile-ID authentication: <mobileNumber:{}>, <identityCode:{}>", mobileNumber, credential.getPrincipalCode());
try {
logEvent(context, AuthenticationType.MobileID, START_AUTH);
Expand Down Expand Up @@ -112,7 +115,7 @@ public Event checkLoginForMobileID(RequestContext context) {
log.info("Mobile-ID authentication complete <sessionId:{}>", session.getSessionId());
AuthenticationIdentity authIdentity = authenticationClient.getAuthenticationIdentity(session, sessionStatus);
context.getFlowExecutionContext().getActiveSession().getScope()
.put(CasWebflowConstants.VAR_ID_CREDENTIAL, constructTaraCredential(authIdentity));
.put(CasWebflowConstants.VAR_ID_CREDENTIAL, constructTaraCredential(authIdentity, context));
logEvent(context, AuthenticationType.MobileID, SUCCESSFUL_AUTH);
return new Event(this, CasWebflowConstants.TRANSITION_ID_SUCCESS);
} else {
Expand All @@ -126,8 +129,19 @@ public Event checkLoginForMobileID(RequestContext context) {
}
}

private TaraCredential constructTaraCredential(AuthenticationIdentity authIdentity) {
return new TaraCredential(AuthenticationType.MobileID,
private TaraCredential constructTaraCredential(AuthenticationIdentity authIdentity, RequestContext context) {
if (isPhoneNumberRequested(context)) {
final PreAuthenticationCredential credential = context.getFlowExecutionContext().getActiveSession().getScope().get("credential", PreAuthenticationCredential.class);
String mobileNumber = getPhoneNumber(credential);

return new MobileIDCredential(
confProvider.getCountryCode() + authIdentity.getIdentityCode(),
authIdentity.getGivenName(),
authIdentity.getSurname(),
mobileNumber);
}

return new MobileIDCredential(
confProvider.getCountryCode() + authIdentity.getIdentityCode(),
authIdentity.getGivenName(),
authIdentity.getSurname());
Expand All @@ -142,6 +156,15 @@ private void validateCredential(String principalCode, String mobileNumber) {
}
}

private String getPhoneNumber(PreAuthenticationCredential credential) {
return StringUtils.isBlank(credential.getMobileNumber()) ? credential.getMobileNumber() : confProvider.getAreaCode() + credential.getMobileNumber();
}

private boolean isPhoneNumberRequested(RequestContext context) {
List<TaraScope> scopes = context.getExternalContext().getSessionMap().get(Constants.TARA_OIDC_SESSION_SCOPES, List.class, null);
return scopes != null && scopes.contains(TaraScope.PHONE);
}

private void logEvent(RequestContext context, Exception e) {
logEvent(context, e, AuthenticationType.MobileID);
}
Expand Down
Loading

0 comments on commit 526b536

Please sign in to comment.