[java-sp-server] branch main updated: Freshen up code and check in some WIP on config.

Scott Cantor cantor.2 at osu.edu
Tue Jan 31 20:50:52 UTC 2023


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

scantor pushed a commit to branch main
in repository java-sp-server.

View the commit online:
http://git.shibboleth.net/view/?p=java-sp-server.git;a=commit;h=2ce4e34671336a5647d263993cc99f62aa8f88b3

The following commit(s) were added to refs/heads/main by this push:
     new 2ce4e34  Freshen up code and check in some WIP on config.
2ce4e34 is described below

commit 2ce4e34671336a5647d263993cc99f62aa8f88b3
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Jan 31 15:50:46 2023 -0500

    Freshen up code and check in some WIP on config.
---
 pom.xml                                            |   4 +-
 sp-bom/pom.xml                                     |  14 ++-
 .../net/shibboleth/sp/conf/global-system.xml       |   2 -
 sp-saml-api/.checkstyle                            |  10 ++
 sp-saml-api/.gitignore                             |   1 +
 {sp-session-api => sp-saml-api}/pom.xml            |  15 ++-
 sp-saml-impl/.checkstyle                           |  10 ++
 sp-saml-impl/.gitignore                            |   1 +
 {sp-session-api => sp-saml-impl}/pom.xml           |   8 +-
 .../AbstractConditionalProfileConfiguration.java   |  68 +++++++++++++
 .../sp/config/AbstractProfileConfiguration.java    | 104 +++++++++++++++++++
 .../net/shibboleth/sp/config/AssertingParty.java   |  55 ++++++++++
 .../sp/config/ConditionalProfileConfiguration.java |  38 +++++++
 .../shibboleth/sp/config/ProfileConfiguration.java |  42 ++++++++
 .../net/shibboleth/sp/config/package-info.java     |  21 ++++
 .../src/main/java/net/shibboleth/sp/ddf/DDF.java   | 111 ++++++++++++++++-----
 .../sp/ddf/RemotedHttpServletRequest.java          |  57 ++++++++---
 .../sp/ddf/RemotedHttpServletResponse.java         |  57 ++++++-----
 .../java/net/shibboleth/sp/ddf/DDFSupportTest.java |   6 +-
 .../test/java/net/shibboleth/sp/ddf/DDFTest.java   |  17 ++--
 .../sp/ddf/RemotedHttpServletRequestTest.java      |   1 +
 .../sp/ddf/RemotedHttpServletResponseTest.java     |   1 +
 sp-server-impl/pom.xml                             |   2 +-
 sp-session-api/pom.xml                             |   7 ++
 .../sp/session/AbstractRemotableSPSession.java     |  98 ++++++++++++++++++
 .../shibboleth/sp/session/RemotableSPSession.java  |  38 +++++++
 .../java/net/shibboleth/sp/session/SPSession.java  | 105 +++++++++++++++++++
 .../shibboleth/sp/session/SessionException.java    |  61 +++++++++++
 .../net/shibboleth/sp/session/package-info.java    |  21 ++++
 29 files changed, 887 insertions(+), 88 deletions(-)

diff --git a/pom.xml b/pom.xml
index 10de363..4f58701 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
         <shib-metadata.version>5.0.0-SNAPSHOT</shib-metadata.version>
         <opensaml.version>5.0.0-SNAPSHOT</opensaml.version>
         <shib-shared.version>9.0.0-SNAPSHOT</shib-shared.version>
-        <spring-int.version>6.0.0-RC1</spring-int.version>
+        <spring-int.version>6.0.1</spring-int.version>
         <checkstyle.configLocation>${project.basedir}/resources/checkstyle.xml</checkstyle.configLocation>
     </properties>
     
@@ -62,6 +62,8 @@
         <module>sp-bom</module>
         <module>sp-conf</module>
         <module>sp-conf-impl</module>
+        <module>sp-saml-api</module>
+        <module>sp-saml-impl</module>
         <module>sp-server-api</module>
         <module>sp-server-impl</module>
         <module>sp-session-api</module>
diff --git a/sp-bom/pom.xml b/sp-bom/pom.xml
index 6d5a4a7..9ddf6af 100644
--- a/sp-bom/pom.xml
+++ b/sp-bom/pom.xml
@@ -17,6 +17,11 @@
     
     <dependencyManagement>
         <dependencies>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sp-saml-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
             <dependency>
                 <groupId>${project.groupId}</groupId>
                 <artifactId>sp-server-api</artifactId>
@@ -30,12 +35,17 @@
             
             <dependency>
                 <groupId>${project.groupId}</groupId>
-                <artifactId>idp-server-impl</artifactId>
+                <artifactId>sp-saml-impl</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>sp-server-impl</artifactId>
                 <version>${project.version}</version>
             </dependency>
             <dependency>
                 <groupId>${project.groupId}</groupId>
-                <artifactId>idp-session-impl</artifactId>
+                <artifactId>sp-session-impl</artifactId>
                 <version>${project.version}</version>
             </dependency>
             
diff --git a/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/global-system.xml b/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/global-system.xml
index f6e954d..3810a1c 100644
--- a/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/global-system.xml
+++ b/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/global-system.xml
@@ -23,8 +23,6 @@
                 <bean class="net.shibboleth.shared.spring.config.BooleanToPredicateConverter" />
                 <bean class="net.shibboleth.shared.spring.config.StringBooleanToPredicateConverter" />
                 <bean class="net.shibboleth.shared.spring.config.StringToResourceConverter" />
-                <bean class="net.shibboleth.shared.spring.config.FunctionToFunctionConverter" />
-                <bean class="net.shibboleth.shared.spring.config.PredicateToPredicateConverter" />
                 <bean class="net.shibboleth.shared.spring.config.StringToDurationConverter" />
                 <bean class="net.shibboleth.shared.spring.config.StringToPeriodConverter" />
             </set>
diff --git a/sp-saml-api/.checkstyle b/sp-saml-api/.checkstyle
new file mode 100644
index 0000000..5bae456
--- /dev/null
+++ b/sp-saml-api/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Shibboleth Checkstyle" location="/java-sp-server/resources/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Shibboleth Checkstyle" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/sp-saml-api/.gitignore b/sp-saml-api/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/sp-saml-api/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/sp-session-api/pom.xml b/sp-saml-api/pom.xml
similarity index 76%
copy from sp-session-api/pom.xml
copy to sp-saml-api/pom.xml
index c064e97..268dfed 100644
--- a/sp-session-api/pom.xml
+++ b/sp-saml-api/pom.xml
@@ -9,18 +9,25 @@
         <version>0.0.1-SNAPSHOT</version>
     </parent>
     
-    <artifactId>sp-session-api</artifactId>
-    <description>SP Session API.</description>
-    <name>Shibboleth SP :: Processing Hub :: Session API</name>
+    <artifactId>sp-saml-api</artifactId>
+    <description>SP SAML API.</description>
+    <name>Shibboleth SP :: Processing Hub :: SAML API</name>
     <packaging>jar</packaging>
     
     <properties>
         <checkstyle.configLocation>${project.basedir}/../resources/checkstyle.xml</checkstyle.configLocation>
-        <automatic.module.name>net.shibboleth.sp.session</automatic.module.name>
+        <automatic.module.name>net.shibboleth.sp.saml</automatic.module.name>
     </properties>
         
     <dependencies>
         <!-- Compile dependencies -->
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>sp-server-api</artifactId>
+            <version>${project.version}</version>
+            <scope>compile</scope>
+        </dependency>
+
         <dependency>
             <groupId>net.shibboleth</groupId>
             <artifactId>shib-attribute-api</artifactId>
diff --git a/sp-saml-impl/.checkstyle b/sp-saml-impl/.checkstyle
new file mode 100644
index 0000000..5bae456
--- /dev/null
+++ b/sp-saml-impl/.checkstyle
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <local-check-config name="Shibboleth Checkstyle" location="/java-sp-server/resources/checkstyle.xml" type="project" description="">
+    <additional-data name="protect-config-file" value="false"/>
+  </local-check-config>
+  <fileset name="all" enabled="true" check-config-name="Shibboleth Checkstyle" local="true">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/sp-saml-impl/.gitignore b/sp-saml-impl/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/sp-saml-impl/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/sp-session-api/pom.xml b/sp-saml-impl/pom.xml
similarity index 85%
copy from sp-session-api/pom.xml
copy to sp-saml-impl/pom.xml
index c064e97..76e23c1 100644
--- a/sp-session-api/pom.xml
+++ b/sp-saml-impl/pom.xml
@@ -9,14 +9,14 @@
         <version>0.0.1-SNAPSHOT</version>
     </parent>
     
-    <artifactId>sp-session-api</artifactId>
-    <description>SP Session API.</description>
-    <name>Shibboleth SP :: Processing Hub :: Session API</name>
+    <artifactId>sp-saml-impl</artifactId>
+    <description>SP SAML Implememntation.</description>
+    <name>Shibboleth SP :: Processing Hub :: SAML Implementation</name>
     <packaging>jar</packaging>
     
     <properties>
         <checkstyle.configLocation>${project.basedir}/../resources/checkstyle.xml</checkstyle.configLocation>
-        <automatic.module.name>net.shibboleth.sp.session</automatic.module.name>
+        <automatic.module.name>net.shibboleth.sp.saml.impl</automatic.module.name>
     </properties>
         
     <dependencies>
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/config/AbstractConditionalProfileConfiguration.java b/sp-server-api/src/main/java/net/shibboleth/sp/config/AbstractConditionalProfileConfiguration.java
new file mode 100644
index 0000000..146da74
--- /dev/null
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/config/AbstractConditionalProfileConfiguration.java
@@ -0,0 +1,68 @@
+/*
+ * 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.sp.config;
+
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.shared.annotation.ParameterName;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.logic.PredicateSupport;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+/**
+ * Base class for {@link ConditionalProfileConfiguration} implementations.
+ *
+ * @since 3.4.0
+ */
+public abstract class AbstractConditionalProfileConfiguration extends AbstractProfileConfiguration
+        implements ConditionalProfileConfiguration {
+
+    /** Activation condition. */
+    @Nonnull private Predicate<ProfileRequestContext> activationCondition;
+
+    /**
+     * Constructor.
+     * 
+     * @param id ID of the communication profile, never null or empty
+     */
+    public AbstractConditionalProfileConfiguration(@Nonnull @NotEmpty @ParameterName(name="id") final String id) {
+        super(id);
+        
+        activationCondition = PredicateSupport.alwaysTrue();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull public Predicate<ProfileRequestContext> getActivationCondition() {
+        return activationCondition;
+    }
+
+    /**
+     * Set an activation condition to control this profile.
+     *
+     * @param condition condition to apply
+     */
+    public void setActivationCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
+        activationCondition = Constraint.isNotNull(condition, "Activation condition cannot be null");
+    }
+    
+}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/config/AbstractProfileConfiguration.java b/sp-server-api/src/main/java/net/shibboleth/sp/config/AbstractProfileConfiguration.java
new file mode 100644
index 0000000..9b7567a
--- /dev/null
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/config/AbstractProfileConfiguration.java
@@ -0,0 +1,104 @@
+/*
+ * 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.sp.config;
+
+import java.util.Objects;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.shared.annotation.ParameterName;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.logic.FunctionSupport;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.security.config.SecurityConfiguration;
+
+/** Base class for {@link ProfileConfiguration} implementations. */
+public abstract class AbstractProfileConfiguration extends AbstractIdentifiableInitializableComponent
+        implements ProfileConfiguration {
+
+    /** Lookup function to supply securityConfiguration property. */
+    @Nonnull private Function<ProfileRequestContext,SecurityConfiguration> securityConfigurationLookupStrategy;
+
+    /**
+     * Constructor.
+     * 
+     * @param id ID of the communication profile, never null or empty
+     */
+    public AbstractProfileConfiguration(@Nonnull @NotEmpty @ParameterName(name="id") final String id) {
+        setId(id);
+        securityConfigurationLookupStrategy = FunctionSupport.constant(null);
+    }
+
+    /** {@inheritDoc} */
+    @Nullable public SecurityConfiguration getSecurityConfiguration(
+            @Nullable final ProfileRequestContext profileRequestContext) {
+        return securityConfigurationLookupStrategy.apply(profileRequestContext);
+    }
+
+    /**
+     * Sets the security configuration for this profile.
+     * 
+     * @param configuration security configuration for this profile
+     */
+    public void setSecurityConfiguration(@Nullable final SecurityConfiguration configuration) {
+        securityConfigurationLookupStrategy = FunctionSupport.constant(configuration);
+    }
+
+    /**
+     * Set a lookup strategy for the security configuration.
+     *
+     * @param strategy  lookup strategy
+     * 
+     * @since 3.3.0
+     */
+    public void setSecurityConfigurationLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,SecurityConfiguration> strategy) {
+        securityConfigurationLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return getId().hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj == null) {
+            return false;
+        }
+
+        if (!(obj instanceof AbstractProfileConfiguration)) {
+            return false;
+        }
+
+        final AbstractProfileConfiguration other = (AbstractProfileConfiguration) obj;
+        return Objects.equals(getId(), other.getId());
+    }
+    
+}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/config/AssertingParty.java b/sp-server-api/src/main/java/net/shibboleth/sp/config/AssertingParty.java
new file mode 100644
index 0000000..b5b9960
--- /dev/null
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/config/AssertingParty.java
@@ -0,0 +1,55 @@
+/*
+ * 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.sp.config;
+
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+
+/**
+ * Package of options exposed for configuring behavior when dealing with particular sources
+ * of identity.
+ * 
+ * <p>Historically referred to as a "relying party", renamed for better accuracy.</p>
+ */
+public interface AssertingParty {
+
+    // TODO: This is entirely speculative but I lean toward this being pretty minimal and not
+    // particularly connected to the "existing" <RelyingParty> settings/concept in the SP.
+    // Rather, this is for hub-side controlled settings geared around technical protocol
+    // settings subject to IT control, modulo a few overrideable options at runtime that
+    // the agent would supply while making subsequent calls.
+    
+    /**
+     * Get map of profile configurations keyed by profile ID.
+     * 
+     * @param profileRequestContext profile request context 
+     * 
+     * @return map of profile configurations
+     */
+    @Nonnull @NonnullElements @NotLive @Unmodifiable Map<String,ProfileConfiguration> getProfileConfigurations(
+            @Nullable final ProfileRequestContext profileRequestContext);
+
+}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/config/ConditionalProfileConfiguration.java b/sp-server-api/src/main/java/net/shibboleth/sp/config/ConditionalProfileConfiguration.java
new file mode 100644
index 0000000..ac0e41d
--- /dev/null
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/config/ConditionalProfileConfiguration.java
@@ -0,0 +1,38 @@
+/*
+ * 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.sp.config;
+
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+/**
+ * A {@link ProfileConfiguration} supporting an activation condition.
+ */
+public interface ConditionalProfileConfiguration extends ProfileConfiguration {
+
+    /**
+     * Get the condition controlling enablement of this profile.
+     * 
+     * @return condition controlling enablement of this profile
+     */
+    @Nonnull Predicate<ProfileRequestContext> getActivationCondition();
+    
+}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/config/ProfileConfiguration.java b/sp-server-api/src/main/java/net/shibboleth/sp/config/ProfileConfiguration.java
new file mode 100644
index 0000000..0f5f78c
--- /dev/null
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/config/ProfileConfiguration.java
@@ -0,0 +1,42 @@
+/*
+ * 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.sp.config;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.security.config.SecurityConfiguration;
+
+import net.shibboleth.shared.component.IdentifiedComponent;
+
+/**
+ * Interface for profile-specific configuration used to customize SP behavior.
+ */
+public interface ProfileConfiguration extends IdentifiedComponent {
+
+    /**
+     * Gets the applicable {@link SecurityConfiguration}.
+     * 
+     * @param profileRequestContext profile request context 
+     * 
+     * @return the profile's security configuration
+     */
+    @Nullable SecurityConfiguration getSecurityConfiguration(
+            @Nullable final ProfileRequestContext profileRequestContext);
+    
+}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/config/package-info.java b/sp-server-api/src/main/java/net/shibboleth/sp/config/package-info.java
new file mode 100644
index 0000000..160ee73
--- /dev/null
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/config/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Core configuration interfaces and classes.
+ */
+package net.shibboleth.sp.config;
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/ddf/DDF.java b/sp-server-api/src/main/java/net/shibboleth/sp/ddf/DDF.java
index c106a62..86549ca 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/ddf/DDF.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/ddf/DDF.java
@@ -37,6 +37,7 @@ import net.shibboleth.shared.annotation.constraint.NonnullElements;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.annotation.constraint.NotLive;
 import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.logic.Constraint;
 
 
@@ -324,19 +325,23 @@ public class DDF implements Iterable<DDF> {
                 break;
                 
             case DDF_INT:
+                assert value instanceof Integer;
                 dup.integer((Integer) value);
                 break;
 
             case DDF_LONG:
+                assert value instanceof Long;
                 dup.longinteger((Long) value);
                 break;
                 
             case DDF_FLOAT:
+                assert value instanceof Double;
                 dup.floating((Double) value);
                 break;
                 
             case DDF_STRUCT:
                 dup.structure();
+                assert value instanceof Map;
                 for (final DDF ddf : ((Map<String,DDF>) value).values()) {
                     dup.add(ddf.copy());
                 }
@@ -344,6 +349,7 @@ public class DDF implements Iterable<DDF> {
                 
             case DDF_LIST:
                 dup.list();
+                assert value instanceof List;
                 for (final DDF ddf : (List<DDF>) value) {
                     dup.add(ddf.copy());
                 }
@@ -377,7 +383,8 @@ public class DDF implements Iterable<DDF> {
      * @return this object
      */
     @Nonnull public DDF name(@Nullable @NotEmpty final String n) {
-        if (!isnull() && (parent == null || !parent.isstruct())) {
+        final DDF p = parent;
+        if (!isnull() && (p == null || !p.isstruct())) {
             if (n != null) {
                 name = Constraint.isNotEmpty(n.substring(0,Integer.min(n.length(), 255)), "Name cannot be empty");
             } else {
@@ -513,8 +520,10 @@ public class DDF implements Iterable<DDF> {
             case DDF_INT:
                 return (Integer) value;
             case DDF_LONG:
+                assert value instanceof Long;
                 return ((Long) value).intValue();
             case DDF_FLOAT:
+                assert value instanceof Double;
                 return ((Double) value).intValue();
             case DDF_STRING:
                 try {
@@ -524,8 +533,10 @@ public class DDF implements Iterable<DDF> {
                     return null;
                 }
             case DDF_STRUCT:
+                assert value instanceof Map;
                 return ((Map<?,?>) value).size();
             case DDF_LIST:
+                assert value instanceof List;
                 return ((List<?>) value).size();
             default:
                 break;
@@ -546,10 +557,12 @@ public class DDF implements Iterable<DDF> {
         
         switch(type) {
             case DDF_INT:
+                assert value instanceof Integer;
                 return ((Integer) value).longValue();
             case DDF_LONG:
                 return (Long) value;
             case DDF_FLOAT:
+                assert value instanceof Double;
                 return ((Double) value).longValue();
             case DDF_STRING:
                 try {
@@ -559,8 +572,10 @@ public class DDF implements Iterable<DDF> {
                     return null;
                 }
             case DDF_STRUCT:
+                assert value instanceof Map;
                 return (long) ((Map<?,?>) value).size();
             case DDF_LIST:
+                assert value instanceof List;
                 return (long) ((List<?>) value).size();
             default:
                 break;
@@ -581,8 +596,10 @@ public class DDF implements Iterable<DDF> {
         
         switch(type) {
             case DDF_INT:
+                assert value instanceof Integer;
                 return ((Integer) value).doubleValue();
             case DDF_LONG:
+                assert value instanceof Long;
                 return ((Long) value).doubleValue();
             case DDF_FLOAT:
                 return (Double) value;
@@ -594,8 +611,10 @@ public class DDF implements Iterable<DDF> {
                     return null;
                 }
             case DDF_STRUCT:
+                assert value instanceof Map;
                 return (double) ((Map<?,?>) value).size();
             case DDF_LIST:
+                assert value instanceof List;
                 return (double) ((List<?>) value).size();
             default:
                 break;
@@ -818,7 +837,7 @@ public class DDF implements Iterable<DDF> {
      * 
      * @return this object
      */
-    @Nonnull public DDF pointer(@Nonnull final Object val) {
+    @Nonnull public DDF pointer(@Nullable final Object val) {
         empty();
         value = val;
         type = DDFType.DDF_POINTER;
@@ -848,11 +867,14 @@ public class DDF implements Iterable<DDF> {
             if (child.name == null) {
                 return child;
             }
+            assert child.name != null;
             getmember(child.name).destroy();
             child.remove();
+            assert value instanceof Map;
             ((Map<String,DDF>) value).put(child.name, child);
         } else {
             child.remove();
+            assert value instanceof List;
             ((List<DDF>) value).add(child);
         }
 
@@ -880,6 +902,7 @@ public class DDF implements Iterable<DDF> {
         child.remove();
         @SuppressWarnings("unchecked")
         final List<DDF> list = (List<DDF>) value;
+        assert list != null;
         list.add(list.indexOf(before), child);
         child.parent = this;
         return child;
@@ -906,7 +929,7 @@ public class DDF implements Iterable<DDF> {
         
         @SuppressWarnings("unchecked")
         final List<DDF> list = (List<DDF>) value;
-        
+        assert list != null;
         final int i = list.indexOf(after);
         if (i == list.size() - 1) {
             list.add(child);
@@ -925,11 +948,14 @@ public class DDF implements Iterable<DDF> {
      */
     @SuppressWarnings("unchecked")
     @Nonnull public DDF remove() {
-        if (parent != null) {
-            if (parent.isstruct()) {
-                ((Map<String,DDF>) parent.value).remove(name);
+        final DDF p = parent;
+        if (p != null) {
+            if (p.isstruct()) {
+                assert p.value instanceof Map;
+                ((Map<String,DDF>) p.value).remove(name);
             } else {
-                ((List<DDF>) parent.value).remove(this);
+                assert p.value instanceof List;
+                ((List<DDF>) p.value).remove(this);
             }
             
             parent = null;
@@ -954,10 +980,11 @@ public class DDF implements Iterable<DDF> {
     @SuppressWarnings("unchecked")
     @Nonnull @NonnullElements @Unmodifiable @NotLive public Map<String,DDF> asMap() {
         if (isstruct()) {
-            return Map.copyOf((Map<String,DDF>) value);
+            assert value instanceof Map;
+            return CollectionSupport.copyToMap((Map<String,DDF>) value);
         }
         
-        return Collections.emptyMap();
+        return CollectionSupport.emptyMap();
     }
 
     /**
@@ -968,12 +995,14 @@ public class DDF implements Iterable<DDF> {
     @SuppressWarnings("unchecked")
     @Nonnull @NonnullElements @Unmodifiable @NotLive public List<DDF> asList() {
         if (isstruct()) {
-            return List.copyOf(((Map<String,DDF>) value).values());
+            assert value instanceof Map;
+            return CollectionSupport.copyToList(((Map<String,DDF>) value).values());
         } else if (islist()) {
-            return List.copyOf((List<DDF>) value);
+            assert value instanceof List;
+            return CollectionSupport.copyToList((List<DDF>) value);
         }
         
-        return Collections.emptyList();
+        return CollectionSupport.emptyList();
     }
 
     /**
@@ -1000,6 +1029,7 @@ public class DDF implements Iterable<DDF> {
                     base.structure();
                 }
                 
+                assert segment != null;
                 DDF node = base.getmember(segment);
                 if (node.isnull()) {
                     node = base.add(new DDF(segment));
@@ -1043,14 +1073,21 @@ public class DDF implements Iterable<DDF> {
                 } catch(final NumberFormatException e) {
                     index = 0;
                 }
-                if (current.islist() && index < ((List<DDF>) current.value).size()) {
-                    current = ((List<DDF>) current.value).get(index);
+                if (current.islist()) {
+                    assert current.value instanceof List;
+                    final List<DDF> currentValue = (List<DDF>) current.value;
+                    if (index < currentValue.size()) {
+                        current = currentValue.get(index);
+                    } else {
+                        return new DDF();
+                    }
                 } else {
                     return new DDF();
                 }
                 i++;
             } else if (current.isstruct()) {
                 // Access the named element and advance the path.
+                assert current.value instanceof Map;
                 current = ((Map<String,DDF>) current.value).get(tokens[i]);
                 if (current == null) {
                     return new DDF();
@@ -1058,6 +1095,7 @@ public class DDF implements Iterable<DDF> {
                 i++;
             } else if (current.islist()) {
                 // Access first element of list, don't advance the path.
+                assert current.value instanceof List;
                 current = ((List<DDF>) current.value).get(0);
                 if (current == null) {
                     return new DDF();
@@ -1067,6 +1105,7 @@ public class DDF implements Iterable<DDF> {
             }
         }
         
+        assert current != null;
         return current;
     }
 
@@ -1099,7 +1138,7 @@ public class DDF implements Iterable<DDF> {
             if (other.name != null) {
                 return false;
             }
-        } else if (!name.equals(other.name)) {
+        } else if (name != null && !name.equals(other.name)) {
             return false;
         }
         
@@ -1111,7 +1150,7 @@ public class DDF implements Iterable<DDF> {
             if (other.value != null) {
                 return false;
             }
-        } else if (!value.equals(other.value)) {
+        } else if (value != null && !value.equals(other.value)) {
             return false;
         }
         
@@ -1124,10 +1163,24 @@ public class DDF implements Iterable<DDF> {
     public int hashCode() {
         final int prime = 31;
         int result = 1;
-        result = prime * result + ((name == null) ? 0 : name.hashCode());
-        result = prime * result + ((parent == null) ? 0 : parent.hashCode());
+        if (name != null) {
+            result = name.hashCode() + prime * result;
+        } else {
+            result = prime * result;
+        }
+        
+        if (parent != null) {
+            result = parent.hashCode() + prime * result;
+        } else {
+            result = prime * result;
+        }
         result = prime * result + ((type == null) ? 0 : type.hashCode());
-        result = prime * result + ((value == null) ? 0 : value.hashCode());
+        
+        if (value != null) {
+            result = value.hashCode() + prime * result;
+        } else {
+            result = prime * result;
+        }
         return result;
     }
 
@@ -1176,7 +1229,8 @@ public class DDF implements Iterable<DDF> {
                 }
                 builder.append(" = ");
                 if (value != null) {
-                    builder.append('"').append(((String) value).replace("\"", "\\\"")).append('"');
+                    final String valueCopy = (String) value;
+                    builder.append('"').append(valueCopy.replace("\"", "\\\"")).append('"');
                 } else {
                     builder.append("null");
                 }
@@ -1189,8 +1243,9 @@ public class DDF implements Iterable<DDF> {
                 }
                 builder.append(" = ");
                 if (value != null) {
+                    final byte[] valueCopy = (byte[]) value;
                     builder.append('{');
-                    for (final byte b : (byte[]) value) {
+                    for (final byte b : valueCopy) {
                         builder.append(Integer.toHexString(b)).append(", ");
                     }
                     builder.append('}');
@@ -1229,6 +1284,7 @@ public class DDF implements Iterable<DDF> {
                     builder.append(' ').append(name);
                 }
                 builder.append(" = {");
+                assert value instanceof Map;
                 if (!((Map<?,?>) value).isEmpty()) {
                     builder.append('\n');
                     for (final DDF child : this) {
@@ -1242,13 +1298,15 @@ public class DDF implements Iterable<DDF> {
                 break;
 
             case DDF_LIST:
-                builder.append("DDF[").append(((List<?>) value).size()).append(']');
+                assert value instanceof List;
+                final List<?> valueCopy = (List<?>) value;
+                builder.append("DDF[").append(valueCopy.size()).append(']');
                 if (name != null) {
                     builder.append(' ').append(name);
                 }
                 builder.append(" = {");
 
-                if (!((List<?>) value).isEmpty()) {
+                if (!valueCopy.isEmpty()) {
                     builder.append('\n');
                     for (final DDF child : this) {
                         child.dump(builder, indent + 2);
@@ -1310,6 +1368,7 @@ public class DDF implements Iterable<DDF> {
                     os.write(Integer.toString(type.getValue()).getBytes("UTF8"));
                     if (value != null) {
                         os.write(' ');
+                        assert value instanceof String;
                         encode(os, ((String) value).getBytes("UTF-8"));
                     }
                     os.write('\n');
@@ -1319,6 +1378,7 @@ public class DDF implements Iterable<DDF> {
                     os.write(Integer.toString(type.getValue()).getBytes("UTF8"));
                     if (value != null) {
                         os.write(' ');
+                        assert value instanceof byte[];
                         encode(os, (byte[]) value);
                     }
                     os.write('\n');
@@ -1327,6 +1387,7 @@ public class DDF implements Iterable<DDF> {
                 case DDF_INT:
                     os.write(Integer.toString(type.getValue()).getBytes("UTF8"));
                     os.write(' ');
+                    assert value instanceof Integer;
                     os.write(Integer.toString((Integer) value).getBytes("UTF8"));
                     os.write('\n');
                     break;
@@ -1334,6 +1395,7 @@ public class DDF implements Iterable<DDF> {
                 case DDF_LONG:
                     os.write(Integer.toString(type.getValue()).getBytes("UTF8"));
                     os.write(' ');
+                    assert value instanceof Long;
                     os.write(Long.toString((Long) value).getBytes("UTF8"));
                     os.write('\n');
                     break;
@@ -1341,11 +1403,13 @@ public class DDF implements Iterable<DDF> {
                 case DDF_FLOAT:
                     os.write(Integer.toString(type.getValue()).getBytes("UTF8"));
                     os.write(' ');
+                    assert value instanceof Double;
                     os.write(Double.toString((Double) value).getBytes("UTF8"));
                     os.write('\n');
                     break;
 
                 case DDF_STRUCT:
+                    assert value instanceof Map;
                     @SuppressWarnings("unchecked")
                     final Collection<DDF> members = ((Map<String,DDF>) value).values();
                     os.write(Integer.toString(type.getValue()).getBytes("UTF8"));
@@ -1358,6 +1422,7 @@ public class DDF implements Iterable<DDF> {
                     break;
 
                 case DDF_LIST:
+                    assert value instanceof List;
                     @SuppressWarnings("unchecked")
                     final Collection<DDF> children = (List<DDF>) value;
                     os.write(Integer.toString(type.getValue()).getBytes("UTF8"));
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/ddf/RemotedHttpServletRequest.java b/sp-server-api/src/main/java/net/shibboleth/sp/ddf/RemotedHttpServletRequest.java
index 4daa0ca..069d1ba 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/ddf/RemotedHttpServletRequest.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/ddf/RemotedHttpServletRequest.java
@@ -48,6 +48,7 @@ import jakarta.servlet.AsyncContext;
 import jakarta.servlet.DispatcherType;
 import jakarta.servlet.ReadListener;
 import jakarta.servlet.RequestDispatcher;
+import jakarta.servlet.ServletConnection;
 import jakarta.servlet.ServletContext;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.ServletInputStream;
@@ -60,6 +61,7 @@ import jakarta.servlet.http.HttpSession;
 import jakarta.servlet.http.HttpUpgradeHandler;
 import jakarta.servlet.http.Part;
 import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.collection.Pair;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.net.URISupport;
@@ -192,7 +194,7 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
     /** {@inheritDoc} */
     public Map<String, String[]> getParameterMap() {
         if (parameters == null) {
-            parameters = new HashMap<>();
+            final Map<String, String[]> parameterMap = new HashMap<>();
             final Multimap<String,String> multimap = ArrayListMultimap.create();
             final String qs = getQueryString();
             if (qs != null) {
@@ -213,13 +215,30 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
             }
             
             for (final Map.Entry<String,Collection<String>> entry : multimap.asMap().entrySet()) {
-                parameters.put(entry.getKey(), entry.getValue().toArray(new String[entry.getValue().size()]));
+                parameterMap.put(entry.getKey(), entry.getValue().toArray(new String[entry.getValue().size()]));
             }
+            
+            parameters = parameterMap;
         }
         
         return parameters;
     }
 
+    /** {@inheritDoc} */
+    public String getRequestId() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    public String getProtocolRequestId() {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    public ServletConnection getServletConnection() {
+        throw new UnsupportedOperationException();
+    }
+    
     /** {@inheritDoc} */
     public String getProtocol() {
         final String protocol = obj.getmember("protocol").string();
@@ -269,7 +288,7 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
 
     /** {@inheritDoc} */
     public Locale getLocale() {
-        throw new UnsupportedOperationException();        
+        throw new UnsupportedOperationException();
     }
 
     /** {@inheritDoc} */
@@ -287,11 +306,6 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
         throw new UnsupportedOperationException();
     }
 
-    /** {@inheritDoc} */
-    public String getRealPath(final String path) {
-        throw new UnsupportedOperationException();
-    }
-
     /** {@inheritDoc} */
     public int getRemotePort() {
         throw new UnsupportedOperationException();
@@ -371,25 +385,32 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
                             if (name.endsWith("_fgwars")) {
                                 name.substring(0, name.length() - 7);
                             }
+                            assert cookies != null;
                             cookies.add(new Cookie(name, nvpair[1]));
                         }
                     }
                 } else {
-                    cookies = Collections.emptyList();
+                    cookies = CollectionSupport.emptyList();
                 }
             } else {
-                cookies = Collections.emptyList();
+                cookies = CollectionSupport.emptyList();
             }
         }
         
+        assert cookies != null;
         if (cookies.isEmpty()) {
             return null;
         }
+        
+        assert cookies != null;
         return cookies.toArray(new Cookie[cookies.size()]);
     }
 
     /** {@inheritDoc} */
     public long getDateHeader(final String name) {
+        if (name == null) {
+            return -1;
+        }
         final DDF h = obj.getmember("headers").getmember(name);
         if (h.isstring()) {
             try {
@@ -409,11 +430,17 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
 
     /** {@inheritDoc} */
     public String getHeader(final String name) {
+        if (name == null) {
+            return null;
+        }
         return decodeUnsafeString(obj.getmember("headers").getmember(name).unsafe_string());
     }
 
     /** {@inheritDoc} */
     public Enumeration<String> getHeaders(final String name) {
+        if (name == null) {
+            return Collections.emptyEnumeration();
+        }
         final String s = decodeUnsafeString(obj.getmember("headers").getmember(name).unsafe_string());
         if (s != null) {
             return Collections.enumeration(Collections.singletonList(s));
@@ -428,6 +455,9 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
 
     /** {@inheritDoc} */
     public int getIntHeader(final String name) {
+        if (name == null) {
+            return -1;
+        }
         final DDF h = obj.getmember("headers").getmember(name);
         if (h.isstring()) {
             return Integer.parseInt(decodeUnsafeString(h.unsafe_string()));
@@ -532,11 +562,6 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
         return false;
     }
 
-    /** {@inheritDoc} */
-    public boolean isRequestedSessionIdFromUrl() {
-        return false;
-    }
-
     /** {@inheritDoc} */
     public boolean authenticate(final HttpServletResponse response) throws IOException, ServletException {
         throw new UnsupportedOperationException();
@@ -684,5 +709,7 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
             return delegate.markSupported();
         }
     }
+    
 
+    /** {@inheritDoc} */
 }
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/ddf/RemotedHttpServletResponse.java b/sp-server-api/src/main/java/net/shibboleth/sp/ddf/RemotedHttpServletResponse.java
index d960f41..8960bb1 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/ddf/RemotedHttpServletResponse.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/ddf/RemotedHttpServletResponse.java
@@ -223,16 +223,6 @@ public class RemotedHttpServletResponse implements HttpServletResponse {
         return url;
     }
 
-    /** {@inheritDoc} */
-    public String encodeUrl(final String url) {
-        return url;
-    }
-
-    /** {@inheritDoc} */
-    public String encodeRedirectUrl(final String url) {
-        return url;
-    }
-
     /** {@inheritDoc} */
     public void sendError(final int sc, final String msg) throws IOException {
         throw new UnsupportedOperationException();
@@ -257,37 +247,49 @@ public class RemotedHttpServletResponse implements HttpServletResponse {
 
     /** {@inheritDoc} */
     public void setDateHeader(final String name, final long date) {
-        unsetHeader(name);
-        addDateHeader(name, date);
+        if (name != null) {
+            unsetHeader(name);
+            addDateHeader(name, date);
+        }
     }
 
     /** {@inheritDoc} */
     public void addDateHeader(final String name, final long date) {
-        final SimpleDateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
-        formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
-        addHeader(name, formatter.format(Date.from(Instant.ofEpochMilli(date))));
+        if (name != null) {
+            final SimpleDateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
+            formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
+            addHeader(name, formatter.format(Date.from(Instant.ofEpochMilli(date))));
+        }
     }
 
     /** {@inheritDoc} */
     public void setHeader(final String name, final String value) {
-        unsetHeader(name);
-        addHeader(name, value);
+        if (name != null) {
+            unsetHeader(name);
+            addHeader(name, value);
+        }
     }
 
     /** {@inheritDoc} */
     public void addHeader(final String name, final String value) {
-        getHeaderList().add(new DDF(name).string(value));
+        if (name != null) {
+            getHeaderList().add(new DDF(name).string(value));
+        }
     }
 
     /** {@inheritDoc} */
     public void setIntHeader(final String name, final int value) {
-        unsetHeader(name);
-        addIntHeader(name, value);
+        if (name != null) {
+            unsetHeader(name);
+            addIntHeader(name, value);
+        }
     }
 
     /** {@inheritDoc} */
     public void addIntHeader(final String name, final int value) {
-        getHeaderList().add(new DDF(name).integer(value));
+        if (name != null) {
+            getHeaderList().add(new DDF(name).integer(value));
+        }
     }
 
     /** {@inheritDoc} */
@@ -295,12 +297,6 @@ public class RemotedHttpServletResponse implements HttpServletResponse {
         obj.addmember("response.status").integer(sc);
     }
 
-    /** {@inheritDoc} */
-    public void setStatus(final int sc, final String sm) {
-        setStatus(sc);
-        obj.addmember("response.status_message").string(sm);
-    }
-
     /** {@inheritDoc} */
     public int getStatus() {
         final Integer i = obj.getmember("response.status").integer();
@@ -318,7 +314,10 @@ public class RemotedHttpServletResponse implements HttpServletResponse {
             if (header.orElseThrow().isstring()) {
                 return header.orElseThrow().string();
             }
-            return header.orElseThrow().integer().toString();
+            final Integer i = header.orElseThrow().integer();
+            if (i != null) {
+                return i.toString();
+            }
         }
         
         return null;
@@ -385,7 +384,7 @@ public class RemotedHttpServletResponse implements HttpServletResponse {
         private int offset;
         
         /** Wrapped array. */
-        private final byte[] buffer;
+        @Nonnull private final byte[] buffer;
         
         /**
          * Constructor.
diff --git a/sp-server-api/src/test/java/net/shibboleth/sp/ddf/DDFSupportTest.java b/sp-server-api/src/test/java/net/shibboleth/sp/ddf/DDFSupportTest.java
index f6dfca9..3cb397a 100644
--- a/sp-server-api/src/test/java/net/shibboleth/sp/ddf/DDFSupportTest.java
+++ b/sp-server-api/src/test/java/net/shibboleth/sp/ddf/DDFSupportTest.java
@@ -56,7 +56,10 @@ public class DDFSupportTest {
     */
     @AfterClass
    public void tearDown() {
-       parserPool.destroy();
+       if (parserPool != null) {
+           parserPool.destroy();
+           parserPool = null;
+       }
    }
     
     /**
@@ -67,6 +70,7 @@ public class DDFSupportTest {
      */
     @Test
     public void test() throws XMLParserException, IOException {
+        assert parserPool != null;
         final Document doc = parserPool.parse(getClass().getResourceAsStream("zork.xml"));
         final DDF ddf = DDFSupport.fromElement(doc.getDocumentElement());
         
diff --git a/sp-server-api/src/test/java/net/shibboleth/sp/ddf/DDFTest.java b/sp-server-api/src/test/java/net/shibboleth/sp/ddf/DDFTest.java
index 923024f..095baac 100644
--- a/sp-server-api/src/test/java/net/shibboleth/sp/ddf/DDFTest.java
+++ b/sp-server-api/src/test/java/net/shibboleth/sp/ddf/DDFTest.java
@@ -35,6 +35,7 @@ import net.shibboleth.shared.collection.Pair;
 /**
  * DDF unit tests.
  */
+ at SuppressWarnings("javadoc")
 public class DDFTest {
 
     @Test
@@ -130,7 +131,9 @@ public class DDFTest {
         assertEquals(obj.integer(), Integer.valueOf(3));
         
         for (final DDF el : obj) {
-            switch (el.name()) {
+            final String name = el.name();
+            assert name != null;
+            switch (name) {
                 case "foo":
                     assertEquals(el.string(), "bar");
                     break;
@@ -158,7 +161,9 @@ public class DDFTest {
 
         obj.addbefore(new DDF("foo4"), obj.getmember("[2]"));
         assertEquals(obj.integer(), Integer.valueOf(5));
-        assertTrue(obj.getmember("[2]").name().equals("foo4"));
+        final String name = obj.getmember("[2]").name();
+        assert name != null;
+        assertTrue(name.equals("foo4"));
         
         assertTrue(obj.asList().get(4).remove().ispointer());
         assertEquals(obj.integer(), Integer.valueOf(4));
@@ -172,8 +177,8 @@ public class DDFTest {
         
         obj.add(new DDF("foo", "bar"));
         assertEquals(obj.integer(), Integer.valueOf(1));
-        assertTrue(obj.getmember("foo").name().equals("foo"));
-        assertTrue(obj.getmember("foo").string().equals("bar"));
+        assertEquals(obj.getmember("foo").name(), "foo");
+        assertEquals(obj.getmember("foo").string(), "bar");
         
         obj.addmember("foo2").integer(42);
         assertEquals(obj.integer(), Integer.valueOf(2));
@@ -182,8 +187,8 @@ public class DDFTest {
         assertEquals(obj.integer(), Integer.valueOf(2));
         assertTrue(obj.getmember("foo2").isstruct());
         assertEquals(obj.getmember("foo2").integer(), Integer.valueOf(1));
-        assertTrue(obj.getmember("foo2").getmember("foo3").string().equals("bar3"));
-        assertTrue(obj.getmember("foo2.foo3").string().equals("bar3"));
+        assertEquals(obj.getmember("foo2").getmember("foo3").string(), "bar3");
+        assertEquals(obj.getmember("foo2.foo3").string(), "bar3");
     }
     
     @Test
diff --git a/sp-server-api/src/test/java/net/shibboleth/sp/ddf/RemotedHttpServletRequestTest.java b/sp-server-api/src/test/java/net/shibboleth/sp/ddf/RemotedHttpServletRequestTest.java
index 8bf34ff..2470ebd 100644
--- a/sp-server-api/src/test/java/net/shibboleth/sp/ddf/RemotedHttpServletRequestTest.java
+++ b/sp-server-api/src/test/java/net/shibboleth/sp/ddf/RemotedHttpServletRequestTest.java
@@ -31,6 +31,7 @@ import org.testng.reporters.Files;
 /**
  * Unit test for {@link RemotedHttpServletRequest}.
  */
+ at SuppressWarnings("javadoc")
 public class RemotedHttpServletRequestTest {
     
     private DDF obj;
diff --git a/sp-server-api/src/test/java/net/shibboleth/sp/ddf/RemotedHttpServletResponseTest.java b/sp-server-api/src/test/java/net/shibboleth/sp/ddf/RemotedHttpServletResponseTest.java
index 4de6dcb..338ff2f 100644
--- a/sp-server-api/src/test/java/net/shibboleth/sp/ddf/RemotedHttpServletResponseTest.java
+++ b/sp-server-api/src/test/java/net/shibboleth/sp/ddf/RemotedHttpServletResponseTest.java
@@ -33,6 +33,7 @@ import org.testng.annotations.Test;
 /**
  * Unit test for {@link RemotedHttpServletResponse}.
  */
+ at SuppressWarnings("javadoc")
 public class RemotedHttpServletResponseTest {
     
     private DDF obj;
diff --git a/sp-server-impl/pom.xml b/sp-server-impl/pom.xml
index ee247d8..eb3352f 100644
--- a/sp-server-impl/pom.xml
+++ b/sp-server-impl/pom.xml
@@ -20,7 +20,7 @@
     </properties>
         
     <dependencies>
-        <!-- compile dependencies -->
+        <!-- Compile dependencies -->
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>sp-server-api</artifactId>
diff --git a/sp-session-api/pom.xml b/sp-session-api/pom.xml
index c064e97..6818c7c 100644
--- a/sp-session-api/pom.xml
+++ b/sp-session-api/pom.xml
@@ -21,6 +21,13 @@
         
     <dependencies>
         <!-- Compile dependencies -->
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>sp-server-api</artifactId>
+            <version>${project.version}</version>
+            <scope>compile</scope>
+        </dependency>
+
         <dependency>
             <groupId>net.shibboleth</groupId>
             <artifactId>shib-attribute-api</artifactId>
diff --git a/sp-session-api/src/main/java/net/shibboleth/sp/session/AbstractRemotableSPSession.java b/sp-session-api/src/main/java/net/shibboleth/sp/session/AbstractRemotableSPSession.java
new file mode 100644
index 0000000..cd57e3b
--- /dev/null
+++ b/sp-session-api/src/main/java/net/shibboleth/sp/session/AbstractRemotableSPSession.java
@@ -0,0 +1,98 @@
+/*
+ * 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.sp.session;
+
+import java.time.Instant;
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+
+import com.google.common.collect.Multimap;
+
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+import net.shibboleth.sp.ddf.DDF;
+
+/**
+ * Abstract base class for a {@link RemotableSPSession}.
+ */
+public abstract class AbstractRemotableSPSession implements RemotableSPSession {
+
+    /** Underlying object housing session. */
+    @Nonnull final DDF obj;
+    
+    /**
+     * Constructor.
+     *
+     * @param id session identifier
+     */
+    protected AbstractRemotableSPSession(@Nonnull @NotEmpty final String id) {
+        obj = new DDF(id);
+    }
+    
+    /** {@inheritDoc} */
+    @Nonnull @NotEmpty public String getId() {
+        return obj.name();
+    }
+
+    /** {@inheritDoc} */
+    @Nonnull public Instant getCreation() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Nonnull public Instant getLastActivity() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Nonnull public Instant getExpiration() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Nonnull @NonnullElements @NotLive @Unmodifiable public Multimap<String,IdPAttribute> getAttributes() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    public void addAttributes(@Nonnull @NonnullElements final Collection<IdPAttribute> attributes) {
+        // TODO Auto-generated method stub
+
+    }
+
+    /** {@inheritDoc} */
+    public void removeAttribute(@Nonnull @NonnullElements final Collection<IdPAttribute> attributes) {
+        // TODO Auto-generated method stub
+
+    }
+
+    /** {@inheritDoc} */
+    @Nonnull public DDF serialize() throws SessionException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+}
\ No newline at end of file
diff --git a/sp-session-api/src/main/java/net/shibboleth/sp/session/RemotableSPSession.java b/sp-session-api/src/main/java/net/shibboleth/sp/session/RemotableSPSession.java
new file mode 100644
index 0000000..a7efab2
--- /dev/null
+++ b/sp-session-api/src/main/java/net/shibboleth/sp/session/RemotableSPSession.java
@@ -0,0 +1,38 @@
+/*
+ * 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.sp.session;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.sp.ddf.DDF;
+
+/**
+ * Extension for sessions that can generate a {@link DDF} representation.
+ */
+public interface RemotableSPSession extends SPSession {
+
+    /**
+     * Serialize the session to a {@link DDF}.
+     * 
+     * @return the serialized session
+     * 
+     * @throws SessionException if unable to serialize
+     */
+    @Nonnull DDF serialize() throws SessionException;
+
+}
\ No newline at end of file
diff --git a/sp-session-api/src/main/java/net/shibboleth/sp/session/SPSession.java b/sp-session-api/src/main/java/net/shibboleth/sp/session/SPSession.java
new file mode 100644
index 0000000..38f950a
--- /dev/null
+++ b/sp-session-api/src/main/java/net/shibboleth/sp/session/SPSession.java
@@ -0,0 +1,105 @@
+/*
+ * 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.sp.session;
+
+import java.time.Instant;
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.ThreadSafe;
+
+import com.google.common.collect.Multimap;
+
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+import net.shibboleth.shared.component.IdentifiedComponent;
+
+/**
+ * A service provider session belonging to a particular subject and client device.
+ */
+ at ThreadSafe
+public interface SPSession extends IdentifiedComponent {
+
+    /**
+     * Get the time when this session was created.
+     * 
+     * @return time this session was created
+     */
+    @Nonnull Instant getCreation();
+    
+    /**
+     * Get the last activity time for the session.
+     * 
+     * <p>This is generally an approximation based on periodic
+     * updating of the value by external agents.</p>
+     * 
+     * @return last activity for the session
+     */
+    @Nonnull Instant getLastActivity();
+    
+    /**
+     * Get the expected expiration time of the session.
+     * 
+     * @return expiration time
+     */
+    @Nonnull Instant getExpiration();
+    
+    /**
+     * Get indexed map of {@link IdPAttribute} objects associated with this session.
+     * 
+     * @return unmodifiable attribute map
+     */
+    @Nonnull @NonnullElements @NotLive @Unmodifiable public Multimap<String,IdPAttribute> getAttributes();
+    
+    /**
+     * Add new {@link IdPAttribute} objects to the session.
+     * 
+     * @param attributes attributes to add
+     */
+    void addAttributes(@Nonnull @NonnullElements final Collection<IdPAttribute> attributes);
+
+    /**
+     * Remove {@link IdPAttribute} objects from the session.
+     * 
+     * @param attributes attributes to remove
+     */
+    void removeAttribute(@Nonnull @NonnullElements final Collection<IdPAttribute> attributes);
+    
+    /**
+     * Test the session's validity based on the supplied client address, possibly binding it
+     * to the session if appropriate.
+     * 
+     * @param address client address for validation
+     * 
+     * @return true iff the session is valid for the specified client address
+     * @throws SessionException if an error occurs binding the address to the session
+     */
+    boolean checkAddress(@Nonnull @NotEmpty final String address) throws SessionException;
+    
+    /**
+     * Test the session's validity based on inactivity, while updating the last activity time.
+     * 
+     * @return true iff the session is still valid
+     * @throws SessionException if an error occurs updating the activity time
+     */
+    boolean checkTimeout() throws SessionException;
+
+}
\ No newline at end of file
diff --git a/sp-session-api/src/main/java/net/shibboleth/sp/session/SessionException.java b/sp-session-api/src/main/java/net/shibboleth/sp/session/SessionException.java
new file mode 100644
index 0000000..65c955e
--- /dev/null
+++ b/sp-session-api/src/main/java/net/shibboleth/sp/session/SessionException.java
@@ -0,0 +1,61 @@
+/*
+ * 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.sp.session;
+
+import javax.annotation.Nullable;
+
+/** Exception indicating a problem with the session layer. */
+public class SessionException extends Exception {
+
+    /** Serial UUID. */
+    private static final long serialVersionUID = 8243844915411488275L;
+
+    /** Constructor. */
+    public SessionException() {
+        
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param message exception message
+     */
+    public SessionException(@Nullable final String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param wrappedException exception to be wrapped by this one
+     */
+    public SessionException(@Nullable final Exception wrappedException) {
+        super(wrappedException);
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param message exception message
+     * @param wrappedException exception to be wrapped by this one
+     */
+    public SessionException(@Nullable final String message, @Nullable final Exception wrappedException) {
+        super(message, wrappedException);
+    }
+
+}
\ No newline at end of file
diff --git a/sp-session-api/src/main/java/net/shibboleth/sp/session/package-info.java b/sp-session-api/src/main/java/net/shibboleth/sp/session/package-info.java
new file mode 100644
index 0000000..ae1c4b6
--- /dev/null
+++ b/sp-session-api/src/main/java/net/shibboleth/sp/session/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * SP Session APIs.
+ */
+package net.shibboleth.sp.session;
\ 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