diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/AbstractAzureActiveDirectoryConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/AbstractAzureActiveDirectoryConf.java new file mode 100644 index 00000000000..d0255ed536f --- /dev/null +++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/AbstractAzureActiveDirectoryConf.java @@ -0,0 +1,119 @@ +/* + * 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; + +import java.io.Serializable; + +public abstract class AbstractAzureActiveDirectoryConf implements Serializable { + + private static final long serialVersionUID = 282571926999684266L; + + private String clientId; + + private String clientSecret; + + /** + * This URL of the security token service that CAS goes to for acquiring tokens for resources and users. + * This URL allows CAS to establish what is called an 'authority'. + * You can think of the authority as the directory issuing the identities/tokens. The login URL here is then + * composed of {@code https:///}, where 'instance' is the Azure AD host + * (such as {@code https://login.microsoftonline.com}) and 'tenant' is the domain name + * (such as {@code contoso.onmicrosoft.com}) or tenant ID of the directory. + * Examples of authority URL are: + * + * + */ + private String loginUrl = "https://login.microsoftonline.com/common/"; + + /** + * Resource url for the graph API to fetch attributes. + */ + private String resource = "https://graph.microsoft.com/"; + + /** + * The microsoft tenant id. + */ + private String tenant; + + /** + * Scope used when fetching access tokens. + * Multiple scopes may be separated using a comma. + */ + private String scope = "openid,email,profile,address"; + + public String getClientId() { + return clientId; + } + + public void setClientId(final String clientId) { + this.clientId = clientId; + } + + public String getClientSecret() { + return clientSecret; + } + + public void setClientSecret(final String clientSecret) { + this.clientSecret = clientSecret; + } + + public String getLoginUrl() { + return loginUrl; + } + + public void setLoginUrl(final String loginUrl) { + this.loginUrl = loginUrl; + } + + public String getResource() { + return resource; + } + + public void setResource(final String resource) { + this.resource = resource; + } + + public String getTenant() { + return tenant; + } + + public void setTenant(final String tenant) { + this.tenant = tenant; + } + + public String getScope() { + return scope; + } + + public void setScope(final String scope) { + this.scope = scope; + } +} diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/AttrRepoConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/AttrRepoConf.java index 18edec4b4e3..682d875f946 100644 --- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/AttrRepoConf.java +++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/AttrRepoConf.java @@ -35,6 +35,8 @@ interface Mapper { Map map(AttrRepoTO attrRepo, JDBCAttrRepoConf conf); Map map(AttrRepoTO attrRepo, SyncopeAttrRepoConf conf); + + Map map(AttrRepoTO attrRepo, AzureActiveDirectoryAttrRepoConf conf); } Map map(AttrRepoTO attrRepo, Mapper mapper); diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/AzureActiveDirectoryAttrRepoConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/AzureActiveDirectoryAttrRepoConf.java new file mode 100644 index 00000000000..11d657a280d --- /dev/null +++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/AzureActiveDirectoryAttrRepoConf.java @@ -0,0 +1,46 @@ +/* + * 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.attr; + +import java.util.Map; +import org.apache.syncope.common.lib.AbstractAzureActiveDirectoryConf; +import org.apache.syncope.common.lib.to.AttrRepoTO; + +public class AzureActiveDirectoryAttrRepoConf extends AbstractAzureActiveDirectoryConf implements AttrRepoConf { + + private static final long serialVersionUID = -2365294132437794196L; + + /** + * Whether attribute repository should consider the underlying attribute names in a case-insensitive manner. + */ + private boolean caseInsensitive; + + public boolean isCaseInsensitive() { + return caseInsensitive; + } + + public void setCaseInsensitive(final boolean caseInsensitive) { + this.caseInsensitive = caseInsensitive; + } + + @Override + public Map map(final AttrRepoTO attrRepo, final Mapper mapper) { + return mapper.map(attrRepo, 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 1f20f073dfd..265b4b457b3 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 @@ -52,6 +52,8 @@ interface Mapper { Map map(AuthModuleTO authModule, SyncopeAuthModuleConf conf); + Map map(AuthModuleTO authModule, AzureActiveDirectoryAuthModuleConf conf); + Map map(AuthModuleTO authModule, X509AuthModuleConf conf); Map map(AuthModuleTO authModule, GoogleMfaAuthModuleConf conf); diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureActiveDirectoryAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureActiveDirectoryAuthModuleConf.java new file mode 100644 index 00000000000..5c0933a7462 --- /dev/null +++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AzureActiveDirectoryAuthModuleConf.java @@ -0,0 +1,52 @@ +/* + * 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.AbstractAzureActiveDirectoryConf; +import org.apache.syncope.common.lib.to.AuthModuleTO; + +public class AzureActiveDirectoryAuthModuleConf extends AbstractAzureActiveDirectoryConf implements AuthModuleConf { + + private static final long serialVersionUID = 6053163884651768614L; + + /** + * A number of authentication handlers are allowed to determine whether they can operate on the provided credential + * and as such lend themselves to be tried and tested during the authentication handler selection phase. + * The credential criteria may be one of the following options:
    + *
  • A regular expression pattern that is tested against the credential identifier.
  • + *
  • A fully qualified class name of your own design that implements {@code Predicate}.
  • + *
  • Path to an external Groovy script that implements the same interface.
  • + *
+ */ + private String credentialCriteria; + + public String getCredentialCriteria() { + return credentialCriteria; + } + + public void setCredentialCriteria(final String credentialCriteria) { + this.credentialCriteria = credentialCriteria; + } + + @Override + public Map map(final AuthModuleTO authModule, final Mapper mapper) { + return mapper.map(authModule, this); + } +} diff --git a/src/main/asciidoc/reference-guide/concepts/attributerepositories.adoc b/src/main/asciidoc/reference-guide/concepts/attributerepositories.adoc index c8299a71370..26da7b278fa 100644 --- a/src/main/asciidoc/reference-guide/concepts/attributerepositories.adoc +++ b/src/main/asciidoc/reference-guide/concepts/attributerepositories.adoc @@ -27,6 +27,7 @@ Some attribute repositories are provided: * https://apereo.github.io/cas/6.6.x/integration/Attribute-Resolution-LDAP.html[LDAP^] * https://apereo.github.io/cas/6.6.x/integration/Attribute-Resolution-Stub.html[Stub^] * https://apereo.github.io/cas/6.6.x/integration/Attribute-Resolution-Syncope.html[Syncope^] +* https://apereo.github.io/cas/6.6.x/integration/Attribute-Resolution-AzureAD.html[Azure Active Directory^] [TIP] ==== diff --git a/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc b/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc index f99e87a2e64..e45ea041d2e 100644 --- a/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc +++ b/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc @@ -29,12 +29,13 @@ Several authentication modules are provided: ** https://apereo.github.io/cas/6.6.x/authentication/LDAP-Authentication.html[LDAP^] ** https://apereo.github.io/cas/6.6.x/authentication/SPNEGO-Authentication.html[SPNEGO^] ** https://apereo.github.io/cas/6.6.x/authentication/Syncope-Authentication.html[Syncope^] + ** https://apereo.github.io/cas/6.6.x/authentication/Azure-ActiveDirectory-Authentication.html[Azure Active Directory^] ** https://apereo.github.io/cas/6.6.x/authentication/X509-Authentication.html[X509^] ** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication-Generic-OpenID-Connect.html[OpenID Connect^] ** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication-OAuth20.html[OAuth2^] ** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication-SAML.htmll[SAML^] ** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication-Apple.html[Apple Signin^] - ** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication-Azure-AD.html[Azure Active Directory^] + ** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication-Azure-AD.html[Azure Active Directory (OIDC)^] ** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication-Google-OpenID-Connect.html[Google OpenID^] ** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication-Keycloak.html[Keycloak^] * MFA: diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AttrRepoPropertySourceMapper.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AttrRepoPropertySourceMapper.java index b24ef8d0649..a42880a42b2 100644 --- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AttrRepoPropertySourceMapper.java +++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AttrRepoPropertySourceMapper.java @@ -23,6 +23,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.syncope.client.lib.SyncopeClient; import org.apache.syncope.common.lib.attr.AttrRepoConf; +import org.apache.syncope.common.lib.attr.AzureActiveDirectoryAttrRepoConf; import org.apache.syncope.common.lib.attr.JDBCAttrRepoConf; import org.apache.syncope.common.lib.attr.LDAPAttrRepoConf; import org.apache.syncope.common.lib.attr.StubAttrRepoConf; @@ -32,6 +33,7 @@ import org.apache.syncope.wa.bootstrap.WARestClient; import org.apereo.cas.configuration.model.core.authentication.AttributeRepositoryStates; import org.apereo.cas.configuration.model.core.authentication.StubPrincipalAttributesProperties; +import org.apereo.cas.configuration.model.support.azuread.AzureActiveDirectoryAttributesProperties; import org.apereo.cas.configuration.model.support.jdbc.JdbcPrincipalAttributesProperties; import org.apereo.cas.configuration.model.support.ldap.LdapPrincipalAttributesProperties; import org.apereo.cas.configuration.model.support.syncope.SyncopePrincipalAttributesProperties; @@ -115,4 +117,21 @@ public Map map(final AttrRepoTO attrRepoTO, final SyncopeAttrRep return prefix("cas.authn.attribute-repository.syncope.", WAConfUtils.asMap(props)); } + + @Override + public Map map(final AttrRepoTO attrRepoTO, final AzureActiveDirectoryAttrRepoConf conf) { + AzureActiveDirectoryAttributesProperties props = new AzureActiveDirectoryAttributesProperties(); + props.setId(attrRepoTO.getKey()); + props.setOrder(attrRepoTO.getOrder()); + props.setClientId(conf.getClientId()); + props.setClientSecret(conf.getClientSecret()); + props.setLoginBaseUrl(conf.getLoginUrl()); + props.setResource(conf.getResource()); + props.setTenant(conf.getTenant()); + props.setScope(conf.getScope()); + props.setCaseInsensitive(conf.isCaseInsensitive()); + props.setAttributes(attrRepoTO.getItems().stream().map(Item::getExtAttrName).collect(Collectors.joining(","))); + + return prefix("cas.authn.attribute-repository.azure-active-directory[].", WAConfUtils.asMap(props)); + } } diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AuthModulePropertySourceMapper.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AuthModulePropertySourceMapper.java index a975bc4ad3d..36a0f061b7b 100644 --- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AuthModulePropertySourceMapper.java +++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AuthModulePropertySourceMapper.java @@ -28,6 +28,7 @@ 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.AzureActiveDirectoryAuthModuleConf; import org.apache.syncope.common.lib.auth.AzureOIDCAuthModuleConf; import org.apache.syncope.common.lib.auth.DuoMfaAuthModuleConf; import org.apache.syncope.common.lib.auth.GoogleMfaAuthModuleConf; @@ -49,6 +50,7 @@ import org.apache.syncope.common.lib.types.AuthModuleState; import org.apache.syncope.wa.bootstrap.WARestClient; import org.apereo.cas.configuration.model.core.authentication.AuthenticationHandlerStates; +import org.apereo.cas.configuration.model.support.azuread.AzureActiveDirectoryAuthenticationProperties; import org.apereo.cas.configuration.model.support.generic.AcceptAuthenticationProperties; import org.apereo.cas.configuration.model.support.jaas.JaasAuthenticationProperties; import org.apereo.cas.configuration.model.support.jdbc.authn.QueryJdbcAuthenticationProperties; @@ -394,6 +396,23 @@ public Map map(final AuthModuleTO authModuleTO, final SyncopeAut return prefix("cas.authn.syncope.", WAConfUtils.asMap(props)); } + @Override + public Map map(final AuthModuleTO authModuleTO, final AzureActiveDirectoryAuthModuleConf conf) { + AzureActiveDirectoryAuthenticationProperties props = new AzureActiveDirectoryAuthenticationProperties(); + props.setName(authModuleTO.getKey()); + props.setOrder(authModuleTO.getOrder()); + props.setState(AuthenticationHandlerStates.valueOf(authModuleTO.getState().name())); + props.setLoginUrl(conf.getLoginUrl()); + props.setResource(conf.getResource()); + props.setClientId(conf.getClientId()); + props.setClientSecret(conf.getClientSecret()); + props.setTenant(conf.getTenant()); + props.setScope(conf.getScope()); + props.setCredentialCriteria(conf.getCredentialCriteria()); + + return prefix("cas.authn.azure-active-directory.", WAConfUtils.asMap(props)); + } + @Override public Map map(final AuthModuleTO authModuleTO, final GoogleMfaAuthModuleConf conf) { GoogleAuthenticatorMultifactorProperties props = new GoogleAuthenticatorMultifactorProperties();