[java-identity-provider] branch maint-4 updated: IDP-2039 - Add audit logging to login flows
Scott Cantor
cantor.2 at osu.edu
Tue Dec 6 19:02:56 UTC 2022
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch maint-4
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=10c4e7753de72b4d6d3ac3a5ac2ce65c33606b65
The following commit(s) were added to refs/heads/maint-4 by this push:
new 10c4e7753 IDP-2039 - Add audit logging to login flows
10c4e7753 is described below
commit 10c4e7753de72b4d6d3ac3a5ac2ce65c33606b65
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Dec 6 14:02:52 2022 -0500
IDP-2039 - Add audit logging to login flows
https://shibboleth.atlassian.net/browse/IDP-2039
Adjust servlet to populate CertificateContext.
Add extractors for cert issuer/subject.
Audit the X509 flows.
---
.../net/shibboleth/idp/authn/AuthnAuditFields.java | 14 ++++++
.../impl/CertificateIssuerAuditExtractor.java | 51 ++++++++++++++++++++++
.../impl/CertificateSubjectAuditExtractor.java | 51 ++++++++++++++++++++++
.../authn/impl/ValidateExternalAuthentication.java | 2 +-
.../shibboleth/idp/authn/impl/X509AuthServlet.java | 15 +++++++
.../idp/flows/authn/x509-authn-beans.xml | 35 ++++++++++++++-
.../idp/flows/authn/x509-internal-authn-beans.xml | 50 ++++++++++++++++++++-
7 files changed, 215 insertions(+), 3 deletions(-)
diff --git a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AuthnAuditFields.java b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AuthnAuditFields.java
index 4c2c3caf2..dca19db27 100644
--- a/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AuthnAuditFields.java
+++ b/idp-authn-api/src/main/java/net/shibboleth/idp/authn/AuthnAuditFields.java
@@ -59,6 +59,20 @@ public final class AuthnAuditFields {
*/
@Nonnull @NotEmpty public static final String AUTHN_RESULT = "AR";
+ /**
+ * X.509 cerificate subject.
+ *
+ * @since 4.3.0
+ */
+ @Nonnull @NotEmpty public static final String X509_SUBJECT = "X509S";
+
+ /**
+ * X.509 cerificate subject.
+ *
+ * @since 4.3.0
+ */
+ @Nonnull @NotEmpty public static final String X509_ISSUER = "X509I";
+
/** Constructor. */
private AuthnAuditFields() {
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/audit/impl/CertificateIssuerAuditExtractor.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/audit/impl/CertificateIssuerAuditExtractor.java
new file mode 100644
index 000000000..40f68925a
--- /dev/null
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/audit/impl/CertificateIssuerAuditExtractor.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.authn.audit.impl;
+
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.function.Function;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.CertificateContext;
+
+/** {@link Function} that returns the issuer of a client certificate. */
+public class CertificateIssuerAuditExtractor implements Function<ProfileRequestContext,String> {
+
+ /** {@inheritDoc} */
+ @Nullable public String apply(@Nullable final ProfileRequestContext input) {
+
+ final AuthenticationContext authnCtx = input.getSubcontext(AuthenticationContext.class);
+ if (authnCtx != null) {
+ final CertificateContext cc = authnCtx.getSubcontext(CertificateContext.class);
+ if (cc != null) {
+ final Certificate cert = cc.getCertificate();
+ if (cert instanceof X509Certificate) {
+ return ((X509Certificate) cert).getIssuerX500Principal().getName();
+ }
+ }
+ }
+
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/audit/impl/CertificateSubjectAuditExtractor.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/audit/impl/CertificateSubjectAuditExtractor.java
new file mode 100644
index 000000000..9bd7e1f67
--- /dev/null
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/audit/impl/CertificateSubjectAuditExtractor.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.idp.authn.audit.impl;
+
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.function.Function;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.CertificateContext;
+
+/** {@link Function} that returns the subject of a client certificate. */
+public class CertificateSubjectAuditExtractor implements Function<ProfileRequestContext,String> {
+
+ /** {@inheritDoc} */
+ @Nullable public String apply(@Nullable final ProfileRequestContext input) {
+
+ final AuthenticationContext authnCtx = input.getSubcontext(AuthenticationContext.class);
+ if (authnCtx != null) {
+ final CertificateContext cc = authnCtx.getSubcontext(CertificateContext.class);
+ if (cc != null) {
+ final Certificate cert = cc.getCertificate();
+ if (cert instanceof X509Certificate) {
+ return ((X509Certificate) cert).getSubjectX500Principal().getName();
+ }
+ }
+ }
+
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/ValidateExternalAuthentication.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/ValidateExternalAuthentication.java
index 8e44c598f..b0f0a3ddd 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/ValidateExternalAuthentication.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/ValidateExternalAuthentication.java
@@ -67,7 +67,7 @@ import org.slf4j.LoggerFactory;
* {@link AbstractValidationAction#handleError(ProfileRequestContext, AuthenticationContext, Exception, String)}
* method is called.
*/
-public class ValidateExternalAuthentication extends AbstractValidationAction {
+public class ValidateExternalAuthentication extends AbstractAuditingValidationAction {
/** Default prefix for metrics. */
@Nonnull @NotEmpty private static final String DEFAULT_METRIC_NAME = "net.shibboleth.idp.authn.external";
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/X509AuthServlet.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/X509AuthServlet.java
index 515b16498..9efc7ff0c 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/X509AuthServlet.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/X509AuthServlet.java
@@ -34,9 +34,12 @@ import javax.servlet.http.HttpServletResponse;
import net.shibboleth.idp.authn.AuthnEventIds;
import net.shibboleth.idp.authn.ExternalAuthentication;
import net.shibboleth.idp.authn.ExternalAuthenticationException;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.CertificateContext;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import org.opensaml.profile.context.ProfileRequestContext;
import org.opensaml.security.SecurityException;
import org.opensaml.security.trust.TrustEngine;
import org.opensaml.security.x509.BasicX509Credential;
@@ -155,6 +158,18 @@ public class X509AuthServlet extends HttpServlet {
log.debug("End-entity X.509 certificate found with subject '{}', issued by '{}'",
cert.getSubjectDN().getName(), cert.getIssuerDN().getName());
+ final ProfileRequestContext prc = ExternalAuthentication.getProfileRequestContext(key, httpRequest);
+ final AuthenticationContext authnCtx = prc.getSubcontext(AuthenticationContext.class);
+ if (authnCtx != null) {
+ final CertificateContext cc = authnCtx.getSubcontext(CertificateContext.class, true);
+ cc.setCertificate(cert);
+ if (certs.length > 1) {
+ for (int i = 1; i < certs.length; i++) {
+ cc.getIntermediates().add(certs[i]);
+ }
+ }
+ }
+
if (trustEngine != null) {
try {
final BasicX509Credential cred = new BasicX509Credential(cert);
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/x509-authn-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/x509-authn-beans.xml
index 587d43217..d8f926814 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/x509-authn-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/x509-authn-beans.xml
@@ -29,10 +29,43 @@
p:metricName="net.shibboleth.idp.authn.x509"
p:addDefaultPrincipals="#{getObject('shibboleth.authn.X509.addDefaultPrincipals') ?: %{idp.authn.X509.addDefaultPrincipals:true}}"
p:classifiedMessages="#{getObject('shibboleth.authn.X509.ClassifiedMessageMap')}"
- p:resultCachingPredicate="#{getObject('shibboleth.authn.X509.resultCachingPredicate')}" />
+ p:resultCachingPredicate="#{getObject('shibboleth.authn.X509.resultCachingPredicate')}"
+ p:populateAuditContextAction="#{%{idp.authn.X509.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('shibboleth.authn.X509.PopulateAuditContext') : null}"
+ p:writeAuditLogAction="#{%{idp.authn.X509.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('WriteAuditLog') : null}" />
<bean id="PopulateSubjectCanonicalizationContext"
class="net.shibboleth.idp.authn.impl.PopulateSubjectCanonicalizationContext" scope="prototype"
p:availableFlows-ref="shibboleth.PostLoginSubjectCanonicalizationFlows" />
+ <!-- Audit logging beans. -->
+
+ <util:map id="shibboleth.authn.AuditFormattingMap">
+ <entry key="#{'%{idp.authn.X509.audit.category:Shibboleth-Audit.X509}'.trim()}"
+ value="#{'%{idp.authn.X509.audit.format:%a|%T|%SP|%s|%AF|%X509S|%X509I|%AR|%UA}'.trim()}" />
+ </util:map>
+
+ <bean id="shibboleth.authn.X509.DefaulAuditExtractors" parent="shibboleth.authn.DefaulAuditExtractors" lazy-init="true"
+ class="org.springframework.beans.factory.config.MapFactoryBean">
+ <property name="sourceMap">
+ <map merge="true">
+ <entry>
+ <key>
+ <util:constant static-field="net.shibboleth.idp.authn.AuthnAuditFields.X509_SUBJECT"/>
+ </key>
+ <bean class="net.shibboleth.idp.authn.audit.impl.CertificateSubjectAuditExtractor" />
+ </entry>
+ <entry>
+ <key>
+ <util:constant static-field="net.shibboleth.idp.authn.AuthnAuditFields.X509_ISSUER"/>
+ </key>
+ <bean class="net.shibboleth.idp.authn.audit.impl.CertificateIssuerAuditExtractor" />
+ </entry>
+ </map>
+ </property>
+ </bean>
+
+ <bean id="shibboleth.authn.X509.PopulateAuditContext" parent="shibboleth.authn.AbstractPopulateAuditContext" lazy-init="true"
+ p:fieldExtractors="#{getObject('shibboleth.authn.X509.AuditExtractors') ?: getObject('shibboleth.authn.X509.DefaulAuditExtractors')}"
+ p:clearAuditContext="true" />
+
</beans>
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/x509-internal-authn-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/x509-internal-authn-beans.xml
index c2baae8e7..1e3d38f54 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/x509-internal-authn-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/authn/x509-internal-authn-beans.xml
@@ -18,6 +18,20 @@
<bean class="net.shibboleth.ext.spring.config.IdentifiableBeanPostProcessor" />
<bean class="net.shibboleth.idp.profile.impl.ProfileActionBeanPostProcessor" />
+ <!-- Default message map. -->
+ <util:map id="shibboleth.authn.X509Internal.ClassifiedMessageMap">
+ <entry key="RequestUnsupported">
+ <list>
+ <value>RequestUnsupported</value>
+ </list>
+ </entry>
+ <entry key="InvalidCredentials">
+ <list>
+ <value>InvalidCredentials</value>
+ </list>
+ </entry>
+ </util:map>
+
<import resource="conditional:%{idp.home}/conf/authn/x509-internal-authn-config.xml" />
<bean id="ExtractX509CertificateFromRequest"
@@ -27,7 +41,10 @@
<bean id="ValidateX509Certificate"
class="net.shibboleth.idp.authn.impl.ValidateCredentials" scope="prototype"
p:addDefaultPrincipals="#{getObject('shibboleth.authn.X509.addDefaultPrincipals') ?: %{idp.authn.X509Internal.addDefaultPrincipals:true}}"
- p:resultCachingPredicate="#{getObject('shibboleth.authn.X509.resultCachingPredicate')}">
+ p:classifiedMessages="#{getObject('shibboleth.authn.X509Internal.ClassifiedMessageMap')}"
+ p:resultCachingPredicate="#{getObject('shibboleth.authn.X509.resultCachingPredicate')}"
+ p:populateAuditContextAction="#{%{idp.authn.X509Internal.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('shibboleth.authn.X509Internal.PopulateAuditContext') : null}"
+ p:writeAuditLogAction="#{%{idp.authn.X509Internal.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('WriteAuditLog') : null}">
<property name="validators">
<bean id="x509" class="net.shibboleth.idp.authn.impl.X509CertificateCredentialValidator"
p:trustEngine="#{getObject('shibboleth.authn.X509.TrustEngine')}"
@@ -39,4 +56,35 @@
class="net.shibboleth.idp.authn.impl.PopulateSubjectCanonicalizationContext" scope="prototype"
p:availableFlows-ref="shibboleth.PostLoginSubjectCanonicalizationFlows" />
+ <!-- Audit logging beans. -->
+
+ <util:map id="shibboleth.authn.AuditFormattingMap">
+ <entry key="#{'%{idp.authn.X509Internal.audit.category:Shibboleth-Audit.X509Internal}'.trim()}"
+ value="#{'%{idp.authn.X509Internal.audit.format:%a|%T|%SP|%s|%AF|%X509S|%X509I|%AR|%UA}'.trim()}" />
+ </util:map>
+
+ <bean id="shibboleth.authn.X509Internal.DefaulAuditExtractors" parent="shibboleth.authn.DefaulAuditExtractors" lazy-init="true"
+ class="org.springframework.beans.factory.config.MapFactoryBean">
+ <property name="sourceMap">
+ <map merge="true">
+ <entry>
+ <key>
+ <util:constant static-field="net.shibboleth.idp.authn.AuthnAuditFields.X509_SUBJECT"/>
+ </key>
+ <bean class="net.shibboleth.idp.authn.audit.impl.CertificateSubjectAuditExtractor" />
+ </entry>
+ <entry>
+ <key>
+ <util:constant static-field="net.shibboleth.idp.authn.AuthnAuditFields.X509_ISSUER"/>
+ </key>
+ <bean class="net.shibboleth.idp.authn.audit.impl.CertificateIssuerAuditExtractor" />
+ </entry>
+ </map>
+ </property>
+ </bean>
+
+ <bean id="shibboleth.authn.X509Internal.PopulateAuditContext" parent="shibboleth.authn.AbstractPopulateAuditContext" lazy-init="true"
+ p:fieldExtractors="#{getObject('shibboleth.authn.X509Internal.AuditExtractors') ?: getObject('shibboleth.authn.X509Internal.DefaulAuditExtractors')}"
+ p:clearAuditContext="true" />
+
</beans>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list