You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2023/03/16 12:55:26 UTC
[syncope] 02/02: [SYNCOPE-1741] Provides support for Azure Active Directory delegated authentication (#427)
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
commit 72ab59ebcf8edb0e2e595692c1e4b1df8f697d8e
Author: Fabio Martelli <fa...@tirasa.net>
AuthorDate: Thu Mar 16 13:50:13 2023 +0100
[SYNCOPE-1741] Provides support for Azure Active Directory delegated authentication (#427)
---
...onf.java => AbstractOAuth20AuthModuleConf.java} | 2 +-
.../lib/auth/AbstractOIDCAuthModuleConf.java | 119 +++++++++++----------
.../syncope/common/lib/auth/AuthModuleConf.java | 2 +
...uthModuleConf.java => AzureAuthModuleConf.java} | 53 +++------
.../common/lib/auth/OAuth20AuthModuleConf.java | 15 ++-
.../common/lib/auth/OIDCAuthModuleConf.java | 67 +-----------
.../concepts/authenticationmodules.adoc | 2 +
.../bootstrap/AuthModulePropertySourceMapper.java | 28 +++++
8 files changed, 128 insertions(+), 160 deletions(-)
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AbstractOIDCAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AbstractOAuth20AuthModuleConf.java
similarity index 97%
copy from common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AbstractOIDCAuthModuleConf.java
copy to common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AbstractOAuth20AuthModuleConf.java
index 6af85787c4..c5478e7b9a 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AbstractOIDCAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AbstractOAuth20AuthModuleConf.java
@@ -21,7 +21,7 @@ package org.apache.syncope.common.lib.auth;
import java.util.LinkedHashMap;
import java.util.Map;
-public abstract class AbstractOIDCAuthModuleConf extends Pac4jAuthModuleConf {
+public abstract class AbstractOAuth20AuthModuleConf extends Pac4jAuthModuleConf {
private static final long serialVersionUID = -4716170241796764061L;
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AbstractOIDCAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AbstractOIDCAuthModuleConf.java
index 6af85787c4..d4a53dec2f 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AbstractOIDCAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AbstractOIDCAuthModuleConf.java
@@ -18,94 +18,105 @@
*/
package org.apache.syncope.common.lib.auth;
-import java.util.LinkedHashMap;
-import java.util.Map;
+public abstract class AbstractOIDCAuthModuleConf extends AbstractOAuth20AuthModuleConf {
-public abstract class AbstractOIDCAuthModuleConf extends Pac4jAuthModuleConf {
+ private static final long serialVersionUID = -471527731042579422L;
- private static final long serialVersionUID = -4716170241796764061L;
+ protected String discoveryUri;
- protected String clientId;
+ /**
+ * Whether an initial nonce should be to used
+ * initially for replay attack mitigation.
+ */
+ protected boolean useNonce;
- protected String clientSecret;
+ /**
+ * The JWS algorithm to use forcefully when validating ID tokens.
+ * If none is defined, the first algorithm from metadata will be used.
+ */
+ protected String preferredJwsAlgorithm;
- protected boolean enabled;
+ /**
+ * Clock skew in order to account for drift, when validating id tokens.
+ */
+ protected String maxClockSkew;
- protected Map<String, String> customParams = new LinkedHashMap<>();
+ /**
+ * The response mode specifies how the result of the authorization request is formatted.
+ * Possible values includes "query", "fragment", "form_post", or "web_message"
+ */
+ protected String responseMode;
- protected String tokenUrl;
+ /**
+ * Checks if sessions expire with token expiration.
+ */
+ protected boolean expireSessionWithToken;
- protected String responseType = "code";
+ /**
+ * Default time period advance (in seconds) for considering an access token expired.
+ * This settings supports the java.time.Duration syntax.
+ * The format of the value will be PTnHnMnS, where n is the relevant hours, minutes or
+ * seconds part of the duration. Any fractional seconds are placed after a decimal point in the seconds section.
+ * If a section has a zero value, it is omitted. The hours, minutes and seconds will all have the same sign.
+ * Example values could be in the form of PT20S, PT15M, PT10H, PT6D, P2DT3H4M.
+ * If the value is set to 0 or never, the duration will be set to zero. If the value is blank, set to -1, or
+ * infinite, the value will effectively represent an unending duration.
+ */
+ protected String tokenExpirationAdvance;
- protected String scope;
-
- protected String userIdAttribute;
-
- public String getClientId() {
- return clientId;
- }
-
- public void setId(final String clientId) {
- this.clientId = clientId;
- }
-
- public String getClientSecret() {
- return clientSecret;
- }
-
- public void setClientSecret(final String clientSecret) {
- this.clientSecret = clientSecret;
+ public String getDiscoveryUri() {
+ return discoveryUri;
}
- public boolean isEnabled() {
- return enabled;
+ public void setDiscoveryUri(final String discoveryUri) {
+ this.discoveryUri = discoveryUri;
}
- public void setEnabled(final boolean enabled) {
- this.enabled = enabled;
+ public boolean isUseNonce() {
+ return useNonce;
}
- public Map<String, String> getCustomParams() {
- return customParams;
+ public void setUseNonce(final boolean useNonce) {
+ this.useNonce = useNonce;
}
- public void setCustomParams(final Map<String, String> customParams) {
- this.customParams = customParams;
+ public String getPreferredJwsAlgorithm() {
+ return preferredJwsAlgorithm;
}
- public String getTokenUrl() {
- return tokenUrl;
+ public void setPreferredJwsAlgorithm(final String preferredJwsAlgorithm) {
+ this.preferredJwsAlgorithm = preferredJwsAlgorithm;
}
- public void setTokenUrl(final String tokenUrl) {
- this.tokenUrl = tokenUrl;
+ public String getMaxClockSkew() {
+ return maxClockSkew;
}
- public String getResponseType() {
- return responseType;
+ public void setMaxClockSkew(final String maxClockSkew) {
+ this.maxClockSkew = maxClockSkew;
}
- public void setResponseType(final String responseType) {
- this.responseType = responseType;
+ public String getResponseMode() {
+ return responseMode;
}
- public String getScope() {
- return scope;
+ public void setResponseMode(final String responseMode) {
+ this.responseMode = responseMode;
}
- public void setScope(final String scope) {
- this.scope = scope;
+ public boolean isExpireSessionWithToken() {
+ return expireSessionWithToken;
}
- public void setClientId(final String clientId) {
- this.clientId = clientId;
+ public void setExpireSessionWithToken(final boolean expireSessionWithToken) {
+ this.expireSessionWithToken = expireSessionWithToken;
}
- public String getUserIdAttribute() {
- return userIdAttribute;
+ public String getTokenExpirationAdvance() {
+ return tokenExpirationAdvance;
}
- public void setUserIdAttribute(final String userIdAttribute) {
- this.userIdAttribute = userIdAttribute;
+ public void setTokenExpirationAdvance(final String tokenExpirationAdvance) {
+ this.tokenExpirationAdvance = tokenExpirationAdvance;
}
}
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 c41588b85d..2cca813cdf 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
@@ -51,6 +51,8 @@ public interface AuthModuleConf extends BaseBean {
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/OAuth20AuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureAuthModuleConf.java
similarity index 53%
copy from common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OAuth20AuthModuleConf.java
copy to common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureAuthModuleConf.java
index 86fbde2282..c5cb26a85d 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OAuth20AuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureAuthModuleConf.java
@@ -21,52 +21,31 @@ package org.apache.syncope.common.lib.auth;
import java.util.Map;
import org.apache.syncope.common.lib.to.AuthModuleTO;
-public class OAuth20AuthModuleConf extends AbstractOIDCAuthModuleConf implements AuthModuleConf {
+public class AzureAuthModuleConf extends AbstractOIDCAuthModuleConf implements AuthModuleConf {
- private static final long serialVersionUID = 299820485764241682L;
+ private static final long serialVersionUID = -471527731042579522L;
- protected String authUrl;
+ /**
+ * 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;
- protected String profileUrl;
-
- protected boolean withState;
-
- protected String profileVerb = "POST";
-
- public String getAuthUrl() {
- return authUrl;
- }
-
- public void setAuthUrl(final String authUrl) {
- this.authUrl = authUrl;
- }
-
- public boolean isWithState() {
- return withState;
- }
-
- public void setWithState(final boolean withState) {
- this.withState = withState;
- }
-
- public String getProfileUrl() {
- return profileUrl;
- }
-
- public void setProfileUrl(final String profileUrl) {
- this.profileUrl = profileUrl;
- }
-
- public String getProfileVerb() {
- return profileVerb;
+ public String getTenant() {
+ return tenant;
}
- public void setProfileVerb(final String profileVerb) {
- this.profileVerb = profileVerb;
+ 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/OAuth20AuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OAuth20AuthModuleConf.java
index 86fbde2282..9b04b1fe8c 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OAuth20AuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OAuth20AuthModuleConf.java
@@ -18,16 +18,19 @@
*/
package org.apache.syncope.common.lib.auth;
+import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.syncope.common.lib.to.AuthModuleTO;
-public class OAuth20AuthModuleConf extends AbstractOIDCAuthModuleConf implements AuthModuleConf {
+public class OAuth20AuthModuleConf extends AbstractOAuth20AuthModuleConf implements AuthModuleConf {
private static final long serialVersionUID = 299820485764241682L;
protected String authUrl;
-
+
protected String profileUrl;
+
+ protected Map<String, String> profileAttrs = new LinkedHashMap<>();
protected boolean withState;
@@ -41,6 +44,14 @@ public class OAuth20AuthModuleConf extends AbstractOIDCAuthModuleConf implements
this.authUrl = authUrl;
}
+ public Map<String, String> getProfileAttrs() {
+ return profileAttrs;
+ }
+
+ public void setProfileAttrs(final Map<String, String> profileAttrs) {
+ this.profileAttrs = profileAttrs;
+ }
+
public boolean isWithState() {
return withState;
}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OIDCAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OIDCAuthModuleConf.java
index 1419e87730..0ea9339c3b 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OIDCAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OIDCAuthModuleConf.java
@@ -23,72 +23,7 @@ import org.apache.syncope.common.lib.to.AuthModuleTO;
public class OIDCAuthModuleConf extends AbstractOIDCAuthModuleConf implements AuthModuleConf {
- private static final long serialVersionUID = -471527731042579422L;
-
- protected String discoveryUri;
-
- /**
- * Whether an initial nonce should be to used
- * initially for replay attack mitigation.
- */
- protected boolean useNonce;
-
- /**
- * The JWS algorithm to use forcefully when validating ID tokens.
- * If none is defined, the first algorithm from metadata will be used.
- */
- protected String preferredJwsAlgorithm;
-
- /**
- * Clock skew in order to account for drift, when validating id tokens.
- */
- protected String maxClockSkew;
-
- /**
- * The response mode specifies how the result of the authorization request is formatted.
- * Possible values includes "query", "fragment", "form_post", or "web_message"
- */
- protected String responseMode;
-
- public String getDiscoveryUri() {
- return discoveryUri;
- }
-
- public void setDiscoveryUri(final String discoveryUri) {
- this.discoveryUri = discoveryUri;
- }
-
- public boolean isUseNonce() {
- return useNonce;
- }
-
- public void setUseNonce(final boolean useNonce) {
- this.useNonce = useNonce;
- }
-
- public String getPreferredJwsAlgorithm() {
- return preferredJwsAlgorithm;
- }
-
- public void setPreferredJwsAlgorithm(final String preferredJwsAlgorithm) {
- this.preferredJwsAlgorithm = preferredJwsAlgorithm;
- }
-
- public String getMaxClockSkew() {
- return maxClockSkew;
- }
-
- public void setMaxClockSkew(final String maxClockSkew) {
- this.maxClockSkew = maxClockSkew;
- }
-
- public String getResponseMode() {
- return responseMode;
- }
-
- public void setResponseMode(final String responseMode) {
- this.responseMode = responseMode;
- }
+ private static final long serialVersionUID = -471527711042579422L;
@Override
public Map<String, Object> map(final AuthModuleTO authModule, final Mapper mapper) {
diff --git a/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc b/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc
index 3d0cc4e61c..3e3e5c40ba 100644
--- a/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc
@@ -28,9 +28,11 @@ Several authentication modules are provided:
** https://apereo.github.io/cas/6.6.x/authentication/JAAS-Authentication.html[JAAS^]
** https://apereo.github.io/cas/6.6.x/authentication/LDAP-Authentication.html[LDAP^]
** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication.html[OpenID Connect^]
+ ** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication.html[OAuth2^]
** https://apereo.github.io/cas/6.6.x/authentication/Syncope-Authentication.html[Static^]
** https://apereo.github.io/cas/6.6.x/authentication/Syncope-Authentication.html[Syncope^]
** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication.html[SAML^]
+ ** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication-Azure-AD.html[Azure Active Directory^]
* MFA:
** https://apereo.github.io/cas/6.6.x/mfa/DuoSecurity-Authentication.html[Duo Security^]
** https://apereo.github.io/cas/6.6.x/mfa/FIDO-U2F-Authentication.html[Fido U2F^]
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 04386f5606..a40b63086e 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
@@ -26,6 +26,7 @@ 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.AuthModuleConf;
+import org.apache.syncope.common.lib.auth.AzureAuthModuleConf;
import org.apache.syncope.common.lib.auth.DuoMfaAuthModuleConf;
import org.apache.syncope.common.lib.auth.GoogleMfaAuthModuleConf;
import org.apache.syncope.common.lib.auth.JDBCAuthModuleConf;
@@ -55,6 +56,7 @@ 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.Pac4jAzureOidcClientProperties;
import org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jGenericOidcClientProperties;
import org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jOidcClientProperties;
import org.apereo.cas.configuration.model.support.pac4j.saml.Pac4jSamlClientProperties;
@@ -161,6 +163,8 @@ public class AuthModulePropertySourceMapper extends PropertySourceMapper impleme
props.setResponseType(conf.getResponseType());
props.setScope(conf.getScope());
props.setPrincipalIdAttribute(conf.getUserIdAttribute());
+ props.setExpireSessionWithToken(conf.isExpireSessionWithToken());
+ props.setTokenExpirationAdvance(conf.getTokenExpirationAdvance());
Pac4jOidcClientProperties client = new Pac4jOidcClientProperties();
client.setGeneric(props);
@@ -311,4 +315,28 @@ 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));
+ }
}