This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/master by this push:
new 66de2edf18 [SYNCOPE-1862] Fix mapping of AttrReleasePolicy for OIDC
client apps (#982)
66de2edf18 is described below
commit 66de2edf18576a051abeef34cf65ea96821c5059
Author: Francesco Chicchiriccò <[email protected]>
AuthorDate: Wed Feb 5 19:16:12 2025 +0100
[SYNCOPE-1862] Fix mapping of AttrReleasePolicy for OIDC client apps (#982)
---
.../org/apache/syncope/fit/AbstractITCase.java | 4 +-
.../apache/syncope/fit/sra/AbstractOIDCITCase.java | 41 +++++
.../apache/syncope/fit/sra/AbstractSRAITCase.java | 33 ----
.../org/apache/syncope/fit/ui/OIDCC4UIITCase.java | 20 +++
.../wa/bootstrap/WAPropertySourceLocator.java | 56 ++----
.../wa/bootstrap/mapping/AttrReleaseMapper.java | 3 +-
.../mapping/DefaultAttrReleaseMapper.java | 192 +++++++++++++++++----
.../starter/mapping/OIDCRPClientAppTOMapper.java | 42 +----
.../starter/mapping/RegisteredServiceMapper.java | 5 +-
.../syncope/wa/starter/WAServiceRegistryTest.java | 3 +-
.../src/test/resources/debug/wa-debug.properties | 7 +-
11 files changed, 247 insertions(+), 159 deletions(-)
diff --git
a/fit/wa-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
b/fit/wa-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index a3038f2ec1..e0ec2b2f69 100644
--- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -65,9 +65,9 @@ public abstract class AbstractITCase {
protected static final String ADMIN_PWD = "password";
- private static final String ANONYMOUS_USER = "anonymous";
+ protected static final String ANONYMOUS_USER = "anonymous";
- private static final String ANONYMOUS_KEY = "anonymousKey";
+ protected static final String ANONYMOUS_KEY = "anonymousKey";
protected static final String CORE_ADDRESS =
"https://localhost:9443/syncope/rest";
diff --git
a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractOIDCITCase.java
b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractOIDCITCase.java
index 2f5ae3042c..c01f6087a0 100644
---
a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractOIDCITCase.java
+++
b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractOIDCITCase.java
@@ -58,10 +58,13 @@ import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.syncope.common.lib.OIDCScopeConstants;
import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO;
+import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf;
import org.apache.syncope.common.lib.to.OIDCRPClientAppTO;
import org.apache.syncope.common.lib.types.ClientAppType;
import org.apache.syncope.common.lib.types.OIDCGrantType;
import org.apache.syncope.common.lib.types.OIDCSubjectType;
+import org.apache.syncope.common.lib.types.PolicyType;
import org.apache.syncope.common.rest.api.RESTHeaders;
import org.apache.syncope.common.rest.api.service.wa.WAConfigService;
import org.apereo.cas.oidc.OidcConstants;
@@ -80,6 +83,37 @@ abstract class AbstractOIDCITCase extends AbstractSRAITCase {
protected static String TOKEN_URI;
+ protected static AttrReleasePolicyTO getAttrReleasePolicy() {
+ String description = "SRA attr release policy";
+
+ return POLICY_SERVICE.list(PolicyType.ATTR_RELEASE).stream().
+ map(AttrReleasePolicyTO.class::cast).
+ filter(policy -> description.equals(policy.getName())
+ && policy.getConf() instanceof DefaultAttrReleasePolicyConf).
+ findFirst().
+ orElseGet(() -> {
+ DefaultAttrReleasePolicyConf policyConf = new
DefaultAttrReleasePolicyConf();
+ policyConf.getReleaseAttrs().put("surname", "family_name");
+ policyConf.getReleaseAttrs().put("fullname", "name");
+ policyConf.getReleaseAttrs().put("firstname",
"given_name");
+ policyConf.getReleaseAttrs().put("email", "email");
+ policyConf.getReleaseAttrs().put("groups", "groups");
+
+ AttrReleasePolicyTO policy = new AttrReleasePolicyTO();
+ policy.setName(description);
+ policy.setConf(policyConf);
+
+ Response response =
POLICY_SERVICE.create(PolicyType.ATTR_RELEASE, policy);
+ if (response.getStatusInfo().getStatusCode() !=
Response.Status.CREATED.getStatusCode()) {
+ fail("Could not create Test Attr Release Policy");
+ }
+
+ return POLICY_SERVICE.read(
+ PolicyType.ATTR_RELEASE,
+
response.getHeaderString(RESTHeaders.RESOURCE_KEY));
+ });
+ }
+
protected static void oidcClientAppSetup(
final String appName,
final String sraRegistrationId,
@@ -136,6 +170,13 @@ abstract class AbstractOIDCITCase extends
AbstractSRAITCase {
WA_CONFIG_SERVICE.pushToWA(WAConfigService.PushSubject.conf, List.of());
throw new IllegalStateException();
}
+ metadata = WebClient.create(
+ WA_ADDRESS + "/actuator/env", ANONYMOUS_USER,
ANONYMOUS_KEY, null).
+ get().readEntity(String.class);
+ if
(!metadata.contains("cas.authn.oidc.core.user-defined-scopes.syncope")) {
+
WA_CONFIG_SERVICE.pushToWA(WAConfigService.PushSubject.conf, List.of());
+ throw new IllegalStateException();
+ }
return true;
} catch (Exception e) {
diff --git
a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractSRAITCase.java
b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractSRAITCase.java
index 2e979013c9..bb7c74c6e4 100644
---
a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractSRAITCase.java
+++
b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractSRAITCase.java
@@ -49,9 +49,7 @@ import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
-import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO;
import org.apache.syncope.common.lib.policy.AuthPolicyTO;
-import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf;
import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf;
import org.apache.syncope.common.lib.to.SRARouteTO;
import org.apache.syncope.common.lib.types.PolicyType;
@@ -241,37 +239,6 @@ abstract class AbstractSRAITCase extends AbstractITCase {
});
}
- protected static AttrReleasePolicyTO getAttrReleasePolicy() {
- String description = "SRA attr release policy";
-
- return POLICY_SERVICE.list(PolicyType.ATTR_RELEASE).stream().
- map(AttrReleasePolicyTO.class::cast).
- filter(policy -> description.equals(policy.getName())
- && policy.getConf() instanceof DefaultAttrReleasePolicyConf).
- findFirst().
- orElseGet(() -> {
- DefaultAttrReleasePolicyConf policyConf = new
DefaultAttrReleasePolicyConf();
- policyConf.getAllowedAttrs().add("family_name");
- policyConf.getAllowedAttrs().add("name");
- policyConf.getAllowedAttrs().add("given_name");
- policyConf.getAllowedAttrs().add("email");
- policyConf.getAllowedAttrs().add("groups");
-
- AttrReleasePolicyTO policy = new AttrReleasePolicyTO();
- policy.setName(description);
- policy.setConf(policyConf);
-
- Response response =
POLICY_SERVICE.create(PolicyType.ATTR_RELEASE, policy);
- if (response.getStatusInfo().getStatusCode() !=
Response.Status.CREATED.getStatusCode()) {
- fail("Could not create Test Attr Release Policy");
- }
-
- return POLICY_SERVICE.read(
- PolicyType.ATTR_RELEASE,
-
response.getHeaderString(RESTHeaders.RESOURCE_KEY));
- });
- }
-
protected static ObjectNode checkGetResponse(
final CloseableHttpResponse response, final String
originalRequestURI) throws IOException {
diff --git
a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OIDCC4UIITCase.java
b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OIDCC4UIITCase.java
index 6505af1172..e5d4158dc8 100644
---
a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OIDCC4UIITCase.java
+++
b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OIDCC4UIITCase.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.fit.ui;
+import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -30,6 +31,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.http.Consts;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpStatus;
@@ -100,6 +103,23 @@ public class OIDCC4UIITCase extends AbstractUIITCase {
clientApp.getScopes().add(OIDCScopeConstants.EMAIL);
CLIENT_APP_SERVICE.update(ClientAppType.OIDCRP, clientApp);
+
+ await().atMost(60, TimeUnit.SECONDS).pollInterval(20,
TimeUnit.SECONDS).until(() -> {
+ try {
+ String metadata = WebClient.create(
+ WA_ADDRESS + "/actuator/env", ANONYMOUS_USER,
ANONYMOUS_KEY, null).
+ get().readEntity(String.class);
+ if
(!metadata.contains("cas.authn.oidc.core.user-defined-scopes.syncope")) {
+
WA_CONFIG_SERVICE.pushToWA(WAConfigService.PushSubject.conf, List.of());
+ throw new IllegalStateException();
+ }
+
+ return true;
+ } catch (Exception e) {
+ // ignore
+ }
+ return false;
+ });
WA_CONFIG_SERVICE.pushToWA(WAConfigService.PushSubject.clientApps,
List.of());
}
diff --git
a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java
b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java
index 0c5a609bbb..a840a1be14 100644
---
a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java
+++
b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java
@@ -19,9 +19,7 @@
package org.apache.syncope.wa.bootstrap;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;
@@ -30,7 +28,6 @@ import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.client.lib.SyncopeClient;
-import org.apache.syncope.common.lib.OIDCScopeConstants;
import org.apache.syncope.common.lib.to.OIDCRPClientAppTO;
import org.apache.syncope.common.rest.api.service.AttrRepoService;
import org.apache.syncope.common.rest.api.service.AuthModuleService;
@@ -40,14 +37,9 @@ import
org.apache.syncope.wa.bootstrap.mapping.AttrReleaseMapper;
import org.apache.syncope.wa.bootstrap.mapping.AttrRepoPropertySourceMapper;
import org.apache.syncope.wa.bootstrap.mapping.AuthModulePropertySourceMapper;
import org.apereo.cas.configuration.model.support.oidc.OidcDiscoveryProperties;
-import org.apereo.cas.oidc.claims.OidcAddressScopeAttributeReleasePolicy;
-import org.apereo.cas.oidc.claims.OidcEmailScopeAttributeReleasePolicy;
-import org.apereo.cas.oidc.claims.OidcPhoneScopeAttributeReleasePolicy;
-import org.apereo.cas.oidc.claims.OidcProfileScopeAttributeReleasePolicy;
-import org.apereo.cas.services.BaseMappedAttributeReleasePolicy;
+import org.apereo.cas.oidc.claims.OidcCustomScopeAttributeReleasePolicy;
import org.apereo.cas.services.ChainingAttributeReleasePolicy;
import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
-import org.apereo.cas.services.ReturnAllowedAttributeReleasePolicy;
import org.apereo.cas.util.crypto.CipherExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -133,43 +125,23 @@ public class WAPropertySourceLocator implements
PropertySourceLocator {
});
Set<String> customClaims =
syncopeClient.getService(WAClientAppService.class).list().stream().
- filter(clientApp -> clientApp.getAttrReleasePolicy() != null
- && clientApp.getClientAppTO() instanceof OIDCRPClientAppTO).
- flatMap(clientApp -> {
- OIDCRPClientAppTO rp =
OIDCRPClientAppTO.class.cast(clientApp.getClientAppTO());
-
+ filter(app -> app.getClientAppTO() instanceof
OIDCRPClientAppTO && app.getAttrReleasePolicy() != null).
+ flatMap(app -> {
RegisteredServiceAttributeReleasePolicy
attributeReleasePolicy =
-
attrReleaseMapper.build(clientApp.getAttrReleasePolicy());
-
- Set<String> claims = new HashSet<>();
- if (attributeReleasePolicy instanceof
BaseMappedAttributeReleasePolicy baseMapped) {
- claims.addAll(baseMapped.
- getAllowedAttributes().values().stream().
-
map(Objects::toString).collect(Collectors.toSet()));
- } else if (attributeReleasePolicy instanceof
ReturnAllowedAttributeReleasePolicy returnAllowed) {
- claims.addAll(returnAllowed.
-
getAllowedAttributes().stream().collect(Collectors.toSet()));
- } else if (attributeReleasePolicy instanceof
ChainingAttributeReleasePolicy chaining) {
- chaining.getPolicies().stream().
-
filter(ReturnAllowedAttributeReleasePolicy.class::isInstance).
-
findFirst().map(ReturnAllowedAttributeReleasePolicy.class::cast).
- map(p ->
p.getAllowedAttributes().stream().collect(Collectors.toSet())).
- ifPresent(claims::addAll);
- }
- if (rp.getScopes().contains(OIDCScopeConstants.PROFILE)) {
-
claims.removeAll(OidcProfileScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
- }
- if (rp.getScopes().contains(OIDCScopeConstants.ADDRESS)) {
-
claims.removeAll(OidcAddressScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
- }
- if (rp.getScopes().contains(OIDCScopeConstants.EMAIL)) {
-
claims.removeAll(OidcEmailScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
+ attrReleaseMapper.build(app.getClientAppTO(),
app.getAttrReleasePolicy());
+
+ if (attributeReleasePolicy instanceof
OidcCustomScopeAttributeReleasePolicy custom) {
+ return custom.getAllowedAttributes().stream();
}
- if (rp.getScopes().contains(OIDCScopeConstants.PHONE)) {
-
claims.removeAll(OidcPhoneScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
+
+ if (attributeReleasePolicy instanceof
ChainingAttributeReleasePolicy chain) {
+ return chain.getPolicies().stream().
+
filter(OidcCustomScopeAttributeReleasePolicy.class::isInstance).
+
map(OidcCustomScopeAttributeReleasePolicy.class::cast).
+ flatMap(p ->
p.getAllowedAttributes().stream());
}
- return claims.stream();
+ return Stream.empty();
}).collect(Collectors.toSet());
if (!customClaims.isEmpty()) {
Stream.concat(new OidcDiscoveryProperties().getClaims().stream(),
customClaims.stream()).
diff --git
a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AttrReleaseMapper.java
b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AttrReleaseMapper.java
index 021968582a..028ebdefee 100644
---
a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AttrReleaseMapper.java
+++
b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AttrReleaseMapper.java
@@ -20,11 +20,12 @@ package org.apache.syncope.wa.bootstrap.mapping;
import org.apache.syncope.common.lib.policy.AttrReleasePolicyConf;
import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO;
+import org.apache.syncope.common.lib.to.ClientAppTO;
import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
public interface AttrReleaseMapper {
boolean supports(AttrReleasePolicyConf conf);
- RegisteredServiceAttributeReleasePolicy build(AttrReleasePolicyTO policy);
+ RegisteredServiceAttributeReleasePolicy build(ClientAppTO clientApp,
AttrReleasePolicyTO policy);
}
diff --git
a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/DefaultAttrReleaseMapper.java
b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/DefaultAttrReleaseMapper.java
index e144d4ce85..07cb261d3e 100644
---
a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/DefaultAttrReleaseMapper.java
+++
b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/DefaultAttrReleaseMapper.java
@@ -18,15 +18,28 @@
*/
package org.apache.syncope.wa.bootstrap.mapping;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import org.apache.syncope.common.lib.OIDCScopeConstants;
import org.apache.syncope.common.lib.policy.AttrReleasePolicyConf;
import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO;
import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf;
+import org.apache.syncope.common.lib.to.ClientAppTO;
+import org.apache.syncope.common.lib.to.OIDCRPClientAppTO;
import
org.apereo.cas.authentication.principal.DefaultPrincipalAttributesRepository;
import
org.apereo.cas.authentication.principal.cache.AbstractPrincipalAttributesRepository;
import
org.apereo.cas.authentication.principal.cache.CachingPrincipalAttributesRepository;
import
org.apereo.cas.configuration.model.core.authentication.PrincipalAttributesCoreProperties.MergingStrategyTypes;
import org.apereo.cas.configuration.support.TriStateBoolean;
+import org.apereo.cas.oidc.claims.BaseOidcScopeAttributeReleasePolicy;
+import org.apereo.cas.oidc.claims.OidcAddressScopeAttributeReleasePolicy;
+import org.apereo.cas.oidc.claims.OidcCustomScopeAttributeReleasePolicy;
+import org.apereo.cas.oidc.claims.OidcEmailScopeAttributeReleasePolicy;
+import org.apereo.cas.oidc.claims.OidcPhoneScopeAttributeReleasePolicy;
+import org.apereo.cas.oidc.claims.OidcProfileScopeAttributeReleasePolicy;
import org.apereo.cas.services.AbstractRegisteredServiceAttributeReleasePolicy;
import org.apereo.cas.services.ChainingAttributeReleasePolicy;
import org.apereo.cas.services.DenyAllAttributeReleasePolicy;
@@ -34,22 +47,113 @@ import
org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
import org.apereo.cas.services.ReturnAllowedAttributeReleasePolicy;
import org.apereo.cas.services.ReturnMappedAttributeReleasePolicy;
import org.apereo.cas.services.consent.DefaultRegisteredServiceConsentPolicy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class DefaultAttrReleaseMapper implements AttrReleaseMapper {
+ protected static final Logger LOG =
LoggerFactory.getLogger(AttrReleaseMapper.class);
+
+ protected static void warnMissingScope(
+ final String clientApp,
+ final String internal,
+ final Object external,
+ final String scope) {
+
+ LOG.warn("OIDC client app {} defines claim mapping {}={} but does not
specify the related scope {}",
+ clientApp, internal, external, scope);
+ }
+
@Override
public boolean supports(final AttrReleasePolicyConf conf) {
return DefaultAttrReleasePolicyConf.class.equals(conf.getClass());
}
+ protected Map<String, BaseOidcScopeAttributeReleasePolicy> buildOidc(
+ final OIDCRPClientAppTO rp,
+ final DefaultAttrReleasePolicyConf conf) {
+
+ Map<String, BaseOidcScopeAttributeReleasePolicy> policies = new
HashMap<>();
+
+ conf.getReleaseAttrs().forEach((internal, external) -> {
+ if
(OidcProfileScopeAttributeReleasePolicy.ALLOWED_CLAIMS.contains(external.toString()))
{
+ if (rp.getScopes().contains(OIDCScopeConstants.PROFILE)) {
+ policies.computeIfAbsent(
+ OIDCScopeConstants.PROFILE,
+ k -> new OidcProfileScopeAttributeReleasePolicy()).
+ getClaimMappings().put(external.toString(),
internal);
+ } else {
+ warnMissingScope(rp.getName(), internal, external,
OIDCScopeConstants.PROFILE);
+ }
+ } else if
(OidcEmailScopeAttributeReleasePolicy.ALLOWED_CLAIMS.contains(external.toString()))
{
+ if (rp.getScopes().contains(OIDCScopeConstants.EMAIL)) {
+ policies.computeIfAbsent(
+ OIDCScopeConstants.EMAIL,
+ k -> new OidcEmailScopeAttributeReleasePolicy()).
+ getClaimMappings().put(external.toString(),
internal);
+ } else {
+ warnMissingScope(rp.getName(), internal, external,
OIDCScopeConstants.EMAIL);
+ }
+ } else if
(OidcAddressScopeAttributeReleasePolicy.ALLOWED_CLAIMS.contains(external.toString()))
{
+ if (rp.getScopes().contains(OIDCScopeConstants.ADDRESS)) {
+ policies.computeIfAbsent(
+ OIDCScopeConstants.ADDRESS,
+ k -> new OidcAddressScopeAttributeReleasePolicy()).
+ getClaimMappings().put(external.toString(),
internal);
+ } else {
+ warnMissingScope(rp.getName(), internal, external,
OIDCScopeConstants.ADDRESS);
+ }
+ } else if
(OidcPhoneScopeAttributeReleasePolicy.ALLOWED_CLAIMS.contains(external.toString()))
{
+ if (rp.getScopes().contains(OIDCScopeConstants.PHONE)) {
+ policies.computeIfAbsent(
+ OIDCScopeConstants.PHONE,
+ k -> new OidcPhoneScopeAttributeReleasePolicy()).
+ getClaimMappings().put(external.toString(),
internal);
+ } else {
+ warnMissingScope(rp.getName(), internal, external,
OIDCScopeConstants.PHONE);
+ }
+ } else {
+ BaseOidcScopeAttributeReleasePolicy custom =
policies.computeIfAbsent(
+ OIDCScopeConstants.SYNCOPE,
+ k -> new OidcCustomScopeAttributeReleasePolicy(
+ OIDCScopeConstants.SYNCOPE, new
ArrayList<>()));
+
+ custom.getAllowedAttributes().add(external.toString());
+ custom.getClaimMappings().put(external.toString(), internal);
+ }
+ });
+
+ return policies;
+ }
+
+ protected Optional<DefaultRegisteredServiceConsentPolicy>
buildConsentPolicy(
+ final AttrReleasePolicyTO policy,
+ final DefaultAttrReleasePolicyConf conf) {
+
+ if (conf.getExcludedAttrs().isEmpty() &&
conf.getIncludeOnlyAttrs().isEmpty()) {
+ return Optional.empty();
+ }
+
+ DefaultRegisteredServiceConsentPolicy consentPolicy = new
DefaultRegisteredServiceConsentPolicy(
+ new HashSet<>(conf.getExcludedAttrs()), new
HashSet<>(conf.getIncludeOnlyAttrs()));
+ consentPolicy.setOrder(policy.getOrder());
+
consentPolicy.setStatus(TriStateBoolean.fromBoolean(policy.getStatus()));
+ return Optional.of(consentPolicy);
+ }
+
@Override
- public RegisteredServiceAttributeReleasePolicy build(final
AttrReleasePolicyTO policy) {
+ public RegisteredServiceAttributeReleasePolicy build(final ClientAppTO
app, final AttrReleasePolicyTO policy) {
DefaultAttrReleasePolicyConf conf = (DefaultAttrReleasePolicyConf)
policy.getConf();
+ Map<String, BaseOidcScopeAttributeReleasePolicy> oidc = null;
ReturnMappedAttributeReleasePolicy returnMapped = null;
if (!conf.getReleaseAttrs().isEmpty()) {
- returnMapped = new ReturnMappedAttributeReleasePolicy();
- returnMapped.setAllowedAttributes(conf.getReleaseAttrs());
+ if (app instanceof OIDCRPClientAppTO rp) {
+ oidc = buildOidc(rp, conf);
+ } else {
+ returnMapped = new ReturnMappedAttributeReleasePolicy();
+ returnMapped.setAllowedAttributes(conf.getReleaseAttrs());
+ }
}
ReturnAllowedAttributeReleasePolicy returnAllowed = null;
@@ -58,50 +162,64 @@ public class DefaultAttrReleaseMapper implements
AttrReleaseMapper {
returnAllowed.setAllowedAttributes(conf.getAllowedAttrs());
}
- AbstractRegisteredServiceAttributeReleasePolicy attributeReleasePolicy;
- if (returnMapped == null && returnAllowed == null) {
- attributeReleasePolicy = new DenyAllAttributeReleasePolicy();
- } else if (returnMapped != null) {
- attributeReleasePolicy = returnMapped;
+ ChainingAttributeReleasePolicy chain = new
ChainingAttributeReleasePolicy();
+ AbstractRegisteredServiceAttributeReleasePolicy single = null;
+ if (oidc == null) {
+ if (returnMapped == null && returnAllowed == null) {
+ single = new DenyAllAttributeReleasePolicy();
+ } else if (returnMapped != null && returnAllowed == null) {
+ single = returnMapped;
+ } else if (returnMapped == null && returnAllowed != null) {
+ single = returnAllowed;
+ } else {
+ chain.addPolicies(returnMapped, returnAllowed);
+ }
} else {
- attributeReleasePolicy = returnAllowed;
+ if (oidc.size() == 1) {
+ single = oidc.values().iterator().next();
+ } else {
+ // if present, add the custom scope at the end of the chain
+ oidc.entrySet().stream().
+ filter(entry ->
!OIDCScopeConstants.SYNCOPE.equals(entry.getKey())).
+ forEach(entry -> chain.addPolicies(entry.getValue()));
+
Optional.ofNullable(oidc.get(OIDCScopeConstants.SYNCOPE)).ifPresent(chain::addPolicies);
+ }
}
- DefaultRegisteredServiceConsentPolicy consentPolicy = new
DefaultRegisteredServiceConsentPolicy(
- new HashSet<>(conf.getExcludedAttrs()), new
HashSet<>(conf.getIncludeOnlyAttrs()));
- consentPolicy.setOrder(policy.getOrder());
-
consentPolicy.setStatus(TriStateBoolean.fromBoolean(policy.getStatus()));
- attributeReleasePolicy.setConsentPolicy(consentPolicy);
+ Optional<DefaultRegisteredServiceConsentPolicy> consentPolicy =
buildConsentPolicy(policy, conf);
+ if (!chain.getPolicies().isEmpty()) {
+ chain.getPolicies().stream().
+
filter(AbstractRegisteredServiceAttributeReleasePolicy.class::isInstance).
+
map(AbstractRegisteredServiceAttributeReleasePolicy.class::cast).
+ forEach(p -> {
+ consentPolicy.ifPresent(p::setConsentPolicy);
+
Optional.ofNullable(conf.getPrincipalIdAttr()).ifPresent(p::setPrincipalIdAttribute);
+ });
- if (conf.getPrincipalIdAttr() != null) {
-
attributeReleasePolicy.setPrincipalIdAttribute(conf.getPrincipalIdAttr());
+ if (conf.getPrincipalAttrRepoConf() != null &&
!conf.getPrincipalAttrRepoConf().getAttrRepos().isEmpty()) {
+ DefaultAttrReleasePolicyConf.PrincipalAttrRepoConf parc =
conf.getPrincipalAttrRepoConf();
+
chain.setMergingPolicy(MergingStrategyTypes.valueOf(parc.getMergingStrategy().name()));
+ }
}
+ Optional.ofNullable(single).ifPresent(p -> {
+ consentPolicy.ifPresent(p::setConsentPolicy);
+
Optional.ofNullable(conf.getPrincipalIdAttr()).ifPresent(p::setPrincipalIdAttribute);
- if (conf.getPrincipalAttrRepoConf() != null &&
!conf.getPrincipalAttrRepoConf().getAttrRepos().isEmpty()) {
- DefaultAttrReleasePolicyConf.PrincipalAttrRepoConf parc =
conf.getPrincipalAttrRepoConf();
+ if (conf.getPrincipalAttrRepoConf() != null &&
!conf.getPrincipalAttrRepoConf().getAttrRepos().isEmpty()) {
+ DefaultAttrReleasePolicyConf.PrincipalAttrRepoConf parc =
conf.getPrincipalAttrRepoConf();
- AbstractPrincipalAttributesRepository par = parc.getExpiration() > 0
- ? new
CachingPrincipalAttributesRepository(parc.getTimeUnit().name(),
parc.getExpiration())
- : new DefaultPrincipalAttributesRepository();
+ AbstractPrincipalAttributesRepository par =
parc.getExpiration() > 0
+ ? new
CachingPrincipalAttributesRepository(parc.getTimeUnit().name(),
parc.getExpiration())
+ : new DefaultPrincipalAttributesRepository();
-
par.setMergingStrategy(MergingStrategyTypes.valueOf(parc.getMergingStrategy().name()));
- par.setIgnoreResolvedAttributes(par.isIgnoreResolvedAttributes());
- par.setAttributeRepositoryIds(new HashSet<>(parc.getAttrRepos()));
- attributeReleasePolicy.setPrincipalAttributesRepository(par);
- }
+
par.setMergingStrategy(MergingStrategyTypes.valueOf(parc.getMergingStrategy().name()));
+
par.setIgnoreResolvedAttributes(par.isIgnoreResolvedAttributes());
+ par.setAttributeRepositoryIds(new
HashSet<>(parc.getAttrRepos()));
- if (returnMapped != null && returnAllowed != null) {
- ChainingAttributeReleasePolicy chain = new
ChainingAttributeReleasePolicy();
- chain.addPolicies(returnMapped, returnAllowed);
-
- if (conf.getPrincipalAttrRepoConf() != null &&
!conf.getPrincipalAttrRepoConf().getAttrRepos().isEmpty()) {
- DefaultAttrReleasePolicyConf.PrincipalAttrRepoConf parc =
conf.getPrincipalAttrRepoConf();
-
chain.setMergingPolicy(MergingStrategyTypes.valueOf(parc.getMergingStrategy().name()));
+ p.setPrincipalAttributesRepository(par);
}
+ });
- return chain;
- }
-
- return attributeReleasePolicy;
+ return single == null ? chain : single;
}
}
diff --git
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java
index 7612dd40e9..2ae5e10c37 100644
---
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java
+++
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java
@@ -21,7 +21,6 @@ package org.apache.syncope.wa.starter.mapping;
import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
-import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.OIDCScopeConstants;
@@ -30,11 +29,7 @@ import org.apache.syncope.common.lib.to.OIDCRPClientAppTO;
import org.apache.syncope.common.lib.types.OIDCGrantType;
import org.apache.syncope.common.lib.types.OIDCResponseType;
import org.apache.syncope.common.lib.wa.WAClientApp;
-import org.apereo.cas.oidc.claims.OidcAddressScopeAttributeReleasePolicy;
-import org.apereo.cas.oidc.claims.OidcEmailScopeAttributeReleasePolicy;
-import org.apereo.cas.oidc.claims.OidcPhoneScopeAttributeReleasePolicy;
-import org.apereo.cas.oidc.claims.OidcProfileScopeAttributeReleasePolicy;
-import org.apereo.cas.services.BaseMappedAttributeReleasePolicy;
+import org.apereo.cas.oidc.claims.OidcCustomScopeAttributeReleasePolicy;
import org.apereo.cas.services.ChainingAttributeReleasePolicy;
import org.apereo.cas.services.OidcRegisteredService;
import org.apereo.cas.services.RegisteredService;
@@ -46,7 +41,6 @@ import
org.apereo.cas.services.RegisteredServiceProxyGrantingTicketExpirationPol
import org.apereo.cas.services.RegisteredServiceProxyTicketExpirationPolicy;
import org.apereo.cas.services.RegisteredServiceServiceTicketExpirationPolicy;
import
org.apereo.cas.services.RegisteredServiceTicketGrantingTicketExpirationPolicy;
-import org.apereo.cas.services.ReturnAllowedAttributeReleasePolicy;
public class OIDCRPClientAppTOMapper extends AbstractClientAppMapper {
@@ -98,40 +92,14 @@ public class OIDCRPClientAppTOMapper extends
AbstractClientAppMapper {
service.setScopes(new HashSet<>(rp.getScopes()));
- Set<String> customClaims = new HashSet<>();
- if (attributeReleasePolicy instanceof BaseMappedAttributeReleasePolicy
baseMapped) {
- customClaims.addAll(baseMapped.
- getAllowedAttributes().values().stream().
- map(Objects::toString).collect(Collectors.toSet()));
- } else if (attributeReleasePolicy instanceof
ReturnAllowedAttributeReleasePolicy returnAllowed) {
- customClaims.addAll(returnAllowed.
-
getAllowedAttributes().stream().collect(Collectors.toSet()));
- } else if (attributeReleasePolicy instanceof
ChainingAttributeReleasePolicy chaining) {
- chaining.getPolicies().stream().
-
filter(ReturnAllowedAttributeReleasePolicy.class::isInstance).
-
findFirst().map(ReturnAllowedAttributeReleasePolicy.class::cast).
- map(p ->
p.getAllowedAttributes().stream().collect(Collectors.toSet())).
- ifPresent(customClaims::addAll);
- }
- if (rp.getScopes().contains(OIDCScopeConstants.PROFILE)) {
-
customClaims.removeAll(OidcProfileScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
- }
- if (rp.getScopes().contains(OIDCScopeConstants.ADDRESS)) {
-
customClaims.removeAll(OidcAddressScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
- }
- if (rp.getScopes().contains(OIDCScopeConstants.EMAIL)) {
-
customClaims.removeAll(OidcEmailScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
- }
- if (rp.getScopes().contains(OIDCScopeConstants.PHONE)) {
-
customClaims.removeAll(OidcPhoneScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
- }
+ if (attributeReleasePolicy instanceof
OidcCustomScopeAttributeReleasePolicy
+ || (attributeReleasePolicy instanceof
ChainingAttributeReleasePolicy chain
+ &&
chain.getPolicies().stream().anyMatch(OidcCustomScopeAttributeReleasePolicy.class::isInstance)))
{
- if (!customClaims.isEmpty()) {
service.getScopes().add(OIDCScopeConstants.SYNCOPE);
}
- // never set attribute relase policy for OIDC services to avoid
becoming scope-free for CAS
- setPolicies(service, authPolicy, mfaPolicy, accessStrategy, null,
+ setPolicies(service, authPolicy, mfaPolicy, accessStrategy,
attributeReleasePolicy,
tgtExpirationPolicy, stExpirationPolicy,
tgtProxyExpirationPolicy, stProxyExpirationPolicy);
return service;
diff --git
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java
index 8c1dd47fc3..1e7b1a1932 100644
---
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java
+++
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java
@@ -137,8 +137,9 @@ public class RegisteredServiceMapper {
Optional<AttrReleaseMapper> attrReleaseMapper =
attrReleaseMappers.stream().
filter(m -> m.supports(attrReleasePolicyTO.getConf())).
findFirst();
- RegisteredServiceAttributeReleasePolicy attributeReleasePolicy =
- attrReleaseMapper.map(mapper ->
mapper.build(attrReleasePolicyTO)).orElse(null);
+ RegisteredServiceAttributeReleasePolicy attributeReleasePolicy =
attrReleaseMapper.
+ map(mapper -> mapper.build(clientApp.getClientAppTO(),
attrReleasePolicyTO)).
+ orElse(null);
RegisteredServiceTicketGrantingTicketExpirationPolicy
tgtExpirationPolicy = null;
RegisteredServiceServiceTicketExpirationPolicy stExpirationPolicy =
null;
diff --git
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java
index 665e62ff47..4d2baaf93b 100644
---
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java
+++
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java
@@ -56,7 +56,6 @@ import org.apereo.cas.services.RegisteredService;
import org.apereo.cas.services.RegisteredServiceAccessStrategy;
import org.apereo.cas.services.RegisteredServiceDelegatedAuthenticationPolicy;
import org.apereo.cas.services.RegisteredServiceLogoutType;
-import org.apereo.cas.services.ReturnAllowedAttributeReleasePolicy;
import org.apereo.cas.services.ServicesManager;
import org.apereo.cas.support.saml.services.SamlRegisteredService;
import org.apereo.cas.util.RandomUtils;
@@ -190,7 +189,7 @@ public class WAServiceRegistryTest extends AbstractTest {
assertTrue(oidc.getAuthenticationPolicy().getRequiredAuthenticationHandlers().contains("TestAuthModule"));
assertTrue(((AnyAuthenticationHandlerRegisteredServiceAuthenticationPolicyCriteria)
oidc.
getAuthenticationPolicy().getCriteria()).isTryAll());
- assertTrue(oidc.getAttributeReleasePolicy() instanceof
ReturnAllowedAttributeReleasePolicy);
+ assertTrue(oidc.getAttributeReleasePolicy() instanceof
DenyAllAttributeReleasePolicy);
assertEquals(RegisteredServiceLogoutType.valueOf(oidcrpto.getLogoutType().name()),
oidc.getLogoutType());
// 5. more client with different attributes
diff --git a/wa/starter/src/test/resources/debug/wa-debug.properties
b/wa/starter/src/test/resources/debug/wa-debug.properties
index 6d144fc690..22a486ef8f 100644
--- a/wa/starter/src/test/resources/debug/wa-debug.properties
+++ b/wa/starter/src/test/resources/debug/wa-debug.properties
@@ -16,8 +16,8 @@
# under the License.
spring.main.allow-circular-references=true
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
-#keymaster.address=https://localhost:9443/syncope/rest/keymaster
+#keymaster.address=http://localhost:9080/syncope/rest/keymaster
+keymaster.address=https://localhost:9443/syncope/rest/keymaster
keymaster.username=${anonymousUser}
keymaster.password=${anonymousKey}
@@ -27,7 +27,8 @@ cas.server.name=http://localhost:8080
cas.server.prefix=${cas.server.name}/syncope-wa
cas.authn.accept.users=admin::password
-cas.authn.syncope.url=http://localhost:9080/syncope
+#cas.authn.syncope.url=http://localhost:9080/syncope
+cas.authn.syncope.url=https://localhost:9443/syncope
cas.authn.syncope.name=DefaultSyncopeAuthModule
##