[java-identity-provider] 02/11: IDP-2039 - Add audit logging to login flows
Scott Cantor
cantor.2 at osu.edu
Tue Jan 3 22:01:42 UTC 2023
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch main
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=bd1f417aef496198595669b70115f8e583b30762
commit bd1f417aef496198595669b70115f8e583b30762
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 ++++++++++++++++++++++
.../impl/AbstractAuditingValidationAction.java | 14 +++---
.../authn/impl/ValidateExternalAuthentication.java | 2 +-
.../shibboleth/idp/authn/impl/X509AuthServlet.java | 17 +++++++-
.../idp/flows/authn/x509-authn-beans.xml | 35 ++++++++++++++-
.../idp/flows/authn/x509-internal-authn-beans.xml | 50 ++++++++++++++++++++-
8 files changed, 221 insertions(+), 13 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 a3ec311f0..097e18709 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/AbstractAuditingValidationAction.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/AbstractAuditingValidationAction.java
index e6a68bc08..fdcdd5c66 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/AbstractAuditingValidationAction.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/AbstractAuditingValidationAction.java
@@ -34,9 +34,8 @@ import net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.profile.audit.impl.PopulateAuditContext;
import net.shibboleth.idp.profile.audit.impl.WriteAuditLog;
import net.shibboleth.idp.profile.context.AuditContext;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.logic.Constraint;
/**
* Base class for validation actions that includes new audit logging support.
@@ -75,8 +74,7 @@ public abstract class AbstractAuditingValidationAction extends AbstractValidatio
* @param strategy lookup strategy
*/
public void setAuditContextCreationStrategy(@Nonnull final Function<ProfileRequestContext,AuditContext> strategy) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
+ checkSetterPreconditions();
auditContextCreationStrategy = Constraint.isNotNull(strategy, "AuditContext creation strategy cannot be null");
}
@@ -88,8 +86,7 @@ public abstract class AbstractAuditingValidationAction extends AbstractValidatio
* @since 4.3.0
*/
public void setPopulateAuditContextAction(@Nullable final PopulateAuditContext action) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
+ checkSetterPreconditions();
populateAuditContextAction = action;
}
@@ -101,8 +98,7 @@ public abstract class AbstractAuditingValidationAction extends AbstractValidatio
* @since 4.3.0
*/
public void setWriteAuditLogAction(@Nullable final WriteAuditLog action) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
+ checkSetterPreconditions();
writeAuditLogAction = action;
}
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 7744c37fa..7536fe2d0 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 net.shibboleth.shared.service.ServiceableComponent;
* {@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 fc73369a8..38fd9e47f 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 jakarta.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.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.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;
@@ -154,6 +157,18 @@ public class X509AuthServlet extends HttpServlet {
log.debug("End-entity X.509 certificate found with subject '{}', issued by '{}'",
cert.getSubjectX500Principal().getName(), cert.getIssuerX500Principal().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);
@@ -207,4 +222,4 @@ public class X509AuthServlet extends HttpServlet {
}
// Checkstyle: CyclomaticComplexity|MethodLength ON
-}
\ No newline at end of file
+}
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 c2f90266c..10abd2916 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 70b15e11e..21fe19d85 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.shared.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