[java-plugin-shibd] branch main updated: Add raw handling of URLs internally to Statedata class.

Codeberg noreply at shibboleth.net
Wed Apr 22 14:13:29 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/ee9532033ab7870373b89631d91c10ebf6877585

The following commit(s) were added to refs/heads/main by this push:
     new ee95320  Add raw handling of URLs internally to Statedata class.
ee95320 is described below

commit ee9532033ab7870373b89631d91c10ebf6877585
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Wed Apr 22 10:13:12 2026 -0400

    Add raw handling of URLs internally to Statedata class.
---
 .../java/net/shibboleth/sp/state/StateData.java    | 115 ++++++++++++++++++++-
 .../sp/state/impl/CookieStateManagerTest.java      |   5 +
 .../state/impl/StorageServiceStateManagerTest.java |   7 +-
 3 files changed, 122 insertions(+), 5 deletions(-)

diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/state/StateData.java b/sp-server-api/src/main/java/net/shibboleth/sp/state/StateData.java
index 4eaafe4..44571c5 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/state/StateData.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/state/StateData.java
@@ -14,7 +14,8 @@
 
 package net.shibboleth.sp.state;
 
-
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
 import java.time.Instant;
 import java.util.List;
 import java.util.Objects;
@@ -23,6 +24,7 @@ import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.NotThreadSafe;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.google.common.base.MoreObjects;
@@ -38,9 +40,10 @@ import net.shibboleth.shared.collection.CollectionSupport;
  * 
  * <p>This class is designed to be extended to support non-generic protocol state as required.</p>
  * 
- * <p>Any URLs managed by this class are typed as String and strictly opaque to this layer so as to
+ * <p>Any URLs managed by this class are typed internally as String and are opaque to this class so as to
  * support any character encoding necessary without assuming UTF-8. Callers should take care to encode
- * any URLs as required (e.g., even base64 encoding is acceptable).</p>
+ * any URLs as required (e.g., even base64 encoding is acceptable), or use the methods suitable for
+ * operating to and from byte arrays to allow the class to address it.</p>
  */
 @JsonInclude(JsonInclude.Include.NON_EMPTY)
 @NotThreadSafe
@@ -190,6 +193,7 @@ public class StateData {
         }
         return this;
     }
+    
     /**
      * Get the location to which the response is expected to be sent.
      * 
@@ -212,6 +216,38 @@ public class StateData {
         return this;
     }
 
+    /**
+     * Get the location to which the response is expected to be sent in a raw form.
+     * 
+     * <p>The value retrieved must have previously been set via the
+     * {@link StateData#setRawResponseLocation(byte[])} method.</p>
+     * 
+     * @return the expected response location as a byte array
+     */
+    @Nullable public byte[] getRawResponseLocation() {
+        if (responseLocation != null) {
+            return decode(responseLocation);
+        } else {
+            return null;
+        }
+    }
+    
+    /**
+     * Set the location to which the response is expected to be sent in a raw form.
+     * 
+     * @param loc the location as a byte array
+     * 
+     * @return the updated object
+     */
+    public StateData setRawResponseLocation(@Nullable final byte[] loc) {
+        if (loc != null) {
+            responseLocation = encode(loc);
+        } else {
+            responseLocation = null;
+        }
+        return this;
+    }
+    
     /**
      * Get the resource location associated with the request.
      * 
@@ -234,7 +270,36 @@ public class StateData {
         return this;
     }
 
+    /**
+     * Get the resource location associated with the request in a raw form.
+     * 
+     * <p>The value retrieved must have previously been set via the {@link #setRawResource(byte[])} method.</p>
+     * 
+     * @return resource as a byte array
+     */
+    @Nullable public byte[] getRawResource() {
+        if (resource != null) {
+            return decode(resource);
+        } else {
+            return null;
+        }
+    }
     
+    /**
+     * Set the resource location associated with the request in a raw form.
+     * 
+     * @param loc the location
+     * 
+     * @return the updated object
+     */
+    public StateData setRawResource(@Nullable final byte[] loc) {
+        if (loc != null) {
+            resource = encode(loc);
+        } else {
+            resource = null;
+        }
+        return this;
+    }    
     
     /** {@inheritDoc} */
     @Override
@@ -290,5 +355,47 @@ public class StateData {
         }
         return "****" + value.substring(value.length() - 2);
     }
-   
+
+    /**
+     * A simple encoder for non-ASCII characters.
+     * 
+     * @param bytes bytes to encode
+     * 
+     * @return the encoded string
+     */
+    @SuppressWarnings("null")
+    @Nonnull protected static String encode(@Nonnull final byte[] bytes) {
+        final StringBuilder sb = new StringBuilder();
+        for (final byte b : bytes) {
+            final int i = Byte.toUnsignedInt(b);
+            if (i < 0x28 || i > 0x7A) {
+                sb.append('%');
+                sb.append((char) hexchar(i >>> 4));
+                sb.append((char) hexchar(i & 0x0F));
+            } else {
+                sb.append((char) b);
+            }
+        }
+        return sb.toString();
+    }
+    
+    @SuppressWarnings("null")
+    @Nonnull protected static byte[] decode(@Nonnull final String s) {
+        // Values are processed as ISO-8859-1.
+        // They may be anything, but it will guarantee a single byte encoding.
+        return URLDecoder.decode(s, StandardCharsets.ISO_8859_1).getBytes(StandardCharsets.ISO_8859_1);
+    }
+    
+    /**
+     * Converts a byte into a hex character.
+     * 
+     * @param b input byte
+     * 
+     * @return the hex character equivalent (capitalized)
+     */
+    private static int hexchar(final int b) {
+        // 48 is '0' and 65 is 'A'
+        return (b <= 9) ? (48 + b) : (65 + b - 10);
+    }
+    
 }
\ No newline at end of file
diff --git a/sp-server-impl/src/test/java/net/shibboleth/sp/state/impl/CookieStateManagerTest.java b/sp-server-impl/src/test/java/net/shibboleth/sp/state/impl/CookieStateManagerTest.java
index d177711..8689881 100644
--- a/sp-server-impl/src/test/java/net/shibboleth/sp/state/impl/CookieStateManagerTest.java
+++ b/sp-server-impl/src/test/java/net/shibboleth/sp/state/impl/CookieStateManagerTest.java
@@ -15,6 +15,7 @@
 package net.shibboleth.sp.state.impl;
 
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.List;
@@ -53,6 +54,9 @@ public class CookieStateManagerTest extends BaseApplicationActionTest {
 
     @Nonnull @NotEmpty private static final String TEST_AUTHORITY = "https://idp.example.org";
     
+    // Unicode character at the end of that filename...
+    @Nonnull @NotEmpty private static final String TEST_RESOURCE = "https://sp.example.org/secure/foo☯.cgi";
+    
     private CookieManager cookieManager;
     private CookieStateManager stateManager;
     
@@ -227,6 +231,7 @@ public class CookieStateManagerTest extends BaseApplicationActionTest {
         data.setRequestTime(Instant.now());
         data.setIssuer(TEST_ISSUER);
         data.setAuthenticationAuthority(TEST_AUTHORITY);
+        data.setRawResource(TEST_RESOURCE.getBytes(StandardCharsets.UTF_8));
         return data;
     }
 
diff --git a/sp-server-impl/src/test/java/net/shibboleth/sp/state/impl/StorageServiceStateManagerTest.java b/sp-server-impl/src/test/java/net/shibboleth/sp/state/impl/StorageServiceStateManagerTest.java
index 7b6f1db..366747e 100644
--- a/sp-server-impl/src/test/java/net/shibboleth/sp/state/impl/StorageServiceStateManagerTest.java
+++ b/sp-server-impl/src/test/java/net/shibboleth/sp/state/impl/StorageServiceStateManagerTest.java
@@ -15,6 +15,7 @@
 package net.shibboleth.sp.state.impl;
 
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.time.Duration;
 import java.time.Instant;
 
@@ -51,7 +52,10 @@ public class StorageServiceStateManagerTest extends BaseApplicationActionTest {
 
     @Nonnull @NotEmpty private static final String TEST_ISSUER = "https://sp.example.org";
 
-    @Nonnull @NotEmpty private static final String TEST_AUTHORITY = "https://idp.example.org";    
+    @Nonnull @NotEmpty private static final String TEST_AUTHORITY = "https://idp.example.org";
+
+    // Unicode character at the end of that filename...
+    @Nonnull @NotEmpty private static final String TEST_RESOURCE = "https://sp.example.org/secure/foo☯.cgi";
     
     private MockHttpServletRequest request;
     private MockHttpServletResponse response;
@@ -277,6 +281,7 @@ public class StorageServiceStateManagerTest extends BaseApplicationActionTest {
         data.setRequestTime(Instant.now());
         data.setIssuer(TEST_ISSUER);
         data.setAuthenticationAuthority(TEST_AUTHORITY);
+        data.setRawResource(TEST_RESOURCE.getBytes(StandardCharsets.UTF_8));
         return data;
     }
     

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


More information about the commits mailing list