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 4268571d17 [SYNCOPE-1771] Adding support for delegated auth via Google, Keycloak and Apple ID 4268571d17 is described below commit 4268571d172d3bb936cdfa79f93a70cd5d3df111 Author: Francesco Chicchiriccò <ilgro...@users.noreply.github.com> AuthorDate: Fri Jul 21 14:17:46 2023 +0200 [SYNCOPE-1771] Adding support for delegated auth via Google, Keycloak and Apple ID --- .../common/lib/auth/AppleOIDCAuthModuleConf.java | 88 ++++++++++++++ .../syncope/common/lib/auth/AuthModuleConf.java | 14 ++- ...oduleConf.java => AzureOIDCAuthModuleConf.java} | 3 +- ...duleConf.java => GoogleOIDCAuthModuleConf.java} | 21 +--- ...leConf.java => KeycloakOIDCAuthModuleConf.java} | 33 +++-- .../bootstrap/AuthModulePropertySourceMapper.java | 134 ++++++++++++++------- 6 files changed, 209 insertions(+), 84 deletions(-) diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AppleOIDCAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AppleOIDCAuthModuleConf.java new file mode 100644 index 0000000000..cc1ce7a80d --- /dev/null +++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AppleOIDCAuthModuleConf.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.syncope.common.lib.auth; + +import java.util.Map; +import org.apache.syncope.common.lib.to.AuthModuleTO; + +public class AppleOIDCAuthModuleConf extends AbstractOIDCAuthModuleConf implements AuthModuleConf { + + private static final long serialVersionUID = -471527731042579522L; + + /** + * Client secret expiration timeout. + * This settings supports the java.time.Duration syntax. + */ + protected String timeout = "PT30S"; + + /** + * Apple team identifier. + * Usually, 10 character string given to you by Apple. + */ + protected String teamId; + + /** + * Private key obtained from Apple. + * Must point to a resource that resolved to an elliptic curve (EC) private key. + */ + protected String privateKey; + + /** + * The identifier for the private key. + * Usually the 10 character Key ID of the private key you create in Apple. + */ + protected String privateKeyId; + + public String getTimeout() { + return timeout; + } + + public void setTimeout(final String timeout) { + this.timeout = timeout; + } + + public String getPrivateKey() { + return privateKey; + } + + public void setPrivateKey(final String privateKey) { + this.privateKey = privateKey; + } + + public String getPrivateKeyId() { + return privateKeyId; + } + + public void setPrivateKeyId(final String privateKeyId) { + this.privateKeyId = privateKeyId; + } + + public String getTeamId() { + return teamId; + } + + public void setTeamId(final String teamId) { + this.teamId = teamId; + } + + @Override + public Map<String, Object> map(final AuthModuleTO authModule, final Mapper mapper) { + return mapper.map(authModule, this); + } +} diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java index 2cca813cdf..8bc5a4fa6d 100644 --- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java +++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java @@ -36,8 +36,18 @@ public interface AuthModuleConf extends BaseBean { Map<String, Object> map(AuthModuleTO authModule, JaasAuthModuleConf conf); + Map<String, Object> map(AuthModuleTO authModule, OAuth20AuthModuleConf conf); + Map<String, Object> map(AuthModuleTO authModule, OIDCAuthModuleConf conf); + Map<String, Object> map(AuthModuleTO authModule, AzureOIDCAuthModuleConf conf); + + Map<String, Object> map(AuthModuleTO authModule, GoogleOIDCAuthModuleConf conf); + + Map<String, Object> map(AuthModuleTO authModule, KeycloakOIDCAuthModuleConf conf); + + Map<String, Object> map(AuthModuleTO authModule, AppleOIDCAuthModuleConf conf); + Map<String, Object> map(AuthModuleTO authModule, SAML2IdPAuthModuleConf conf); Map<String, Object> map(AuthModuleTO authModule, SyncopeAuthModuleConf conf); @@ -49,10 +59,6 @@ public interface AuthModuleConf extends BaseBean { Map<String, Object> map(AuthModuleTO authModule, U2FAuthModuleConf conf); Map<String, Object> map(AuthModuleTO authModule, SimpleMfaAuthModuleConf conf); - - Map<String, Object> map(AuthModuleTO authModule, OAuth20AuthModuleConf conf); - - Map<String, Object> map(AuthModuleTO authModule, AzureAuthModuleConf conf); } Map<String, Object> map(AuthModuleTO authModule, Mapper mapper); diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureOIDCAuthModuleConf.java similarity index 94% copy from common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureAuthModuleConf.java copy to common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureOIDCAuthModuleConf.java index c5cb26a85d..f1cd5b73f1 100644 --- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureAuthModuleConf.java +++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureOIDCAuthModuleConf.java @@ -21,7 +21,7 @@ package org.apache.syncope.common.lib.auth; import java.util.Map; import org.apache.syncope.common.lib.to.AuthModuleTO; -public class AzureAuthModuleConf extends AbstractOIDCAuthModuleConf implements AuthModuleConf { +public class AzureOIDCAuthModuleConf extends AbstractOIDCAuthModuleConf implements AuthModuleConf { private static final long serialVersionUID = -471527731042579522L; @@ -47,5 +47,4 @@ public class AzureAuthModuleConf extends AbstractOIDCAuthModuleConf implements A public Map<String, Object> map(final AuthModuleTO authModule, final Mapper mapper) { return mapper.map(authModule, this); } - } diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/GoogleOIDCAuthModuleConf.java similarity index 61% copy from common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureAuthModuleConf.java copy to common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/GoogleOIDCAuthModuleConf.java index c5cb26a85d..f848f450a7 100644 --- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureAuthModuleConf.java +++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/GoogleOIDCAuthModuleConf.java @@ -21,31 +21,12 @@ package org.apache.syncope.common.lib.auth; import java.util.Map; import org.apache.syncope.common.lib.to.AuthModuleTO; -public class AzureAuthModuleConf extends AbstractOIDCAuthModuleConf implements AuthModuleConf { +public class GoogleOIDCAuthModuleConf extends AbstractOIDCAuthModuleConf implements AuthModuleConf { private static final long serialVersionUID = -471527731042579522L; - /** - * Azure AD tenant name. After tenant is configured, #getDiscoveryUri() property will be overridden. - * - * Azure AD tenant name can take 4 different values: - * - organizations: Only users with work or school accounts from Azure AD can sign in. - * - consumers: Only users with a personal Microsoft account can sign in. - * - Specific tenant domain name or ID: Only user with account under that the specified tenant can login - */ - protected String tenant; - - public String getTenant() { - return tenant; - } - - public void setTenant(final String tenant) { - this.tenant = tenant; - } - @Override public Map<String, Object> map(final AuthModuleTO authModule, final Mapper mapper) { return mapper.map(authModule, this); } - } diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/KeycloakOIDCAuthModuleConf.java similarity index 62% rename from common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureAuthModuleConf.java rename to common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/KeycloakOIDCAuthModuleConf.java index c5cb26a85d..a4f464c157 100644 --- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureAuthModuleConf.java +++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/KeycloakOIDCAuthModuleConf.java @@ -21,31 +21,38 @@ package org.apache.syncope.common.lib.auth; import java.util.Map; import org.apache.syncope.common.lib.to.AuthModuleTO; -public class AzureAuthModuleConf extends AbstractOIDCAuthModuleConf implements AuthModuleConf { +public class KeycloakOIDCAuthModuleConf extends AbstractOIDCAuthModuleConf implements AuthModuleConf { private static final long serialVersionUID = -471527731042579522L; /** - * Azure AD tenant name. After tenant is configured, #getDiscoveryUri() property will be overridden. - * - * Azure AD tenant name can take 4 different values: - * - organizations: Only users with work or school accounts from Azure AD can sign in. - * - consumers: Only users with a personal Microsoft account can sign in. - * - Specific tenant domain name or ID: Only user with account under that the specified tenant can login + * Keycloak realm used to construct metadata discovery URI. */ - protected String tenant; + protected String realm; - public String getTenant() { - return tenant; + /** + * Keycloak base URL used to construct metadata discovery URI. + */ + protected String baseUri; + + public String getRealm() { + return realm; + } + + public void setRealm(final String realm) { + this.realm = realm; + } + + public String getBaseUri() { + return baseUri; } - public void setTenant(final String tenant) { - this.tenant = tenant; + public void setBaseUri(final String baseUri) { + this.baseUri = baseUri; } @Override public Map<String, Object> map(final AuthModuleTO authModule, final Mapper mapper) { return mapper.map(authModule, this); } - } diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/AuthModulePropertySourceMapper.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/AuthModulePropertySourceMapper.java index a40b63086e..0efbff13b5 100644 --- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/AuthModulePropertySourceMapper.java +++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/AuthModulePropertySourceMapper.java @@ -25,12 +25,16 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.client.lib.SyncopeClient; +import org.apache.syncope.common.lib.auth.AbstractOIDCAuthModuleConf; +import org.apache.syncope.common.lib.auth.AppleOIDCAuthModuleConf; import org.apache.syncope.common.lib.auth.AuthModuleConf; -import org.apache.syncope.common.lib.auth.AzureAuthModuleConf; +import org.apache.syncope.common.lib.auth.AzureOIDCAuthModuleConf; import org.apache.syncope.common.lib.auth.DuoMfaAuthModuleConf; import org.apache.syncope.common.lib.auth.GoogleMfaAuthModuleConf; +import org.apache.syncope.common.lib.auth.GoogleOIDCAuthModuleConf; import org.apache.syncope.common.lib.auth.JDBCAuthModuleConf; import org.apache.syncope.common.lib.auth.JaasAuthModuleConf; +import org.apache.syncope.common.lib.auth.KeycloakOIDCAuthModuleConf; import org.apache.syncope.common.lib.auth.LDAPAuthModuleConf; import org.apache.syncope.common.lib.auth.OAuth20AuthModuleConf; import org.apache.syncope.common.lib.auth.OIDCAuthModuleConf; @@ -56,8 +60,12 @@ import org.apereo.cas.configuration.model.support.mfa.gauth.LdapGoogleAuthentica import org.apereo.cas.configuration.model.support.mfa.simple.CasSimpleMultifactorAuthenticationProperties; import org.apereo.cas.configuration.model.support.mfa.u2f.U2FMultifactorAuthenticationProperties; import org.apereo.cas.configuration.model.support.pac4j.oauth.Pac4jOAuth20ClientProperties; +import org.apereo.cas.configuration.model.support.pac4j.oidc.BasePac4jOidcClientProperties; +import org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jAppleOidcClientProperties; import org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jAzureOidcClientProperties; import org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jGenericOidcClientProperties; +import org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jGoogleOidcClientProperties; +import org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jKeyCloakOidcClientProperties; import org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jOidcClientProperties; import org.apereo.cas.configuration.model.support.pac4j.saml.Pac4jSamlClientProperties; import org.apereo.cas.configuration.model.support.syncope.SyncopeAuthenticationProperties; @@ -149,8 +157,32 @@ public class AuthModulePropertySourceMapper extends PropertySourceMapper impleme } @Override - public Map<String, Object> map(final AuthModuleTO authModuleTO, final OIDCAuthModuleConf conf) { - Pac4jGenericOidcClientProperties props = new Pac4jGenericOidcClientProperties(); + public Map<String, Object> map(final AuthModuleTO authModuleTO, final OAuth20AuthModuleConf conf) { + Pac4jOAuth20ClientProperties props = new Pac4jOAuth20ClientProperties(); + props.setId(conf.getClientId()); + props.setSecret(conf.getClientSecret()); + props.setClientName(Optional.ofNullable(conf.getClientName()).orElse(authModuleTO.getKey())); + props.setEnabled(authModuleTO.getState() == AuthModuleState.ACTIVE); + props.setCustomParams(conf.getCustomParams()); + props.setAuthUrl(conf.getAuthUrl()); + props.setProfileVerb(conf.getProfileVerb()); + props.setProfileUrl(conf.getProfileUrl()); + props.setTokenUrl(conf.getTokenUrl()); + props.setResponseType(conf.getResponseType()); + props.setScope(conf.getScope()); + props.setPrincipalIdAttribute(conf.getUserIdAttribute()); + props.setWithState(conf.isWithState()); + props.setProfileAttrs(authModuleTO.getItems().stream(). + collect(Collectors.toMap(Item::getIntAttrName, Item::getExtAttrName))); + + return prefix("cas.authn.pac4j.oauth2[].", CasCoreConfigurationUtils.asMap(props)); + } + + protected void map( + final AuthModuleTO authModuleTO, + final BasePac4jOidcClientProperties props, + final AbstractOIDCAuthModuleConf conf) { + props.setId(conf.getClientId()); props.setSecret(conf.getClientSecret()); props.setClientName(Optional.ofNullable(conf.getClientName()).orElse(authModuleTO.getKey())); @@ -165,6 +197,13 @@ public class AuthModulePropertySourceMapper extends PropertySourceMapper impleme props.setPrincipalIdAttribute(conf.getUserIdAttribute()); props.setExpireSessionWithToken(conf.isExpireSessionWithToken()); props.setTokenExpirationAdvance(conf.getTokenExpirationAdvance()); + } + + @Override + public Map<String, Object> map(final AuthModuleTO authModuleTO, final OIDCAuthModuleConf conf) { + Pac4jGenericOidcClientProperties props = new Pac4jGenericOidcClientProperties(); + map(authModuleTO, props, conf); + Pac4jOidcClientProperties client = new Pac4jOidcClientProperties(); client.setGeneric(props); @@ -172,25 +211,54 @@ public class AuthModulePropertySourceMapper extends PropertySourceMapper impleme } @Override - public Map<String, Object> map(final AuthModuleTO authModuleTO, final OAuth20AuthModuleConf conf) { - Pac4jOAuth20ClientProperties props = new Pac4jOAuth20ClientProperties(); - props.setId(conf.getClientId()); - props.setSecret(conf.getClientSecret()); - props.setClientName(Optional.ofNullable(conf.getClientName()).orElse(authModuleTO.getKey())); - props.setEnabled(authModuleTO.getState() == AuthModuleState.ACTIVE); - props.setCustomParams(conf.getCustomParams()); - props.setAuthUrl(conf.getAuthUrl()); - props.setProfileVerb(conf.getProfileVerb()); - props.setProfileUrl(conf.getProfileUrl()); - props.setTokenUrl(conf.getTokenUrl()); - props.setResponseType(conf.getResponseType()); - props.setScope(conf.getScope()); - props.setPrincipalIdAttribute(conf.getUserIdAttribute()); - props.setWithState(conf.isWithState()); - props.setProfileAttrs(authModuleTO.getItems().stream(). - collect(Collectors.toMap(Item::getIntAttrName, Item::getExtAttrName))); + public Map<String, Object> map(final AuthModuleTO authModuleTO, final AzureOIDCAuthModuleConf conf) { + Pac4jAzureOidcClientProperties props = new Pac4jAzureOidcClientProperties(); + map(authModuleTO, props, conf); + props.setTenant(conf.getTenant()); - return prefix("cas.authn.pac4j.oauth2[].", CasCoreConfigurationUtils.asMap(props)); + Pac4jOidcClientProperties client = new Pac4jOidcClientProperties(); + client.setAzure(props); + + return prefix("cas.authn.pac4j.oidc[].azure.", CasCoreConfigurationUtils.asMap(props)); + } + + @Override + public Map<String, Object> map(final AuthModuleTO authModuleTO, final GoogleOIDCAuthModuleConf conf) { + Pac4jGoogleOidcClientProperties props = new Pac4jGoogleOidcClientProperties(); + map(authModuleTO, props, conf); + + Pac4jOidcClientProperties client = new Pac4jOidcClientProperties(); + client.setGoogle(props); + + return prefix("cas.authn.pac4j.oidc[].google.", CasCoreConfigurationUtils.asMap(props)); + } + + @Override + public Map<String, Object> map(final AuthModuleTO authModuleTO, final KeycloakOIDCAuthModuleConf conf) { + Pac4jKeyCloakOidcClientProperties props = new Pac4jKeyCloakOidcClientProperties(); + map(authModuleTO, props, conf); + props.setRealm(conf.getRealm()); + props.setBaseUri(conf.getBaseUri()); + + Pac4jOidcClientProperties client = new Pac4jOidcClientProperties(); + client.setKeycloak(props); + + return prefix("cas.authn.pac4j.oidc[].keycloak.", CasCoreConfigurationUtils.asMap(props)); + } + + @Override + public Map<String, Object> map(final AuthModuleTO authModuleTO, final AppleOIDCAuthModuleConf conf) { + Pac4jAppleOidcClientProperties props = new Pac4jAppleOidcClientProperties(); + map(authModuleTO, props, conf); + props.setTimeout(conf.getTimeout()); + props.setPrivateKey(conf.getPrivateKey()); + props.setPrivateKeyId(conf.getPrivateKeyId()); + props.setTeamId(conf.getTeamId()); + + Pac4jOidcClientProperties client = new Pac4jOidcClientProperties(); + client.setApple(props); + + return prefix("cas.authn.pac4j.oidc[].apple.", CasCoreConfigurationUtils.asMap(props)); } @Override @@ -315,28 +383,4 @@ public class AuthModulePropertySourceMapper extends PropertySourceMapper impleme return prefix("cas.authn.mfa.simple.", CasCoreConfigurationUtils.asMap(props)); } - - @Override - public Map<String, Object> map(final AuthModuleTO authModuleTO, final AzureAuthModuleConf conf) { - Pac4jAzureOidcClientProperties props = new Pac4jAzureOidcClientProperties(); - props.setId(conf.getClientId()); - props.setSecret(conf.getClientSecret()); - props.setClientName(Optional.ofNullable(conf.getClientName()).orElse(authModuleTO.getKey())); - props.setEnabled(authModuleTO.getState() == AuthModuleState.ACTIVE); - props.setCustomParams(conf.getCustomParams()); - props.setDiscoveryUri(conf.getDiscoveryUri()); - props.setMaxClockSkew(conf.getMaxClockSkew()); - props.setPreferredJwsAlgorithm(conf.getPreferredJwsAlgorithm()); - props.setResponseMode(conf.getResponseMode()); - props.setResponseType(conf.getResponseType()); - props.setScope(conf.getScope()); - props.setPrincipalIdAttribute(conf.getUserIdAttribute()); - props.setExpireSessionWithToken(conf.isExpireSessionWithToken()); - props.setTokenExpirationAdvance(conf.getTokenExpirationAdvance()); - props.setTenant(conf.getTenant()); - Pac4jOidcClientProperties client = new Pac4jOidcClientProperties(); - client.setAzure(props); - - return prefix("cas.authn.pac4j.oidc[].azure.", CasCoreConfigurationUtils.asMap(props)); - } }