This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch 3_0_X
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/3_0_X by this push:
     new 7965e3e9e7 [SYNCOPE-1789] WA: adding support for X509 auth
7965e3e9e7 is described below

commit 7965e3e9e73d5568d9bb4b683530259456d251c5
Author: Francesco Chicchiriccò <ilgro...@apache.org>
AuthorDate: Thu Nov 16 13:15:35 2023 +0100

    [SYNCOPE-1789] WA: adding support for X509 auth
---
 .../syncope/common/lib/auth/AuthModuleConf.java    |   2 +
 .../common/lib/auth/OAuth20AuthModuleConf.java     |   4 +-
 .../common/lib/auth/X509AuthModuleConf.java        | 523 +++++++++++++++++++++
 .../common/lib/types/X509PolicySetting.java        |  35 ++
 .../common/lib/types/X509PrincipalType.java        |  59 +++
 .../lib/types/X509RevocationCheckerType.java       |  38 ++
 .../lib/types/X509RevocationFetcherType.java       |  32 ++
 .../common/lib/types/X509SubjectDnFormat.java      |  47 ++
 .../concepts/authenticationmodules.adoc            |  11 +-
 .../mapping/AuthModulePropertySourceMapper.java    |  77 +++
 wa/starter/pom.xml                                 |   4 +
 11 files changed, 826 insertions(+), 6 deletions(-)

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 8bc5a4fa6d..c5e1d36975 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 @@ public interface AuthModuleConf extends BaseBean {
 
         Map<String, Object> map(AuthModuleTO authModule, SyncopeAuthModuleConf 
conf);
 
+        Map<String, Object> map(AuthModuleTO authModule, X509AuthModuleConf 
conf);
+
         Map<String, Object> map(AuthModuleTO authModule, 
GoogleMfaAuthModuleConf conf);
 
         Map<String, Object> map(AuthModuleTO authModule, DuoMfaAuthModuleConf 
conf);
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 7bf34ae855..dfbe574ddd 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
@@ -27,9 +27,9 @@ public class OAuth20AuthModuleConf extends 
AbstractOAuth20AuthModuleConf impleme
     private static final long serialVersionUID = 299820485764241682L;
 
     protected String authUrl;
-    
+
     protected String profileUrl;
-    
+
     protected Map<String, String> profileAttrs = new LinkedHashMap<>();
 
     protected boolean withState;
diff --git 
a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/X509AuthModuleConf.java
 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/X509AuthModuleConf.java
new file mode 100644
index 0000000000..73d19106f7
--- /dev/null
+++ 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/X509AuthModuleConf.java
@@ -0,0 +1,523 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import org.apache.syncope.common.lib.AbstractLDAPConf;
+import org.apache.syncope.common.lib.to.AuthModuleTO;
+import org.apache.syncope.common.lib.types.X509PolicySetting;
+import org.apache.syncope.common.lib.types.X509PrincipalType;
+import org.apache.syncope.common.lib.types.X509RevocationCheckerType;
+import org.apache.syncope.common.lib.types.X509RevocationFetcherType;
+import org.apache.syncope.common.lib.types.X509SubjectDnFormat;
+
+public class X509AuthModuleConf implements AuthModuleConf {
+
+    private static final long serialVersionUID = 1915254775199296906L;
+
+    public static class LDAP extends AbstractLDAPConf implements Serializable {
+
+        private static final long serialVersionUID = -7274446267090678730L;
+
+        /**
+         * The LDAP attribute that holds the certificate revocation list.
+         */
+        private String certificateAttribute = "certificateRevocationList";
+
+        public String getCertificateAttribute() {
+            return certificateAttribute;
+        }
+
+        public void setCertificateAttribute(final String certificateAttribute) 
{
+            this.certificateAttribute = certificateAttribute;
+        }
+    }
+
+    /**
+     * The authentication handler name.
+     */
+    private String name;
+
+    /**
+     * The order of the authentication handler in the chain.
+     */
+    private int order = Integer.MAX_VALUE;
+
+    /**
+     * Threshold value if expired CRL revocation policy is to be handled via 
threshold.
+     */
+    private int revocationPolicyThreshold = 172_800;
+
+    /**
+     * Whether revocation checking should check all resources, or stop at 
first one.
+     */
+    private boolean checkAll;
+
+    /**
+     * The refresh interval of the internal scheduler in cases where CRL 
revocation checking
+     * is done via resources.
+     */
+    private int refreshIntervalSeconds = 3_600;
+
+    /**
+     * When CRL revocation checking is done via distribution points,
+     * decide if fetch failures should throw errors.
+     */
+    private boolean throwOnFetchFailure;
+
+    private X509PrincipalType principalType = X509PrincipalType.SUBJECT_DN;
+
+    /**
+     * Relevant for {@code CN_EDIPI}, {@code RFC822_EMAIL}, {@code SUBJECT}, 
{@code SUBJECT_ALT_NAME} principal types.
+     */
+    private String principalAlternateAttribute;
+
+    /**
+     * Relevant for {@code SUBJECT_DN} principal type.
+     */
+    private X509SubjectDnFormat principalTypeSubjectDnFormat = 
X509SubjectDnFormat.DEFAULT;
+
+    /**
+     * Relevant for {@code SERIAL_NO_DN} principal type.
+     * The serial number prefix used for principal resolution.
+     */
+    private String principalTypeSerialNoDnSerialNumberPrefix = "SERIALNUMBER=";
+
+    /**
+     * Relevant for {@code SERIAL_NO_DN} principal type.
+     * Value delimiter used for principal resolution.
+     */
+    private String principalTypeSerialNoDnValueDelimiter = ", ";
+
+    /**
+     * Relevant for {@code SERIAL_NO} principal type.
+     * Radix used.
+     */
+    private int principalTypeSerialNoSNRadix;
+
+    /**
+     * Relevant for {@code SERIAL_NO} principal type.
+     * If radix hex padding should be used.
+     */
+    private boolean principalTypeSerialNoHexSNZeroPadding;
+
+    /**
+     * Revocation certificate checking is carried out according to this 
setting.
+     */
+    private X509RevocationCheckerType revocationChecker = 
X509RevocationCheckerType.NONE;
+
+    /**
+     * Options to describe how to fetch CRL resources.
+     */
+    private X509RevocationFetcherType crlFetcher = 
X509RevocationFetcherType.RESOURCE;
+
+    /**
+     * List of CRL resources to use for fetching.
+     */
+    private final List<String> crlResources = new ArrayList<>(0);
+
+    /**
+     * When CRLs are cached, indicate maximum number of elements kept in 
memory.
+     */
+    private int cacheMaxElementsInMemory = 1_000;
+
+    /**
+     * When CRLs are cached, indicate whether cache should overflow to disk.
+     */
+    private boolean cacheDiskOverflow;
+
+    /**
+     * Size of cache on disk.
+     */
+    private String cacheDiskSize = "100MB";
+
+    /**
+     * When CRLs are cached, indicate if cache items should be eternal.
+     */
+    private boolean cacheEternal;
+
+    /**
+     * Determine whether X509 authentication should allow other forms of 
authentication such as username/password.
+     * If this setting is turned off, typically the ability to view the login 
form as the primary form of
+     * authentication is turned off.
+     */
+    private boolean mixedMode = true;
+
+    /**
+     * When CRLs are cached, indicate the time-to-live of cache items.
+     */
+    private long cacheTimeToLiveSeconds = TimeUnit.HOURS.toSeconds(4);
+
+    /**
+     * If the CRL resource is unavailable, activate the this policy.
+     */
+    private X509PolicySetting crlResourceUnavailablePolicy = 
X509PolicySetting.DENY;
+
+    /**
+     * If the CRL resource has expired, activate the this policy.
+     * Activated if {@link #revocationChecker} is {@code RESOURCE}.
+     */
+    private X509PolicySetting crlResourceExpiredPolicy = 
X509PolicySetting.DENY;
+
+    /**
+     * If the CRL is unavailable, activate the this policy.
+     * Activated if {@link #revocationChecker} is {@code CRL}.
+     */
+    private X509PolicySetting crlUnavailablePolicy = X509PolicySetting.DENY;
+
+    /**
+     * If the CRL has expired, activate the this policy.
+     * Activated if {@link #revocationChecker} is {@code CRL}.
+     */
+    private X509PolicySetting crlExpiredPolicy = X509PolicySetting.DENY;
+
+    /**
+     * The compiled pattern supplied by the deployer.
+     */
+    private String regExTrustedIssuerDnPattern;
+
+    /**
+     * Deployer supplied setting for maximum pathLength in a SUPPLIED
+     * certificate.
+     */
+    private int maxPathLength = 1;
+
+    /**
+     * Deployer supplied setting to allow unlimited pathLength in a SUPPLIED
+     * certificate.
+     */
+    private boolean maxPathLengthAllowUnspecified = false;
+
+    /**
+     * Deployer supplied setting to check the KeyUsage extension.
+     */
+    private boolean checkKeyUsage = false;
+
+    /**
+     * Deployer supplied setting to force require the correct KeyUsage
+     * extension.
+     */
+    private boolean requireKeyUsage = false;
+
+    /**
+     * The pattern that authorizes an acceptable certificate by its subject dn.
+     */
+    private String regExSubjectDnPattern = ".+";
+
+    /**
+     * Whether to extract certificate from request.
+     * The default implementation extracts certificate from header via Tomcat 
SSLValve parsing logic
+     * and using the {@link #DEFAULT_CERT_HEADER_NAME} header.
+     * Must be false by default because if someone enables it they need to 
make sure they are
+     * behind proxy that won't let the header arrive directly from the browser.
+     */
+    private boolean extractCert;
+
+    /**
+     * The name of the header to consult for an X509 cert (e.g. when behind 
proxy).
+     */
+    private String sslHeaderName = "ssl_client_cert";
+
+    private LDAP ldap;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public int getOrder() {
+        return order;
+    }
+
+    public void setOrder(final int order) {
+        this.order = order;
+    }
+
+    public int getRevocationPolicyThreshold() {
+        return revocationPolicyThreshold;
+    }
+
+    public void setRevocationPolicyThreshold(final int 
revocationPolicyThreshold) {
+        this.revocationPolicyThreshold = revocationPolicyThreshold;
+    }
+
+    public boolean isCheckAll() {
+        return checkAll;
+    }
+
+    public void setCheckAll(final boolean checkAll) {
+        this.checkAll = checkAll;
+    }
+
+    public int getRefreshIntervalSeconds() {
+        return refreshIntervalSeconds;
+    }
+
+    public void setRefreshIntervalSeconds(final int refreshIntervalSeconds) {
+        this.refreshIntervalSeconds = refreshIntervalSeconds;
+    }
+
+    public boolean isThrowOnFetchFailure() {
+        return throwOnFetchFailure;
+    }
+
+    public void setThrowOnFetchFailure(final boolean throwOnFetchFailure) {
+        this.throwOnFetchFailure = throwOnFetchFailure;
+    }
+
+    public X509PrincipalType getPrincipalType() {
+        return principalType;
+    }
+
+    public void setPrincipalType(final X509PrincipalType principalType) {
+        this.principalType = principalType;
+    }
+
+    public String getPrincipalAlternateAttribute() {
+        return principalAlternateAttribute;
+    }
+
+    public void setPrincipalAlternateAttribute(final String 
principalAlternateAttribute) {
+        this.principalAlternateAttribute = principalAlternateAttribute;
+    }
+
+    public X509SubjectDnFormat getPrincipalTypeSubjectDnFormat() {
+        return principalTypeSubjectDnFormat;
+    }
+
+    public void setPrincipalTypeSubjectDnFormat(final X509SubjectDnFormat 
principalTypeSubjectDnFormat) {
+        this.principalTypeSubjectDnFormat = principalTypeSubjectDnFormat;
+    }
+
+    public String getPrincipalTypeSerialNoDnSerialNumberPrefix() {
+        return principalTypeSerialNoDnSerialNumberPrefix;
+    }
+
+    public void setPrincipalTypeSerialNoDnSerialNumberPrefix(final String 
principalTypeSerialNoDnSerialNumberPrefix) {
+        this.principalTypeSerialNoDnSerialNumberPrefix = 
principalTypeSerialNoDnSerialNumberPrefix;
+    }
+
+    public String getPrincipalTypeSerialNoDnValueDelimiter() {
+        return principalTypeSerialNoDnValueDelimiter;
+    }
+
+    public void setPrincipalTypeSerialNoDnValueDelimiter(final String 
principalTypeSerialNoDnValueDelimiter) {
+        this.principalTypeSerialNoDnValueDelimiter = 
principalTypeSerialNoDnValueDelimiter;
+    }
+
+    public int getPrincipalTypeSerialNoSNRadix() {
+        return principalTypeSerialNoSNRadix;
+    }
+
+    public void setPrincipalTypeSerialNoSNRadix(final int 
principalTypeSerialNoSNRadix) {
+        this.principalTypeSerialNoSNRadix = principalTypeSerialNoSNRadix;
+    }
+
+    public boolean isPrincipalTypeSerialNoHexSNZeroPadding() {
+        return principalTypeSerialNoHexSNZeroPadding;
+    }
+
+    public void setPrincipalTypeSerialNoHexSNZeroPadding(final boolean 
principalTypeSerialNoHexSNZeroPadding) {
+        this.principalTypeSerialNoHexSNZeroPadding = 
principalTypeSerialNoHexSNZeroPadding;
+    }
+
+    public X509RevocationCheckerType getRevocationChecker() {
+        return revocationChecker;
+    }
+
+    public void setRevocationChecker(final X509RevocationCheckerType 
revocationChecker) {
+        this.revocationChecker = revocationChecker;
+    }
+
+    public X509RevocationFetcherType getCrlFetcher() {
+        return crlFetcher;
+    }
+
+    public void setCrlFetcher(final X509RevocationFetcherType crlFetcher) {
+        this.crlFetcher = crlFetcher;
+    }
+
+    public int getCacheMaxElementsInMemory() {
+        return cacheMaxElementsInMemory;
+    }
+
+    public void setCacheMaxElementsInMemory(final int 
cacheMaxElementsInMemory) {
+        this.cacheMaxElementsInMemory = cacheMaxElementsInMemory;
+    }
+
+    public boolean isCacheDiskOverflow() {
+        return cacheDiskOverflow;
+    }
+
+    public void setCacheDiskOverflow(final boolean cacheDiskOverflow) {
+        this.cacheDiskOverflow = cacheDiskOverflow;
+    }
+
+    public String getCacheDiskSize() {
+        return cacheDiskSize;
+    }
+
+    public void setCacheDiskSize(final String cacheDiskSize) {
+        this.cacheDiskSize = cacheDiskSize;
+    }
+
+    public boolean isCacheEternal() {
+        return cacheEternal;
+    }
+
+    public void setCacheEternal(final boolean cacheEternal) {
+        this.cacheEternal = cacheEternal;
+    }
+
+    public boolean isMixedMode() {
+        return mixedMode;
+    }
+
+    public void setMixedMode(final boolean mixedMode) {
+        this.mixedMode = mixedMode;
+    }
+
+    public long getCacheTimeToLiveSeconds() {
+        return cacheTimeToLiveSeconds;
+    }
+
+    public void setCacheTimeToLiveSeconds(final long cacheTimeToLiveSeconds) {
+        this.cacheTimeToLiveSeconds = cacheTimeToLiveSeconds;
+    }
+
+    public X509PolicySetting getCrlResourceUnavailablePolicy() {
+        return crlResourceUnavailablePolicy;
+    }
+
+    public void setCrlResourceUnavailablePolicy(final X509PolicySetting 
crlResourceUnavailablePolicy) {
+        this.crlResourceUnavailablePolicy = crlResourceUnavailablePolicy;
+    }
+
+    public X509PolicySetting getCrlResourceExpiredPolicy() {
+        return crlResourceExpiredPolicy;
+    }
+
+    public void setCrlResourceExpiredPolicy(final X509PolicySetting 
crlResourceExpiredPolicy) {
+        this.crlResourceExpiredPolicy = crlResourceExpiredPolicy;
+    }
+
+    public X509PolicySetting getCrlUnavailablePolicy() {
+        return crlUnavailablePolicy;
+    }
+
+    public void setCrlUnavailablePolicy(final X509PolicySetting 
crlUnavailablePolicy) {
+        this.crlUnavailablePolicy = crlUnavailablePolicy;
+    }
+
+    public X509PolicySetting getCrlExpiredPolicy() {
+        return crlExpiredPolicy;
+    }
+
+    public void setCrlExpiredPolicy(final X509PolicySetting crlExpiredPolicy) {
+        this.crlExpiredPolicy = crlExpiredPolicy;
+    }
+
+    public List<String> getCrlResources() {
+        return crlResources;
+    }
+
+    public String getRegExTrustedIssuerDnPattern() {
+        return regExTrustedIssuerDnPattern;
+    }
+
+    public void setRegExTrustedIssuerDnPattern(final String 
regExTrustedIssuerDnPattern) {
+        this.regExTrustedIssuerDnPattern = regExTrustedIssuerDnPattern;
+    }
+
+    public int getMaxPathLength() {
+        return maxPathLength;
+    }
+
+    public void setMaxPathLength(final int maxPathLength) {
+        this.maxPathLength = maxPathLength;
+    }
+
+    public boolean isMaxPathLengthAllowUnspecified() {
+        return maxPathLengthAllowUnspecified;
+    }
+
+    public void setMaxPathLengthAllowUnspecified(final boolean 
maxPathLengthAllowUnspecified) {
+        this.maxPathLengthAllowUnspecified = maxPathLengthAllowUnspecified;
+    }
+
+    public boolean isCheckKeyUsage() {
+        return checkKeyUsage;
+    }
+
+    public void setCheckKeyUsage(final boolean checkKeyUsage) {
+        this.checkKeyUsage = checkKeyUsage;
+    }
+
+    public boolean isRequireKeyUsage() {
+        return requireKeyUsage;
+    }
+
+    public void setRequireKeyUsage(final boolean requireKeyUsage) {
+        this.requireKeyUsage = requireKeyUsage;
+    }
+
+    public String getRegExSubjectDnPattern() {
+        return regExSubjectDnPattern;
+    }
+
+    public void setRegExSubjectDnPattern(final String regExSubjectDnPattern) {
+        this.regExSubjectDnPattern = regExSubjectDnPattern;
+    }
+
+    public boolean isExtractCert() {
+        return extractCert;
+    }
+
+    public void setExtractCert(final boolean extractCert) {
+        this.extractCert = extractCert;
+    }
+
+    public String getSslHeaderName() {
+        return sslHeaderName;
+    }
+
+    public void setSslHeaderName(final String sslHeaderName) {
+        this.sslHeaderName = sslHeaderName;
+    }
+
+    public LDAP getLdap() {
+        return ldap;
+    }
+
+    public void setLdap(final LDAP ldap) {
+        this.ldap = ldap;
+    }
+
+    @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/types/X509PolicySetting.java
 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/X509PolicySetting.java
new file mode 100644
index 0000000000..40d09bb772
--- /dev/null
+++ 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/X509PolicySetting.java
@@ -0,0 +1,35 @@
+/*
+ * 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.types;
+
+public enum X509PolicySetting {
+    /**
+     * Allow to proceed.
+     */
+    ALLOW,
+    /**
+     * Deny and block.
+     */
+    DENY,
+    /**
+     * Throttle the request whereby expired data is permitted up to a 
threshold period of time but not afterward.
+     */
+    THRESHOLD;
+
+}
diff --git 
a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/X509PrincipalType.java
 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/X509PrincipalType.java
new file mode 100644
index 0000000000..a970e3baba
--- /dev/null
+++ 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/X509PrincipalType.java
@@ -0,0 +1,59 @@
+/*
+ * 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.types;
+
+public enum X509PrincipalType {
+    /**
+     * Create principal by common name and EDIPI.
+     */
+    CN_EDIPI,
+    /**
+     * Create principal from the RFC822 type name (aka email address) in the 
subject alternative name field.
+     * The subject alternative name field contains a list of various types of 
names, one type is RFC822 e-mail
+     * address. This will return the first e-mail address that is found (if 
there are more than one).
+     */
+    RFC822_EMAIL,
+    /**
+     * Create principal by serial no.
+     * Resolve the principal by the serial number with a configurable 
<strong>radix</strong>, ranging from 2 to 36.
+     * If {@code radix} is {@code 16}, then the serial number could be filled 
with leading zeros to even the number of
+     * digits.
+     */
+    SERIAL_NO,
+    /**
+     * Create principal by serial no and DN.
+     */
+    SERIAL_NO_DN,
+    /**
+     * Create principal by subject.
+     * Resolve the principal by extracting one or more attribute values from 
the
+     * certificate subject DN and combining them with intervening delimiters.
+     */
+    SUBJECT,
+    /**
+     * Create principal by subject alternative name.
+     * Resolve the principal by the subject alternative name extension. (type: 
otherName)
+     */
+    SUBJECT_ALT_NAME,
+    /**
+     * Create principal by subject DN.
+     */
+    SUBJECT_DN;
+
+}
diff --git 
a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/X509RevocationCheckerType.java
 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/X509RevocationCheckerType.java
new file mode 100644
index 0000000000..cdf75f9416
--- /dev/null
+++ 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/X509RevocationCheckerType.java
@@ -0,0 +1,38 @@
+/*
+ * 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.types;
+
+public enum X509RevocationCheckerType {
+    /**
+     * No revocation is performed.
+     */
+    NONE,
+    /**
+     * The CRL URI(s) mentioned in the certificate cRLDistributionPoints 
extension field.
+     *
+     * Caches are available to prevent excessive IO against CRL endpoints. CRL 
data fetched if does not exist in the
+     * cache or if it is expired
+     */
+    CRL,
+    /**
+     * A CRL hosted at a fixed location. The CRL is fetched at periodic 
intervals and cached.
+     */
+    RESOURCE;
+
+}
diff --git 
a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/X509RevocationFetcherType.java
 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/X509RevocationFetcherType.java
new file mode 100644
index 0000000000..f0713ac6e5
--- /dev/null
+++ 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/X509RevocationFetcherType.java
@@ -0,0 +1,32 @@
+/*
+ * 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.types;
+
+public enum X509RevocationFetcherType {
+    /**
+     * All revocation checks use fixed resources to fetch the CRL resource 
from the specified location.
+     */
+    RESOURCE,
+    /**
+     * A CRL resource may be fetched from a pre-configured attribute, in the 
event that the CRL resource location is an
+     * LDAP URI.
+     */
+    LDAP;
+
+}
diff --git 
a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/X509SubjectDnFormat.java
 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/X509SubjectDnFormat.java
new file mode 100644
index 0000000000..50b631f43d
--- /dev/null
+++ 
b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/X509SubjectDnFormat.java
@@ -0,0 +1,47 @@
+/*
+ * 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.types;
+
+public enum X509SubjectDnFormat {
+    /**
+     * Denigrated result of calling certificate.getSubjectDN() method.
+     * Javadocs designate this method as "denigrated" for not being portable 
and/or not being well defined.
+     * It is what has been used by CAS for a long time so it remains the 
default.
+     */
+    DEFAULT,
+    /**
+     * RFC 1779 String format of Distinguished Names.
+     * Calls {@code X500Principal.getName("RFC1779")} which emits a subject DN 
with the attribute keywords defined
+     * in RFC 1779 (CN, L, ST, O, OU, C, STREET). Any other attribute type is 
emitted as an OID.
+     */
+    RFC1779,
+    /**
+     * RFC 2253 String format of Distinguished Names.
+     * Calls {@code X500Principal.getName("RFC2253")} which emits a subject DN 
with the attribute keywords defined in
+     * RFC 2253 (CN, L, ST, O, OU, C, STREET, DC, UID). Any other attribute 
type is emitted as an OID.
+     */
+    RFC2253,
+    /**
+     * Canonical String format of Distinguished Names.
+     * Calls X500Principal.getName("CANONICAL" which emits a subject DN that 
starts with RFC 2253 and applies
+     * additional canonicalizations described in the javadoc.
+     */
+    CANONICAL;
+
+}
diff --git 
a/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc 
b/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc
index 477fee1da7..6f54041d91 100644
--- a/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc
@@ -27,12 +27,15 @@ Several authentication modules are provided:
     ** 
https://apereo.github.io/cas/6.6.x/authentication/Database-Authentication.html[Database^]
     ** 
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/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/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/authentication/X509-Authentication.html[X509^]
+    ** 
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-Google-OpenID-Connect.html[Google
 OpenID^]
+    ** 
https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication-Keycloak.html[Keycloak^]
 * 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/mapping/AuthModulePropertySourceMapper.java
 
b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AuthModulePropertySourceMapper.java
index e3c4c876a4..251e1e2318 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
@@ -42,6 +42,7 @@ import 
org.apache.syncope.common.lib.auth.SimpleMfaAuthModuleConf;
 import org.apache.syncope.common.lib.auth.StaticAuthModuleConf;
 import org.apache.syncope.common.lib.auth.SyncopeAuthModuleConf;
 import org.apache.syncope.common.lib.auth.U2FAuthModuleConf;
+import org.apache.syncope.common.lib.auth.X509AuthModuleConf;
 import org.apache.syncope.common.lib.to.AuthModuleTO;
 import org.apache.syncope.common.lib.to.Item;
 import org.apache.syncope.common.lib.types.AuthModuleState;
@@ -69,6 +70,10 @@ import 
org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jKeyCloakOidcCl
 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;
+import 
org.apereo.cas.configuration.model.support.x509.SubjectDnPrincipalResolverProperties.SubjectDnFormat;
+import org.apereo.cas.configuration.model.support.x509.X509LdapProperties;
+import org.apereo.cas.configuration.model.support.x509.X509Properties;
+import 
org.apereo.cas.configuration.model.support.x509.X509Properties.PrincipalTypes;
 import org.apereo.cas.util.ResourceUtils;
 import org.apereo.cas.util.model.TriStateBoolean;
 
@@ -293,6 +298,78 @@ public class AuthModulePropertySourceMapper extends 
PropertySourceMapper impleme
         return prefix("cas.authn.pac4j.saml[].", 
CasCoreConfigurationUtils.asMap(props));
     }
 
+    @Override
+    public Map<String, Object> map(final AuthModuleTO authModuleTO, final 
X509AuthModuleConf conf) {
+        X509Properties props = new X509Properties();
+        props.setName(conf.getName());
+        props.setOrder(conf.getOrder());
+        props.setCacheDiskOverflow(conf.isCacheDiskOverflow());
+        props.setCacheDiskSize(conf.getCacheDiskSize());
+        props.setCacheEternal(conf.isCacheEternal());
+        props.setCacheMaxElementsInMemory(conf.getCacheMaxElementsInMemory());
+        props.setCacheTimeToLiveSeconds(conf.getCacheTimeToLiveSeconds());
+        props.setCheckAll(conf.isCheckAll());
+        props.setCheckKeyUsage(conf.isCheckKeyUsage());
+        props.setCrlExpiredPolicy(conf.getCrlExpiredPolicy().name());
+        props.setCrlFetcher(conf.getCrlFetcher().name());
+        
props.setCrlResourceExpiredPolicy(conf.getCrlResourceExpiredPolicy().name());
+        
props.setCrlResourceUnavailablePolicy(conf.getCrlResourceUnavailablePolicy().name());
+        props.setCrlResources(conf.getCrlResources());
+        props.setCrlUnavailablePolicy(conf.getCrlUnavailablePolicy().name());
+        props.setExtractCert(conf.isExtractCert());
+        props.setMaxPathLength(conf.getMaxPathLength());
+        
props.setMaxPathLengthAllowUnspecified(conf.isMaxPathLengthAllowUnspecified());
+        props.setMixedMode(conf.isMixedMode());
+        props.setRefreshIntervalSeconds(conf.getRefreshIntervalSeconds());
+        props.setRegExSubjectDnPattern(conf.getRegExSubjectDnPattern());
+        
props.setRegExTrustedIssuerDnPattern(conf.getRegExTrustedIssuerDnPattern());
+        props.setRequireKeyUsage(conf.isRequireKeyUsage());
+        props.setRevocationChecker(conf.getRevocationChecker().name());
+        
props.setRevocationPolicyThreshold(conf.getRevocationPolicyThreshold());
+        props.setSslHeaderName(conf.getSslHeaderName());
+        props.setThrowOnFetchFailure(conf.isThrowOnFetchFailure());
+
+        
props.setPrincipalType(PrincipalTypes.valueOf(conf.getPrincipalType().name()));
+        if (StringUtils.isNotBlank(conf.getPrincipalAlternateAttribute())) {
+            switch (props.getPrincipalType()) {
+                case CN_EDIPI:
+                    
props.getCnEdipi().setAlternatePrincipalAttribute(conf.getPrincipalAlternateAttribute());
+                    break;
+
+                case RFC822_EMAIL:
+                    
props.getRfc822Email().setAlternatePrincipalAttribute(conf.getPrincipalAlternateAttribute());
+                    break;
+
+                case SUBJECT:
+                    
props.setPrincipalDescriptor(conf.getPrincipalAlternateAttribute());
+                    break;
+
+                case SUBJECT_ALT_NAME:
+                    
props.getSubjectAltName().setAlternatePrincipalAttribute(conf.getPrincipalAlternateAttribute());
+                    break;
+
+                case SUBJECT_DN:
+                case SERIAL_NO_DN:
+                case SERIAL_NO:
+                default:
+            }
+        }
+        
props.getSubjectDn().setFormat(SubjectDnFormat.valueOf(conf.getPrincipalTypeSubjectDnFormat().name()));
+        
props.getSerialNoDn().setSerialNumberPrefix(conf.getPrincipalTypeSerialNoDnSerialNumberPrefix());
+        
props.getSerialNoDn().setValueDelimiter(conf.getPrincipalTypeSerialNoDnValueDelimiter());
+        
props.getSerialNo().setPrincipalHexSNZeroPadding(conf.isPrincipalTypeSerialNoHexSNZeroPadding());
+        
props.getSerialNo().setPrincipalSNRadix(conf.getPrincipalTypeSerialNoSNRadix());
+
+        if (conf.getLdap() != null) {
+            X509LdapProperties ldapProps = new X509LdapProperties();
+            
ldapProps.setCertificateAttribute(conf.getLdap().getCertificateAttribute());
+            fill(ldapProps, conf.getLdap());
+            props.setLdap(ldapProps);
+        }
+
+        return prefix("cas.authn.x509.", 
CasCoreConfigurationUtils.asMap(props));
+    }
+
     @Override
     public Map<String, Object> map(final AuthModuleTO authModuleTO, final 
SyncopeAuthModuleConf conf) {
         SyncopeClient syncopeClient = waRestClient.getSyncopeClient();
diff --git a/wa/starter/pom.xml b/wa/starter/pom.xml
index dc568eba65..c43ea8978f 100644
--- a/wa/starter/pom.xml
+++ b/wa/starter/pom.xml
@@ -326,6 +326,10 @@ under the License.
       <groupId>org.apereo.cas</groupId>
       <artifactId>cas-server-support-surrogate-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apereo.cas</groupId>
+      <artifactId>cas-server-support-x509-webflow</artifactId>
+    </dependency>
 
     <dependency>
       <groupId>org.springframework.boot</groupId>


Reply via email to