[java-plugin-shibd] branch main updated: Preliminary WIP on Hub auditing.

Codeberg noreply at shibboleth.net
Wed Jun 24 16:09:09 UTC 2026


This is an automated email from the git hooks/post-receive script.

codeberg pushed a commit to branch main
in repository java-plugin-shibd.

View the commit online:
https://codeberg.org/Shibboleth/java-plugin-shibd/commit/63fb0c408a64fe4a763f424524913815e3c78baf

The following commit(s) were added to refs/heads/main by this push:
     new 63fb0c4  Preliminary WIP on Hub auditing.
63fb0c4 is described below

commit 63fb0c408a64fe4a763f424524913815e3c78baf
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Wed Jun 24 12:06:27 2026 -0400

    Preliminary WIP on Hub auditing.
---
 .../META-INF/net.shibboleth.idp/postconfig.xml     | 57 ++++++++++++++
 .../idp/flows/sp/abstract/sp-abstract-beans.xml    | 35 ++++++++-
 .../idp/flows/sp/abstract/sp-abstract-flow.xml     |  9 ++-
 .../java/net/shibboleth/sp/flows/PingFlowTest.java |  2 +-
 .../net/shibboleth/sp/profile/SPAuditFields.java   | 43 +++++++++++
 .../sp/audit/impl/AgentIDAuditExtractor.java       | 41 ++++++++++
 .../sp/audit/impl/AgentInputAuditExtractor.java    | 89 ++++++++++++++++++++++
 .../net/shibboleth/sp/audit/impl/package-info.java | 18 +++++
 .../sp/profile/impl/DecodeAgentRequest.java        |  1 -
 .../shibboleth/sp/profile/impl/WriteAuditLog.java  | 40 ++++++++++
 10 files changed, 331 insertions(+), 4 deletions(-)

diff --git a/sp-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/sp-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index 977e77d..eb2a668 100644
--- a/sp-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/sp-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -72,4 +72,61 @@
         class="net.shibboleth.sp.util.TemplatedStringLookupStrategy"
         p:velocityEngine-ref="shibboleth.VelocityEngine" />
 
+    <!-- Audit beans. -->
+
+    <bean id="shibboleth.sp.DefaultFlowStartAuditExtractors" lazy-init="true"
+            class="org.springframework.beans.factory.config.MapFactoryBean">
+        <property name="sourceMap">
+            <map>
+                <entry>
+                    <key>
+                        <util:constant static-field="net.shibboleth.idp.profile.IdPAuditFields.START_TIME"/>
+                    </key>
+                    <bean parent="shibboleth.Functions.Expression" c:_0="T(java.time.Instant).now()" />
+                </entry>
+            </map>
+        </property>
+    </bean>
+
+    <bean id="shibboleth.sp.DefaultPostAgentDecodeAuditExtractors" lazy-init="true"
+            class="org.springframework.beans.factory.config.MapFactoryBean">
+        <property name="sourceMap">
+            <map>
+                <entry>
+                    <key>
+                        <util:constant static-field="net.shibboleth.sp.profile.SPAuditFields.AGENT_ID"/>
+                    </key>
+                    <bean class="net.shibboleth.sp.audit.impl.AgentIDAuditExtractor" />
+                </entry>
+                <entry>
+                    <key>
+                        <util:constant static-field="net.shibboleth.sp.profile.SPAuditFields.APPLICATION_ID"/>
+                    </key>
+                    <bean class="net.shibboleth.sp.audit.impl.AgentInputAuditExtractor">
+                        <property name="fieldName">
+                            <util:constant static-field="net.shibboleth.sp.profile.SPConstants.APPLICATION"/>
+                        </property>
+                    </bean>
+                </entry>
+                <entry>
+                    <key>
+                        <util:constant static-field="net.shibboleth.sp.profile.SPAuditFields.TX_ID"/>
+                    </key>
+                    <bean class="net.shibboleth.sp.audit.impl.AgentInputAuditExtractor">
+                        <property name="fieldName">
+                            <util:constant static-field="net.shibboleth.sp.profile.SPConstants.TXID"/>
+                        </property>
+                    </bean>
+                </entry>
+                <entry>
+                    <key>
+                        <util:constant static-field="net.shibboleth.sp.profile.SPAuditFields.OPERATION"/>
+                    </key>
+                    <!-- No field name pulls name of structure -> operation. -->
+                    <bean class="net.shibboleth.sp.audit.impl.AgentInputAuditExtractor" />
+                </entry>
+            </map>
+        </property>
+    </bean>
+
 </beans>
\ No newline at end of file
diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/abstract/sp-abstract-beans.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/abstract/sp-abstract-beans.xml
index 4102728..97a844c 100644
--- a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/abstract/sp-abstract-beans.xml
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/abstract/sp-abstract-beans.xml
@@ -7,11 +7,41 @@
                            http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
     default-init-method="initialize" default-destroy-method="destroy">
     
+    <!-- Beans for flow auditing -->
+
+    <bean id="shibboleth.sp.AbstractPopulateAuditContext" abstract="true"
+        class="net.shibboleth.idp.profile.audit.impl.PopulateAuditContext" scope="prototype"
+        p:formattingMapParser-ref="shibboleth.sp.AuditFormattingMapParser"
+        p:dateTimeFormat="#{getObject('shibboleth.AuditDateTimeFormat')}"
+        p:useDefaultTimeZone="#{getObject('shibboleth.AuditDefaultTimeZone') ?: false}"
+        p:fieldReplacements="#{getObject('shibboleth.AuditFieldReplacementMap')}" />
+        
+    <bean id="shibboleth.sp.AuditFormattingMapParser" scope="prototype" lazy-init="true"
+        class="net.shibboleth.idp.profile.audit.impl.PopulateAuditContext.FormattingMapParser"
+        c:_0="#{getObject('shibboleth.sp.AuditFormattingMap') ?: getObject('shibboleth.sp.DefaultAuditFormattingMap')}" />
+    
+    <bean id="WriteAuditLog"
+        class="net.shibboleth.sp.profile.impl.WriteAuditLog" scope="prototype" lazy-init="true"
+        p:formattingMap="#{getObject('shibboleth.sp.AuditFormattingMap') ?: getObject('shibboleth.sp.DefaultAuditFormattingMap')}"
+        p:dateTimeFormat="#{getObject('shibboleth.AuditDateTimeFormat')}"
+        p:useDefaultTimeZone="#{getObject('shibboleth.AuditDefaultTimeZone') ?: false}"
+        p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier"
+        p:httpServletResponseSupplier-ref="shibboleth.RemotedHttpServletResponseSupplier" />
+
+    <util:map id="shibboleth.sp.DefaultAuditFormattingMap">
+        <entry key="Shibboleth-Audit.SP" value="%AGENT|%APP|%TX|%OP|%a|%ST|%T|%IDP|%SP|%i|%ac|%t|%attr|%n|%f|%X|%XA|%b|%bb|%e|%S|%SS|%UA" />
+    </util:map>
+
+    <!-- Action beans. -->
+    
     <bean id="InitializeProfileRequestContext"
         class="net.shibboleth.idp.profile.impl.InitializeProfileRequestContext" scope="prototype"
         p:profileId-ref="shibboleth.sp.profileId"
         p:loggingId-ref="shibboleth.sp.loggingId" />
 
+    <bean id="FlowStartPopulateAuditContext" parent="shibboleth.sp.AbstractPopulateAuditContext"
+        p:fieldExtractors="#{getObject('shibboleth.sp.FlowStartAuditExtractors') ?: getObject('shibboleth.sp.DefaultFlowStartAuditExtractors')}" />
+
     <bean id="PopulateMetricContext"
         class="org.opensaml.profile.action.impl.PopulateMetricContext" scope="prototype"
         p:counterName="#{getObject('shibboleth.metrics.ProfileCounter')}"
@@ -64,6 +94,9 @@
         p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
         p:populateMDC="%{idp.logging.MDC.enabled:true}" />
         
+    <bean id="PostAgentDecodePopulateAuditContext" parent="shibboleth.sp.AbstractPopulateAuditContext"
+        p:fieldExtractors="#{getObject('shibboleth.sp.PostAgentDecodeAuditExtractors') ?: getObject('shibboleth.sp.DefaultPostAgentDecodeAuditExtractors')}" />
+        
     <bean id="EncodeAgentResponse" class="net.shibboleth.sp.profile.impl.EncodeAgentResponse" scope="prototype"
         p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
         p:httpServletResponseSupplier-ref="shibboleth.HttpServletResponseSupplier" />
@@ -76,7 +109,7 @@
     <bean id="PrepareAgentErrorResponse"
         class="net.shibboleth.sp.profile.impl.PrepareAgentErrorResponse" scope="prototype" />
 
-    <!-- ======== Beans for agent authentication ======== -->
+    <!-- Beans for agent authentication -->
 
     <util:list id="DefaultAgentValidators">
         <!-- Default just uses secrets defined in agents.xml file. -->
diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/abstract/sp-abstract-flow.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/abstract/sp-abstract-flow.xml
index 2e67dbe..d2171c9 100644
--- a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/abstract/sp-abstract-flow.xml
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/abstract/sp-abstract-flow.xml
@@ -8,6 +8,7 @@
             <!-- Needed to allow reuse of various classes. -->
             <evaluate expression="opensamlProfileRequestContext.ensureSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).setAttemptedFlow(flowRequestContext.getActiveFlow().getApplicationContext().getBean('AgentAuthnFlowDescriptor'))" />
         </on-entry>
+        <evaluate expression="FlowStartPopulateAuditContext" />
         <evaluate expression="PopulateMetricContext" />
         <evaluate expression="ExtractUsernamePasswordFromBasicAuth" />
         <evaluate expression="InitializeAgentRequestContext" />
@@ -36,6 +37,7 @@
     
     <action-state id="DecodeAgentRequest">
         <evaluate expression="DecodeAgentRequest" />
+        <evaluate expression="PostAgentDecodePopulateAuditContext" />
         <evaluate expression="'proceed'" />
 
         <transition on="proceed" to="DoOperation" />
@@ -79,6 +81,7 @@
 
     <end-state id="HandleErrorWithView" view="sp/error">
         <on-entry>
+            <evaluate expression="WriteAuditLog" />
             <evaluate expression="environment" result="requestScope.environment" />
             <evaluate expression="currentEvent" result="requestScope.currentEvent" />
             <evaluate expression="opensamlProfileRequestContext" result="requestScope.profileRequestContext" />
@@ -104,7 +107,11 @@
 
     <!-- Successful terminal state (success meaning outbound agent response encoded). -->
 
-    <end-state id="end" />
+    <end-state id="end">
+        <on-entry>
+            <evaluate expression="WriteAuditLog" />
+        </on-entry>
+    </end-state>
 
     <!-- All unhandled non-proceed results are turned into errors. -->
     <global-transitions>
diff --git a/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/PingFlowTest.java b/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/PingFlowTest.java
index c33534f..7100b07 100644
--- a/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/PingFlowTest.java
+++ b/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/PingFlowTest.java
@@ -81,7 +81,7 @@ public class PingFlowTest extends AbstractSPFlowTest {
     @Test
     public void testSuccess() throws IOException {
         setDefaultAuth();
-        setRequest("GET", new DDF(null));
+        setRequest("GET", new DDF("ping"));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertFlowExecutionResult(result, FLOW_ID);
         assertFlowExecutionOutcome(result.getOutcome());
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/profile/SPAuditFields.java b/sp-server-api/src/main/java/net/shibboleth/sp/profile/SPAuditFields.java
new file mode 100644
index 0000000..28bf80d
--- /dev/null
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/profile/SPAuditFields.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed 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.sp.profile;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+
+/**
+ * Constants for token and logout consumer operations.
+ */
+public final class SPAuditFields {
+
+
+    /** Private constructor. */
+    private SPAuditFields() {
+    }
+    
+    /** Agent ID field. */
+    @Nonnull @NotEmpty public static final String AGENT_ID = "AGENT";
+
+    /** Application ID field. */
+    @Nonnull @NotEmpty public static final String APPLICATION_ID = "APP";
+
+    /** Transaction ID field. */
+    @Nonnull @NotEmpty public static final String TX_ID = "TX";
+
+    /** Operation field. */
+    @Nonnull @NotEmpty public static final String OPERATION = "OP";
+    
+}
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/audit/impl/AgentIDAuditExtractor.java b/sp-server-impl/src/main/java/net/shibboleth/sp/audit/impl/AgentIDAuditExtractor.java
new file mode 100644
index 0000000..1ea2f18
--- /dev/null
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/audit/impl/AgentIDAuditExtractor.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed 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.sp.audit.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.sp.Agent;
+import net.shibboleth.sp.context.AgentRequestContext;
+
+/**
+ * Audit field extractor that pulls the Agent ID from the active request.
+ */
+public class AgentIDAuditExtractor implements Function<ProfileRequestContext,String> {
+    
+    /** {@inheritDoc} */
+    @Nullable public String apply(@Nullable final ProfileRequestContext input) {
+        
+        final AgentRequestContext agentRequestContext =
+                input != null ? input.getSubcontext(AgentRequestContext.class) : null;
+        
+        final Agent agent = agentRequestContext != null ? agentRequestContext.getAgent() : null;
+        return agent != null ? agent.getId() : null;
+    }
+
+}
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/audit/impl/AgentInputAuditExtractor.java b/sp-server-impl/src/main/java/net/shibboleth/sp/audit/impl/AgentInputAuditExtractor.java
new file mode 100644
index 0000000..9e3f3fa
--- /dev/null
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/audit/impl/AgentInputAuditExtractor.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed 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.sp.audit.impl;
+
+import java.nio.charset.StandardCharsets;
+import java.util.function.Function;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.sp.context.AgentRequestContext;
+import net.shibboleth.sp.ddf.DDF;
+
+/**
+ * Audit field extractor that pulls out and converts a particular input member from an
+ * Agent's request message.
+ * 
+ * <p>If no field is specified, the "name" of the actual message structure is returned.</p>
+ */
+public class AgentInputAuditExtractor implements Function<ProfileRequestContext,String> {
+
+    /**
+     * Name of field to extract.
+     */
+    @Nullable private String fieldName;
+    
+    /**
+     * Sets name of the field for which to return a value.
+     * 
+     * @param name field name, dotted notation permitted
+     */
+    public void setFieldName(@Nullable final String name) {
+        fieldName = name;
+    }
+    
+    /** {@inheritDoc} */
+    @Nullable public String apply(@Nullable final ProfileRequestContext input) {
+        
+        final AgentRequestContext agentRequestContext =
+                input != null ? input.getSubcontext(AgentRequestContext.class) : null;
+        final DDF obj = agentRequestContext != null ? agentRequestContext.getInput() : null;
+        if (obj != null) {
+            if (fieldName != null) {
+                final DDF field = obj.getmember(fieldName);
+                if (field.isstring()) {
+                    return field.string();
+                } else if (field.isint() || field.islist() || field.isstruct()) {
+                    final Integer val = field.integer();
+                    if (val != null) {
+                        return val.toString();
+                    }
+                } else if (field.islong()) {
+                    final Long val = field.longinteger();
+                    if (val != null) {
+                        return val.toString();
+                    }
+                } else if (field.isfloat()) {
+                    final Double val = field.floating();
+                    if (val != null) {
+                        return val.toString();
+                    }
+                } else if (field.isunsafestring()) {
+                    final byte[] val = field.unsafe_string();
+                    if (val != null) {
+                        return new String(val, StandardCharsets.UTF_8);
+                    }
+                }
+            } else {
+                return obj.name();
+            }
+        }
+        
+        return null;
+    }
+
+}
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/audit/impl/package-info.java b/sp-server-impl/src/main/java/net/shibboleth/sp/audit/impl/package-info.java
new file mode 100644
index 0000000..ccf4014
--- /dev/null
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/audit/impl/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed 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.
+ */
+
+/**
+ * Implementation of SP audting features.
+ */
+package net.shibboleth.sp.audit.impl;
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/DecodeAgentRequest.java b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/DecodeAgentRequest.java
index dd61813..098a837 100644
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/DecodeAgentRequest.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/DecodeAgentRequest.java
@@ -108,7 +108,6 @@ public class DecodeAgentRequest extends AbstractAgentAction {
             }
 
             final AgentRequestContext agentContext = ensureAgentRequestContext();
-            final Agent agent = agentContext.getAgent();
             
             try (final InputStream in = request.getInputStream()) {
                 assert in != null;
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/WriteAuditLog.java b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/WriteAuditLog.java
new file mode 100644
index 0000000..5241b4e
--- /dev/null
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/WriteAuditLog.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed 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.sp.profile.impl;
+
+import javax.annotation.Nullable;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+/**
+ * Subclass of IdP auditing action that overrides the {@link HttpServletRequest} accessor
+ * to bypass the "non-null" assumptions built into our profile action classes.
+ * 
+ * This is an "alternative" means of getting the remoted request into the execution code
+ * and is necessary when the object may be absent.
+ */
+public class WriteAuditLog extends net.shibboleth.idp.profile.audit.impl.WriteAuditLog {
+
+    /** {@inheritDoc} */
+    @Override
+    @Nullable public HttpServletRequest getHttpServletRequest() {
+        try {
+            return super.getHttpServletRequest();
+        } catch (final IllegalStateException e) {
+            return null;
+        }
+    }
+    
+}
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list