[SYNCOPE-1816] Provides the possibility to add a JcifsSpnegoAuthenticationHandler (#691)
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 6d476ea..0e1208d 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
@@ -59,6 +59,8 @@
Map<String, Object> map(AuthModuleTO authModule, DuoMfaAuthModuleConf 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 0000000..06ef008
--- /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 05e8655..5b619a5 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.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;
@@ -66,6 +67,8 @@
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;
@@ -443,4 +446,41 @@
return prefix("cas.authn.mfa.simple.", WAConfUtils.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));
+ }
}