This is an automated email from the ASF dual-hosted git repository. fmartelli pushed a commit to branch SYNCOPE-1816 in repository https://gitbox.apache.org/repos/asf/syncope.git
commit 2f973e7c36a009afb3bb5b1be54506aae044e85a Author: fabio <[email protected]> AuthorDate: Thu Apr 18 10:53:56 2024 +0200 [SYNCOPE-1816] Provides the possibility to add a JcifsSpnegoAuthenticationHandler --- .../syncope/common/lib/auth/AuthModuleConf.java | 2 + .../common/lib/auth/JcifsSpnegoAuthModuleConf.java | 339 +++++++++++++++++++++ .../mapping/AuthModulePropertySourceMapper.java | 40 +++ 3 files changed, 381 insertions(+) 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 c5e1d36975..8414ba9a6e 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 @@ -61,6 +61,8 @@ 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, JcifsSpnegoAuthModuleConf conf); } Map<String, Object> map(AuthModuleTO authModule, Mapper mapper); diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/JcifsSpnegoAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/JcifsSpnegoAuthModuleConf.java new file mode 100644 index 0000000000..06ef008a91 --- /dev/null +++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/JcifsSpnegoAuthModuleConf.java @@ -0,0 +1,339 @@ +/* + * 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.io.Serializable; +import java.util.Map; +import org.apache.syncope.common.lib.AbstractLDAPConf; +import org.apache.syncope.common.lib.to.AuthModuleTO; + +/** + * SPNEGO is an authentication technology that is primarily used to provide transparent CAS authentication to browsers + * running on Windows running under Active Directory domain credentials. There are three actors involved: the client, + * the CAS server, and the Active Directory Domain Controller/KDC. + */ +public class JcifsSpnegoAuthModuleConf implements AuthModuleConf { + + private static final long serialVersionUID = -7775771400312303131L; + + public static class LDAP extends AbstractLDAPConf implements Serializable { + + private static final long serialVersionUID = -7274446267090678730L; + + } + + /** + * The Login conf.Absolute path to the jaas login configuration file. + * This should define the spnego authentication details. + * Make sure you have at least specified the JCIFS Service Principal defined. + */ + private String loginConf; + + /** + * The Kerberos conf. + * As with all Kerberos installations, a Kerberos Key Distribution Center (KDC) is required. + * It needs to contain the user name and password you will use to be authenticated to Kerberos. + * As with most Kerberos installations, a Kerberos configuration file krb5.conf is + * consulted to determine such things as the default realm and KDC. + * Typically, the default realm and the KDC for that realm are indicated in + * the Kerberos krb5.conf configuration file. + * The path to the configuration file must typically be defined + * as an absolute path. + */ + private String kerberosConf; + + /** + * The Kerberos kdc. + */ + private String kerberosKdc = "172.10.1.10"; + + /** + * The Kerberos realm. + */ + private String kerberosRealm = "EXAMPLE.COM"; + + /** + * The Kerberos debug. + */ + private boolean kerberosDebug; + + /** + * The Use subject creds only. + */ + private boolean useSubjectCredsOnly; + + /** + * If specified, will create the principal by ths name on successful authentication. + */ + private boolean principalWithDomainName; + + /** + * Allows authentication if spnego credential is marked as NTLM. + */ + private boolean ntlmAllowed = true; + + /** + * If the authenticated principal cannot be determined from the spegno credential, + * will set the http status code to 401. + */ + private boolean send401OnAuthenticationFailure = true; + + /** + * The bean id of a webflow action whose job is to evaluate the client host + * to see if the request is authorized for spnego. + * Supported strategies include {@code hostnameSpnegoClientAction} where + * CAS checks to see if the request’s remote hostname matches a predefine pattern. + * and {@code ldapSpnegoClientAction} where + * CAS checks an LDAP instance for the remote hostname, to locate a pre-defined attribute whose + * mere existence would allow the webflow to resume to SPNEGO. + */ + private String hostNameClientActionStrategy = "hostnameSpnegoClientAction"; + + /** + * LDAP settings for spnego to validate clients, etc. + */ + private LDAP ldap = new LDAP(); + + /** + * When validating clients, specifies the DNS timeout used to look up an address. + */ + private String dnsTimeout = "PT2S"; + + /** + * A regex pattern that indicates whether the client host name is allowed for spnego. + */ + private String hostNamePatternString = ".+"; + + /** + * A regex pattern that indicates whether the client IP is allowed for spnego. + */ + private String ipsToCheckPattern = "127.+"; + + /** + * Alternative header name to use in order to find the host address. + */ + private String alternativeRemoteHostAttribute = "alternateRemoteHeader"; + + /** + * In case LDAP is used to validate clients, this is the attribute that indicates the host. + */ + private String spnegoAttributeName = "distinguishedName"; + + /** + * Determines the header to set and the message prefix when negotiating spnego. + */ + private boolean ntlm; + + /** + * If true, does not terminate authentication and allows CAS to resume + * and fallback to normal authentication means such as uid/psw via the login page. + * If disallowed, considers spnego authentication to be final in the event of failures. + */ + private boolean mixedModeAuthentication; + + /** + * Begins negotiating spnego if the user-agent is one of the supported browsers. + */ + private String supportedBrowsers = "MSIE,Trident,Firefox,AppleWebKit"; + + /** + * The size of the pool used to validate SPNEGO tokens. + * A pool is used to provider better performance than what was previously offered by the simple Lombok + * {@code Synchronized} annotation. + */ + private int poolSize = 10; + + /** + * The timeout of the pool used to validate SPNEGO tokens. + */ + private String poolTimeout = "PT2S"; + + public String getLoginConf() { + return loginConf; + } + + public void setLoginConf(final String loginConf) { + this.loginConf = loginConf; + } + + public String getKerberosConf() { + return kerberosConf; + } + + public void setKerberosConf(final String kerberosConf) { + this.kerberosConf = kerberosConf; + } + + public String getKerberosKdc() { + return kerberosKdc; + } + + public void setKerberosKdc(final String kerberosKdc) { + this.kerberosKdc = kerberosKdc; + } + + public String getKerberosRealm() { + return kerberosRealm; + } + + public void setKerberosRealm(final String kerberosRealm) { + this.kerberosRealm = kerberosRealm; + } + + public boolean isKerberosDebug() { + return kerberosDebug; + } + + public void setKerberosDebug(final boolean kerberosDebug) { + this.kerberosDebug = kerberosDebug; + } + + public boolean isUseSubjectCredsOnly() { + return useSubjectCredsOnly; + } + + public void setUseSubjectCredsOnly(final boolean useSubjectCredsOnly) { + this.useSubjectCredsOnly = useSubjectCredsOnly; + } + + public boolean isPrincipalWithDomainName() { + return principalWithDomainName; + } + + public void setPrincipalWithDomainName(final boolean principalWithDomainName) { + this.principalWithDomainName = principalWithDomainName; + } + + public boolean isNtlmAllowed() { + return ntlmAllowed; + } + + public void setNtlmAllowed(final boolean ntlmAllowed) { + this.ntlmAllowed = ntlmAllowed; + } + + public boolean isSend401OnAuthenticationFailure() { + return send401OnAuthenticationFailure; + } + + public void setSend401OnAuthenticationFailure(final boolean send401OnAuthenticationFailure) { + this.send401OnAuthenticationFailure = send401OnAuthenticationFailure; + } + + public String getHostNameClientActionStrategy() { + return hostNameClientActionStrategy; + } + + public void setHostNameClientActionStrategy(final String hostNameClientActionStrategy) { + this.hostNameClientActionStrategy = hostNameClientActionStrategy; + } + + public LDAP getLdap() { + return ldap; + } + + public void setLdap(final LDAP ldap) { + this.ldap = ldap; + } + + public String getDnsTimeout() { + return dnsTimeout; + } + + public void setDnsTimeout(final String dnsTimeout) { + this.dnsTimeout = dnsTimeout; + } + + public String getHostNamePatternString() { + return hostNamePatternString; + } + + public void setHostNamePatternString(final String hostNamePatternString) { + this.hostNamePatternString = hostNamePatternString; + } + + public String getIpsToCheckPattern() { + return ipsToCheckPattern; + } + + public void setIpsToCheckPattern(final String ipsToCheckPattern) { + this.ipsToCheckPattern = ipsToCheckPattern; + } + + public String getAlternativeRemoteHostAttribute() { + return alternativeRemoteHostAttribute; + } + + public void setAlternativeRemoteHostAttribute(final String alternativeRemoteHostAttribute) { + this.alternativeRemoteHostAttribute = alternativeRemoteHostAttribute; + } + + public String getSpnegoAttributeName() { + return spnegoAttributeName; + } + + public void setSpnegoAttributeName(final String spnegoAttributeName) { + this.spnegoAttributeName = spnegoAttributeName; + } + + public boolean isNtlm() { + return ntlm; + } + + public void setNtlm(final boolean ntlm) { + this.ntlm = ntlm; + } + + public boolean isMixedModeAuthentication() { + return mixedModeAuthentication; + } + + public void setMixedModeAuthentication(final boolean mixedModeAuthentication) { + this.mixedModeAuthentication = mixedModeAuthentication; + } + + public String getSupportedBrowsers() { + return supportedBrowsers; + } + + public void setSupportedBrowsers(final String supportedBrowsers) { + this.supportedBrowsers = supportedBrowsers; + } + + public int getPoolSize() { + return poolSize; + } + + public void setPoolSize(final int poolSize) { + this.poolSize = poolSize; + } + + public String getPoolTimeout() { + return poolTimeout; + } + + public void setPoolTimeout(final String poolTimeout) { + this.poolTimeout = poolTimeout; + } + + @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/mapping/AuthModulePropertySourceMapper.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AuthModulePropertySourceMapper.java index b90e83f317..695f5f1a2a 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 @@ -33,6 +33,7 @@ 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.JcifsSpnegoAuthModuleConf; import org.apache.syncope.common.lib.auth.KeycloakOIDCAuthModuleConf; import org.apache.syncope.common.lib.auth.LDAPAuthModuleConf; import org.apache.syncope.common.lib.auth.OAuth20AuthModuleConf; @@ -69,6 +70,8 @@ import org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jGoogleOidcClie 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.spnego.SpnegoLdapProperties; +import org.apereo.cas.configuration.model.support.spnego.SpnegoProperties; import org.apereo.cas.configuration.model.support.syncope.SyncopeAuthenticationProperties; import org.apereo.cas.configuration.model.support.x509.SubjectDnPrincipalResolverProperties.SubjectDnFormat; import org.apereo.cas.configuration.model.support.x509.X509LdapProperties; @@ -466,4 +469,41 @@ 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 JcifsSpnegoAuthModuleConf conf) { + SpnegoProperties props = new SpnegoProperties(); + props.setName(authModuleTO.getKey()); + props.setOrder(authModuleTO.getOrder()); + + props.setMixedModeAuthentication(conf.isMixedModeAuthentication()); + props.setIpsToCheckPattern(conf.getIpsToCheckPattern()); + props.setSend401OnAuthenticationFailure(conf.isSend401OnAuthenticationFailure()); + props.setAlternativeRemoteHostAttribute(conf.getAlternativeRemoteHostAttribute()); + props.setDnsTimeout(conf.getDnsTimeout()); + props.setHostNameClientActionStrategy(conf.getHostNameClientActionStrategy()); + props.setHostNamePatternString(conf.getHostNamePatternString()); + props.setNtlm(conf.isNtlm()); + props.setNtlmAllowed(conf.isNtlmAllowed()); + props.setPoolSize(conf.getPoolSize()); + props.setPoolTimeout(conf.getPoolTimeout()); + props.setPrincipalWithDomainName(conf.isPrincipalWithDomainName()); + props.setSpnegoAttributeName(conf.getSpnegoAttributeName()); + props.setSupportedBrowsers(conf.getSupportedBrowsers()); + + props.getSystem().setUseSubjectCredsOnly(conf.isUseSubjectCredsOnly()); + props.getSystem().setLoginConf(conf.getLoginConf()); + props.getSystem().setKerberosKdc(conf.getKerberosKdc()); + props.getSystem().setKerberosRealm(conf.getKerberosRealm()); + props.getSystem().setKerberosConf(conf.getKerberosConf()); + props.getSystem().setKerberosDebug(conf.isKerberosDebug() ? Boolean.TRUE.toString() : Boolean.FALSE.toString()); + + if (conf.getLdap() != null) { + SpnegoLdapProperties ldapProps = new SpnegoLdapProperties(); + fill(ldapProps, conf.getLdap()); + props.setLdap(ldapProps); + } + + return prefix("cas.authn.spnego.", CasCoreConfigurationUtils.asMap(props)); + } }
