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

Commit bb738d5

Browse files
committed
Merge branch 'develop'
2 parents e970408 + 3362dcb commit bb738d5

18 files changed

+706
-157
lines changed

doc/Configuration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ Table 20 - Relevant parameters for enabling/disabling additional OpenID Connect
620620
| Property | Mandatory | Description |
621621
| :---------------- | :---------- | :----------------|
622622
| `oidc.dynamic-client-registration.enabled` | N | Enables/disables the CAS built-in /oidc/registration endpoint and displays/hides the related information at the /oidc/.well-known/openid-configuration. Defaults to `false`, if not specified. |
623-
| `oidc.profile-endpoint.enabled` | N | Enables/disables the CAS built-in /oidc/profile endpoint and displays/hides the related information at the /oidc/.well-known/openid-configuration. Defaults to `false`, if not specified. |
623+
| `oidc.profile-endpoint.enabled` | N | Enables/disables the CAS built-in /oidc/profile endpoint and displays/hides the related information at the /oidc/.well-known/openid-configuration. Defaults to `true`, if not specified. |
624624
| `oidc.revocation-endpoint.enabled` | N | Enables/disables the CAS built-in /oidc/revocation endpoint and displays/hides the related information at the /oidc/.well-known/openid-configuration. Defaults to `false`, if not specified. |
625625
| `oidc.introspection-endpoint.enabled` | N | Enables/disables the CAS built-in /oidc/introspection endpoint and displays/hides the related information at the /oidc/.well-known/openid-configuration. Defaults to `false`, if not specified. |
626626

pom.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<groupId>ee.ria.tara</groupId>
77
<artifactId>tara-server</artifactId>
88
<packaging>war</packaging>
9-
<version>1.4.1</version>
9+
<version>1.4.2</version>
1010

1111
<properties>
1212
<cas.version>5.3.7</cas.version>
@@ -401,6 +401,12 @@
401401
<version>1.4.0</version>
402402
<scope>test</scope>
403403
</dependency>
404+
<dependency>
405+
<groupId>org.apereo.cas</groupId>
406+
<artifactId>cas-server-core-cookie</artifactId>
407+
<version>${cas.version}</version>
408+
<scope>test</scope>
409+
</dependency>
404410

405411
</dependencies>
406412

src/main/java/ee/ria/sso/authentication/TaraAuthenticationHandler.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@
1313
import org.springframework.util.Assert;
1414

1515
import java.security.GeneralSecurityException;
16-
import java.util.ArrayList;
17-
import java.util.LinkedHashMap;
18-
import java.util.Map;
16+
import java.util.*;
1917

2018
import static ee.ria.sso.authentication.principal.TaraPrincipal.Attribute.*;
2119

@@ -42,7 +40,7 @@ protected AuthenticationHandlerExecutionResult doAuthentication(Credential crede
4240
principalAttributes.put(EMAIL_VERIFIED.name(), ((IdCardCredential)taraCredential).getEmailVerified());
4341
} else if (credential instanceof EidasCredential) {
4442
principalAttributes.put(DATE_OF_BIRTH.name(), ((EidasCredential)taraCredential).getDateOfBirth());
45-
principalAttributes.put(LEVEL_OF_ASSURANCE.name(), ((EidasCredential)taraCredential).getLevelOfAssurance().getAcrName());
43+
principalAttributes.put(ACR.name(),((EidasCredential)taraCredential).getLevelOfAssurance().getAcrName());
4644
}
4745

4846
return this.createHandlerResult(credential, this.principalFactory
@@ -60,10 +58,10 @@ private Map<String, Object> getMandatoryPrincipalParameters(TaraCredential taraC
6058
}, "Cannot authenticate without missing mandatory credential parameters! Provided credential: " + taraCredential);
6159

6260
final Map<String, Object> map = new LinkedHashMap<>();
63-
map.put(PRINCIPAL_CODE.name(), taraCredential.getPrincipalCode());
61+
map.put(SUB.name(), taraCredential.getPrincipalCode());
6462
map.put(GIVEN_NAME.name(), taraCredential.getFirstName());
6563
map.put(FAMILY_NAME.name(), taraCredential.getLastName());
66-
map.put(AUTHENTICATION_TYPE.name(), taraCredential.getType().getAmrName());
64+
map.put(AMR.name(), Arrays.asList(Arrays.asList(taraCredential.getType().getAmrName())));
6765

6866
if (EstonianIdCodeUtil.isEEPrefixedEstonianIdCode(taraCredential.getPrincipalCode())) {
6967
map.put(DATE_OF_BIRTH.name(), EstonianIdCodeUtil.extractDateOfBirthFromEEPrefixedEstonianIdCode(taraCredential.getPrincipalCode()));

src/main/java/ee/ria/sso/authentication/principal/TaraPrincipal.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
public class TaraPrincipal implements Principal {
1313

1414
public enum Attribute {
15-
PRINCIPAL_CODE,
15+
SUB,
1616
GIVEN_NAME,
1717
FAMILY_NAME,
18-
AUTHENTICATION_TYPE,
1918
DATE_OF_BIRTH,
19+
AMR,
2020
EMAIL,
2121
EMAIL_VERIFIED,
22-
LEVEL_OF_ASSURANCE;
22+
ACR;
2323
}
2424

2525
private String id;

src/main/java/ee/ria/sso/authentication/principal/TaraPrincipalFactory.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
import lombok.NoArgsConstructor;
55
import org.apereo.cas.authentication.principal.Principal;
66
import org.apereo.cas.authentication.principal.PrincipalFactory;
7+
import org.apereo.cas.ticket.TicketGrantingTicket;
78
import org.springframework.util.Assert;
89

10+
import java.util.List;
911
import java.util.Map;
12+
import java.util.stream.Collectors;
1013

1114
@NoArgsConstructor
1215
@EqualsAndHashCode
@@ -23,4 +26,24 @@ public Principal createPrincipal(String id, Map<String, Object> attributes) {
2326
Assert.notEmpty(attributes, "No attributes found when creating principal");
2427
return new TaraPrincipal(id, attributes);
2528
}
29+
30+
public static Principal createPrincipal(TicketGrantingTicket tgt) {
31+
Assert.notNull(tgt, "TGT cannot be null!");
32+
final Principal principal = tgt.getAuthentication().getPrincipal();
33+
Assert.notNull(tgt, "No authentication associated with TGT!");
34+
35+
Map<String, Object> attributes = principal.getAttributes()
36+
.entrySet()
37+
.stream()
38+
.filter(
39+
entry -> entry.getValue() instanceof List
40+
&& !((List)entry.getValue()).isEmpty()
41+
).collect(
42+
Collectors.toMap(
43+
entry -> entry.getKey().toLowerCase(),
44+
entry -> ((List)entry.getValue()).stream().findFirst().get()
45+
)
46+
);
47+
return new TaraPrincipal(principal.getId(), attributes);
48+
}
2649
}

src/main/java/ee/ria/sso/config/TaraOidcConfiguration.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.apereo.cas.support.oauth.web.response.accesstoken.ext.BaseAccessTokenGrantRequestExtractor;
3333
import org.apereo.cas.support.oauth.web.response.callback.OAuth20AuthorizationResponseBuilder;
3434
import org.apereo.cas.support.oauth.web.views.ConsentApprovalViewResolver;
35+
import org.apereo.cas.support.oauth.web.views.OAuth20UserProfileViewRenderer;
3536
import org.apereo.cas.ticket.ExpirationPolicy;
3637
import org.apereo.cas.ticket.UniqueTicketIdGenerator;
3738
import org.apereo.cas.ticket.accesstoken.AccessTokenFactory;

src/main/java/ee/ria/sso/flow/AbstractFlowExecutionException.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
public abstract class AbstractFlowExecutionException extends ActionExecutionException {
1414

15-
protected final ModelAndView modelAndView;
15+
protected final transient ModelAndView modelAndView;
1616

1717
public AbstractFlowExecutionException(String flowId, ModelAndView modelAndView, Exception e) {
1818
super(flowId, null, null, new LocalAttributeMap(), e);

src/main/java/ee/ria/sso/oidc/TaraOidcIdTokenGeneratorService.java

Lines changed: 27 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package ee.ria.sso.oidc;
22

3-
import ee.ria.sso.authentication.AuthenticationType;
43
import ee.ria.sso.authentication.principal.TaraPrincipal;
4+
import ee.ria.sso.authentication.principal.TaraPrincipalFactory;
55
import lombok.Getter;
66
import lombok.extern.slf4j.Slf4j;
77
import org.apache.commons.codec.digest.MessageDigestAlgorithms;
@@ -18,7 +18,6 @@
1818
import org.apereo.cas.support.oauth.OAuth20ResponseTypes;
1919
import org.apereo.cas.support.oauth.services.OAuthRegisteredService;
2020
import org.apereo.cas.ticket.accesstoken.AccessToken;
21-
import org.apereo.cas.util.CollectionUtils;
2221
import org.apereo.cas.util.DigestUtils;
2322
import org.apereo.cas.util.EncodingUtils;
2423
import org.apereo.cas.util.Pac4jUtils;
@@ -33,7 +32,6 @@
3332
import javax.servlet.http.HttpServletRequest;
3433
import javax.servlet.http.HttpServletResponse;
3534
import java.util.*;
36-
import java.util.stream.Collectors;
3735

3836
import static ee.ria.sso.authentication.principal.TaraPrincipal.Attribute.*;
3937

@@ -47,9 +45,9 @@ public class TaraOidcIdTokenGeneratorService extends OidcIdTokenGeneratorService
4745
public static final String CLAIM_EMAIL = "email";
4846
public static final String CLAIM_EMAIL_VERIFIED = "email_verified";
4947

50-
private static final List<TaraPrincipal.Attribute> validProfileAttributesToClaimsList = Arrays.asList(
48+
public static final List<TaraPrincipal.Attribute> validProfileAttributesToClaimsList = Collections.unmodifiableList(Arrays.asList(
5149
FAMILY_NAME, GIVEN_NAME, DATE_OF_BIRTH
52-
);
50+
));
5351

5452
public TaraOidcIdTokenGeneratorService(final CasConfigurationProperties casProperties,
5553
final OidcIdTokenSigningAndEncryptionService signingService,
@@ -105,10 +103,7 @@ protected JwtClaims produceIdTokenClaims(final HttpServletRequest request,
105103
claims.setJwtId(UUID.randomUUID().toString());
106104
claims.setIssuer(oidc.getIssuer());
107105
claims.setAudience(service.getClientId());
108-
109-
final NumericDate expirationDate = NumericDate.now();
110-
expirationDate.addSeconds(timeoutInSeconds);
111-
claims.setExpirationTime(expirationDate);
106+
claims.setExpirationTime( getExpirationDate(timeoutInSeconds));
112107
claims.setIssuedAtToNow();
113108
claims.setNotBeforeMinutesInThePast(oidc.getSkew());
114109

@@ -117,59 +112,43 @@ protected JwtClaims produceIdTokenClaims(final HttpServletRequest request,
117112
claims.setClaim(OAuth20Constants.STATE, authentication.getAttributes().get(OAuth20Constants.STATE));
118113
claims.setClaim(OAuth20Constants.NONCE, authentication.getAttributes().get(OAuth20Constants.NONCE));
119114
claims.setClaim(OidcConstants.CLAIM_AT_HASH, generateAccessTokenHash(accessTokenId));
120-
121115
return claims;
122116
}
123117

124-
private void setTaraClaims(AccessToken accessTokenId, JwtClaims claims) {
125-
Assert.notNull(accessTokenId.getTicketGrantingTicket(), "No TGT associated with this access token!");
126-
Assert.notNull(accessTokenId.getTicketGrantingTicket().getAuthentication(), "No authentication associated with this TGT!");
127118

128-
Principal taraPrincipal = accessTokenId.getTicketGrantingTicket().getAuthentication().getPrincipal();
129-
claims.setSubject(getMandatoryPrincipalAttribute(PRINCIPAL_CODE, taraPrincipal));
119+
private void setTaraClaims(AccessToken accessToken, JwtClaims claims) {
120+
Assert.notNull(accessToken.getTicketGrantingTicket(), "No TGT associated with this access token!");
121+
Assert.notNull(accessToken.getTicketGrantingTicket().getAuthentication(), "No authentication associated with this TGT!");
122+
Principal taraPrincipal = TaraPrincipalFactory.createPrincipal(accessToken.getTicketGrantingTicket());
130123

131-
if (isEmailClaimsRequested(taraPrincipal)) {
132-
claims.setStringClaim(CLAIM_EMAIL, getMandatoryPrincipalAttribute(TaraPrincipal.Attribute.EMAIL, taraPrincipal));
133-
claims.setClaim(CLAIM_EMAIL_VERIFIED, getMandatoryPrincipalAttribute(TaraPrincipal.Attribute.EMAIL_VERIFIED, taraPrincipal, Boolean.class));
124+
claims.setSubject(getAttributeValue(SUB, taraPrincipal));
125+
126+
if (taraPrincipal.getAttributes().containsKey(EMAIL.name()) && taraPrincipal.getAttributes().containsKey(EMAIL_VERIFIED.name())) {
127+
claims.setStringClaim(CLAIM_EMAIL, getAttributeValue(EMAIL, taraPrincipal));
128+
claims.setClaim(CLAIM_EMAIL_VERIFIED, getAttributeValue(EMAIL_VERIFIED, taraPrincipal, Boolean.class));
134129
}
135130

136131
claims.setClaim(CLAIM_PROFILE_ATTRIBUTES, getProfileAttributesMap(taraPrincipal));
137-
claims.setStringListClaim(OidcConstants.AMR, getAmrValuesList(taraPrincipal));
132+
claims.setStringListClaim(OidcConstants.AMR, getAttributeValue(AMR, taraPrincipal, List.class));
138133

139-
if (isOfAuthenticationType(taraPrincipal, AuthenticationType.eIDAS)) {
140-
String levelOfAssurance = getMandatoryPrincipalAttribute(LEVEL_OF_ASSURANCE, taraPrincipal);
141-
claims.setStringClaim(OidcConstants.ACR, levelOfAssurance);
134+
if (taraPrincipal.getAttributes().containsKey(ACR.name())) {
135+
claims.setStringClaim(OidcConstants.ACR, getAttributeValue(ACR, taraPrincipal));
142136
}
143137
}
144138

145-
private boolean isEmailClaimsRequested(Principal taraPrincipal) {
146-
return taraPrincipal.getAttributes().get(TaraPrincipal.Attribute.EMAIL.name()) != null;
147-
}
148-
149139
private Map<String, Object> getProfileAttributesMap(Principal principal) {
150140
final Map<String, Object> principalAttributes = principal.getAttributes();
151141
final Map<String, Object> profileAttributes = new TreeMap(String.CASE_INSENSITIVE_ORDER);
152142

153143
validProfileAttributesToClaimsList.forEach(key -> {
154144
Object value = principalAttributes.get(key.name().toLowerCase());
155145
if (value != null)
156-
profileAttributes.put(key.name().toLowerCase(), ((List)value).get(0));
146+
profileAttributes.put(key.name().toLowerCase(), value);
157147
});
158148

159149
return profileAttributes;
160150
}
161151

162-
private static boolean isOfAuthenticationType(Principal principal, AuthenticationType type) {
163-
String authenticationType = getMandatoryPrincipalAttribute(AUTHENTICATION_TYPE, principal);
164-
return type.getAmrName().equals(authenticationType);
165-
}
166-
167-
private List<String> getAmrValuesList(Principal principal) {
168-
String authenticationType = getMandatoryPrincipalAttribute(AUTHENTICATION_TYPE, principal);
169-
return CollectionUtils.toCollection(authenticationType).stream()
170-
.map(e -> e.toString()).collect(Collectors.toList());
171-
}
172-
173152
private String generateAccessTokenHash(final AccessToken accessTokenId) {
174153
final byte[] tokenBytes = accessTokenId.getId().getBytes();
175154
final String hashAlg;
@@ -189,18 +168,20 @@ private String generateAccessTokenHash(final AccessToken accessTokenId) {
189168
return EncodingUtils.encodeBase64(hashBytesLeftHalf);
190169
}
191170

192-
private static String getMandatoryPrincipalAttribute(TaraPrincipal.Attribute attribute, Principal principal) {
193-
return getMandatoryPrincipalAttribute(attribute, principal, String.class);
171+
private static String getAttributeValue(TaraPrincipal.Attribute attribute, Principal principal) {
172+
return getAttributeValue(attribute, principal, String.class);
194173
}
195174

196-
private static <T> T getMandatoryPrincipalAttribute(TaraPrincipal.Attribute attribute, Principal principal, Class<T> clazz) {
175+
private static <T> T getAttributeValue(TaraPrincipal.Attribute attribute, Principal principal, Class<T> clazz) {
197176
String attributeName = attribute.name();
198177
Assert.notNull(principal.getAttributes().get(attributeName), "Mandatory attribute " + attributeName + " not found when generating OIDC token");
199-
List list = ((List)(principal.getAttributes().get(attributeName)));
200-
Assert.isTrue(list.size() == 1, "Expected a single profile attribute. Found " + list.size());
201-
Object o = list.get(0);
202-
Assert.isTrue(o.getClass().isAssignableFrom(clazz), "Cannot assign principal value of type " + o.getClass() + " to " + clazz);
203-
return (T)o;
178+
return (T)principal.getAttributes().get(attributeName);
179+
}
180+
181+
private NumericDate getExpirationDate(long timeoutInSeconds) {
182+
final NumericDate expirationDate = NumericDate.now();
183+
expirationDate.addSeconds(timeoutInSeconds);
184+
return expirationDate;
204185
}
205186
}
206187

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package ee.ria.sso.oidc;
2+
3+
import ee.ria.sso.authentication.principal.TaraPrincipalFactory;
4+
import lombok.extern.slf4j.Slf4j;
5+
import org.apereo.cas.authentication.principal.Principal;
6+
import org.apereo.cas.oidc.OidcConstants;
7+
import org.apereo.cas.support.oauth.profile.OAuth20UserProfileDataCreator;
8+
import org.apereo.cas.ticket.accesstoken.AccessToken;
9+
import org.apereo.inspektr.audit.annotation.Audit;
10+
import org.pac4j.core.context.J2EContext;
11+
12+
import java.util.LinkedHashMap;
13+
import java.util.Map;
14+
15+
16+
17+
@Slf4j
18+
public class TaraOidcUserProfileDataCreator implements OAuth20UserProfileDataCreator {
19+
20+
@Override
21+
@Audit(action = "OAUTH2_USER_PROFILE_DATA",
22+
actionResolverName = "OAUTH2_USER_PROFILE_DATA_ACTION_RESOLVER",
23+
resourceResolverName = "OAUTH2_USER_PROFILE_DATA_RESOURCE_RESOLVER")
24+
public Map<String, Object> createFrom(final AccessToken accessToken, final J2EContext context) {
25+
Principal principal = TaraPrincipalFactory.createPrincipal(accessToken.getTicketGrantingTicket());
26+
27+
final Map<String, Object> map = new LinkedHashMap<>();
28+
for (Map.Entry<String, Object> entry : principal.getAttributes().entrySet()) {
29+
map.put(entry.getKey(), entry.getValue());
30+
}
31+
32+
map.put(OidcConstants.CLAIM_AUTH_TIME, accessToken.getTicketGrantingTicket().getAuthentication().getAuthenticationDate().toEpochSecond());
33+
34+
return map;
35+
}
36+
}
37+

src/main/java/org/apereo/cas/oidc/config/OidcConfiguration.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.github.benmanes.caffeine.cache.LoadingCache;
66
import ee.ria.sso.Constants;
77
import ee.ria.sso.config.TaraProperties;
8+
import ee.ria.sso.oidc.TaraOidcUserProfileDataCreator;
89
import lombok.extern.slf4j.Slf4j;
910
import org.apache.commons.lang3.StringUtils;
1011
import org.apereo.cas.CentralAuthenticationService;
@@ -403,7 +404,7 @@ discoverySettings, profileScopeToAttributesFilter(),
403404
ticketGrantingTicketCookieGenerator.getIfAvailable());
404405
}
405406

406-
@ConditionalOnProperty(name = Constants.TARA_OIDC_PROFILE_ENDPOINT_ENABLED)
407+
@ConditionalOnProperty(name = Constants.TARA_OIDC_PROFILE_ENDPOINT_ENABLED, matchIfMissing = true)
407408
@RefreshScope
408409
@Bean
409410
public OidcUserProfileEndpointController oidcProfileController() {
@@ -413,12 +414,13 @@ public OidcUserProfileEndpointController oidcProfileController() {
413414
profileScopeToAttributesFilter(),
414415
casProperties,
415416
ticketGrantingTicketCookieGenerator.getIfAvailable(),
416-
oauthUserProfileViewRenderer, oidcUserProfileDataCreator());
417+
oauthUserProfileViewRenderer,
418+
taraOidcUserProfileDataCreator());
417419
}
418420

419421
@Bean
420-
public OAuth20UserProfileDataCreator oidcUserProfileDataCreator() {
421-
return new OidcUserProfileDataCreator(servicesManager, profileScopeToAttributesFilter());
422+
public OAuth20UserProfileDataCreator taraOidcUserProfileDataCreator() {
423+
return new TaraOidcUserProfileDataCreator();
422424
}
423425

424426
@ConditionalOnMissingBean(name = "oidcAuthorizeController")

0 commit comments

Comments
 (0)