[java-plugin-shibd] branch main updated: Cached authentication action and unit test.
Scott Cantor
cantor.2 at osu.edu
Wed May 22 19:22:41 UTC 2024
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch main
in repository java-plugin-shibd.
View the commit online:
http://git.shibboleth.net/view/?p=java-plugin-shibd.git;a=commit;h=ebc1e1038354c26b90cd615624d9aaa9f09252ae
The following commit(s) were added to refs/heads/main by this push:
new ebc1e10 Cached authentication action and unit test.
ebc1e10 is described below
commit ebc1e1038354c26b90cd615624d9aaa9f09252ae
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed May 22 15:22:38 2024 -0400
Cached authentication action and unit test.
---
pom.xml | 2 +-
.../idp/flows/sp/abstract/sp-abstract-beans.xml | 14 ++
.../idp/flows/sp/abstract/sp-abstract-flow.xml | 5 +-
sp-server-impl/pom.xml | 17 +-
.../authn/impl/ValidateCachedAuthentication.java | 66 ++++--
.../sp/authn/impl/TestResourceConverter.java | 127 +++++++++++
.../impl/ValidateCachedAuthenticationTest.java | 249 +++++++++++++++++++++
.../shibboleth/sp/authn/impl/SealerKeyStore.jks | Bin 0 -> 984 bytes
.../shibboleth/sp/authn/impl/SealerKeyStore.kver | 1 +
9 files changed, 454 insertions(+), 27 deletions(-)
diff --git a/pom.xml b/pom.xml
index 102ade4..9a3c976 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,7 +67,7 @@
<artifactId>shib-support</artifactId>
<scope>provided</scope>
</dependency>
-
+
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
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 f548136..2ebd014 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
@@ -39,4 +39,18 @@
class="net.shibboleth.sp.authn.impl.ValidateAgentAddress" scope="prototype"
p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier" />
+ <bean id="ValidateCachedAuthentication"
+ class="net.shibboleth.sp.authn.impl.ValidateCachedAuthentication" scope="prototype"
+ p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
+ p:cookieName="%{sp.agent.cachedAuthentication.cookie:__Host-shibsp_agent_token}"
+ p:cookieManager-ref="sp.CookieManager"
+ p:dataSealer-ref="shibboleth.DataSealer" />
+
+ <bean id="sp.CookieManager" parent="shibboleth.PersistentCookieManager"
+ p:secure="true"
+ p:httpOnly="true"
+ p:cookieDomain=""
+ p:cookiePath="/"
+ p:maxAge="%{sp.agent.cachedAuthentication.maxAge:3600}" />
+
</beans>
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 47c2b43..298c66f 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
@@ -20,8 +20,9 @@
<evaluate expression="ValidateCachedAuthentication" />
<evaluate expression="'proceed'" />
- <transition on="proceed" to="DecodeMessage" />
- <transition on="NoCredentials" to="DoAuthentication" />
+ <!-- Inverting the usual approach, proceed means "not cached". -->
+ <transition on="proceed" to="DoAuthentication" />
+ <transition on="BypassAuthentication" to="DecodeMessage" />
</action-state>
<action-state id="DoAuthentication">
diff --git a/sp-server-impl/pom.xml b/sp-server-impl/pom.xml
index 5caa179..d9f0b83 100644
--- a/sp-server-impl/pom.xml
+++ b/sp-server-impl/pom.xml
@@ -87,6 +87,16 @@
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>${shib-shared.groupId}</groupId>
+ <artifactId>shib-networking</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>${shib-shared.groupId}</groupId>
+ <artifactId>shib-security</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>${shib-shared.groupId}</groupId>
<artifactId>shib-service</artifactId>
@@ -109,16 +119,13 @@
<artifactId>jcl-over-slf4j</artifactId>
<scope>provided</scope>
</dependency>
+
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
- <dependency>
- <groupId>com.beust</groupId>
- <artifactId>jcommander</artifactId>
- <scope>provided</scope>
- </dependency>
+
<!-- Test Dependencies -->
<dependency>
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/authn/impl/ValidateCachedAuthentication.java b/sp-server-impl/src/main/java/net/shibboleth/sp/authn/impl/ValidateCachedAuthentication.java
index 6358694..90ce90e 100644
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/authn/impl/ValidateCachedAuthentication.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/authn/impl/ValidateCachedAuthentication.java
@@ -36,6 +36,7 @@ import net.shibboleth.shared.net.CookieManager;
import net.shibboleth.shared.net.URISupport;
import net.shibboleth.shared.primitive.LoggerFactory;
import net.shibboleth.shared.primitive.StringSupport;
+import net.shibboleth.shared.security.DataExpiredException;
import net.shibboleth.shared.security.DataSealer;
import net.shibboleth.shared.security.DataSealerException;
import net.shibboleth.shared.servlet.HttpServletSupport;
@@ -46,17 +47,24 @@ import net.shibboleth.sp.context.AgentRequestContext;
* An action that checks for a sealed cookie authenticating request without the need for
* validating a shared secret or other credentials.
*
- * <p>The cookie is an address-bound bearer token containing the agent authorized to use it,
- * the destination, the address, an expiration, etc.</p>
+ * <p>For safety's sake, the default event signal indicates that authentication should
+ * <strong>not</strong> be bypassed. An explicit event is used to signal bypass.</p>
+ *
+ * <p>The cookie is an address-bound bearer token containing the agent authorized to use it.
+ * The data being sealed, it has an internal expiration independently of the cookie's own.</p>
*
- * <p>TODO: adding a key proof via MAC in some way</p>
+ * <p>TODO: adding a key proof via MAC in some way</p>
*
* @event {@link EventIds#PROCEED_EVENT_ID}
* @event {@link EventIds#INVALID_PROFILE_CTX}
+ * @event {@link #BYPASS_AUTHENTICATION}
* @pre <pre>ProfileRequestContext.ensureSubcontext(AgentRequestContext.class).getAgent() != null</pre>
*/
public class ValidateCachedAuthentication extends AbstractProfileAction {
+ /** Bypass event indicating cookie was accepted. */
+ @Nonnull @NotEmpty public static final String BYPASS_AUTHENTICATION = "BypassAuthentication";
+
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(ValidateCachedAuthentication.class);
@@ -152,7 +160,6 @@ public class ValidateCachedAuthentication extends AbstractProfileAction {
if (!agent.isSupportsCachedAuthentication()) {
log.debug("{} Agent '{}' does not support cached authentication, skipping", getLogPrefix(), agent.getId());
- ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
return false;
}
@@ -162,33 +169,54 @@ public class ValidateCachedAuthentication extends AbstractProfileAction {
/** {@inheritDoc} */
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- final HttpServletRequest request = getHttpServletRequest();
- final String addr = request != null ? HttpServletSupport.getRemoteAddr(request) : null;
- if (addr == null) {
- log.warn("{} No client address for request from agent '{}', skipping cached authentication check", getLogPrefix(),
- agent.getId());
- ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
- return;
- }
try {
assert cookieName != null;
final String wrapped = cookieManager.getCookieValue(cookieName, null);
if (wrapped == null) {
- log.debug("{} No cookie in request from agent '{}', skipping cached authentication check", getLogPrefix(),
+ log.debug("{} No cookie from agent '{}', skipping cached authentication check", getLogPrefix(),
agent.getId());
- ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
return;
}
final String unwrapped = dataSealer.unwrap(URISupport.doURLDecode(wrapped));
-
- // TODO parse and validate...
-
+ if (isValid(profileRequestContext, unwrapped)) {
+ log.info("{} Accepted cookie from agent '{}', skipping full authentication", getLogPrefix(),
+ agent.getId());
+ ActionSupport.buildEvent(profileRequestContext, BYPASS_AUTHENTICATION);
+ } else {
+ log.debug("{} Rejected cookie from agent '{}', full authentication will proceed", getLogPrefix(),
+ agent.getId());
+ }
+ } catch (final DataExpiredException e) {
+ log.debug("{} Cookie from agent '{}' expired, authentication not bypassed", getLogPrefix(), agent.getId());
} catch (final DataSealerException e) {
- log.warn("{} Error decrypting cookie from agent '{}', authentication not bypassed", getLogPrefix(), agent.getId(), e);
- ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
+ log.warn("{} Error decrypting cookie from agent '{}', authentication not bypassed", getLogPrefix(),
+ agent.getId(), e);
+ }
+ }
+
+ private boolean isValid(@Nonnull final ProfileRequestContext profileRequestContext, @Nonnull final String cookie) {
+ final String[] split = cookie.split("!");
+ if (split == null || split.length != 2) {
+ log.warn("{} Cookie malformed from agent '{}'", getLogPrefix(), agent.getId());
+ return false;
+ }
+
+ if (!split[0].equals(agent.getId())) {
+ log.warn("{} Cookie from agent '{}' issued to agent '{}'", getLogPrefix(), agent.getId(), split[0]);
+ return false;
+ }
+
+ final HttpServletRequest request = getHttpServletRequest();
+ final String addr = request != null ? HttpServletSupport.getRemoteAddr(request) : null;
+ if (!split[1].equals(addr)) {
+ log.warn("{} Cookie from agent '{}' and address {} issued to address {}", getLogPrefix(), agent.getId(),
+ split[1], addr);
+ return false;
}
+
+ return true;
}
}
\ No newline at end of file
diff --git a/sp-server-impl/src/test/java/net/shibboleth/sp/authn/impl/TestResourceConverter.java b/sp-server-impl/src/test/java/net/shibboleth/sp/authn/impl/TestResourceConverter.java
new file mode 100644
index 0000000..04e7ad7
--- /dev/null
+++ b/sp-server-impl/src/test/java/net/shibboleth/sp/authn/impl/TestResourceConverter.java
@@ -0,0 +1,127 @@
+/*
+ * 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.authn.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+
+import javax.annotation.Nonnull;
+
+import org.springframework.core.io.Resource;
+
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * Bridging class between {@link Resource} and {@link net.shibboleth.shared.resource.Resource}.
+ */
+public final class TestResourceConverter implements net.shibboleth.shared.resource.Resource {
+
+ /** The cached Spring {@link Resource}. */
+ private Resource springResource;
+
+ /**
+ * A private for shimming the provided input.
+ *
+ * @param theResource the spring resource;
+ */
+ private TestResourceConverter(@Nonnull Resource theResource) {
+
+ springResource = Constraint.isNotNull(theResource, "provided Spring Resource should not be null");
+ }
+
+ /**
+ * Return a {@link Resource} that does all the work of the provided {@link Resource}.
+ *
+ * <p>
+ * If the input implements {@link Resource} then it is cast to the output, other a shim class is
+ * generated.
+ * </p>
+ *
+ * @param springResource the input
+ * @return a {@link Resource} which reflects what the Spring one does
+ */
+ @Nonnull public static net.shibboleth.shared.resource.Resource of(@Nonnull Resource springResource) {
+ if (springResource instanceof net.shibboleth.shared.resource.Resource) {
+ return (net.shibboleth.shared.resource.Resource) springResource;
+ }
+ return new TestResourceConverter(springResource);
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nonnull public InputStream getInputStream() throws IOException {
+ return springResource.getInputStream();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean exists() {
+ return springResource.exists();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isReadable() {
+ return springResource.isReadable();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isOpen() {
+ return springResource.isOpen();
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nonnull public URL getURL() throws IOException {
+ return springResource.getURL();
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nonnull public URI getURI() throws IOException {
+ return springResource.getURI();
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nonnull public File getFile() throws IOException {
+ return springResource.getFile();
+ }
+
+ /** {@inheritDoc} */
+ @Override public long contentLength() throws IOException {
+ return springResource.contentLength();
+ }
+
+ /** {@inheritDoc} */
+ @Override public long lastModified() throws IOException {
+ return springResource.lastModified();
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nonnull public net.shibboleth.shared.resource.Resource createRelativeResource(
+ @Nonnull String relativePath) throws IOException {
+
+ return of(springResource.createRelative(relativePath));
+ }
+
+ /** {@inheritDoc} */
+ @Override public String getFilename() {
+ return springResource.getFilename();
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nonnull public String getDescription() {
+ return springResource.getDescription();
+ }
+
+}
diff --git a/sp-server-impl/src/test/java/net/shibboleth/sp/authn/impl/ValidateCachedAuthenticationTest.java b/sp-server-impl/src/test/java/net/shibboleth/sp/authn/impl/ValidateCachedAuthenticationTest.java
new file mode 100644
index 0000000..6585158
--- /dev/null
+++ b/sp-server-impl/src/test/java/net/shibboleth/sp/authn/impl/ValidateCachedAuthenticationTest.java
@@ -0,0 +1,249 @@
+/*
+ * 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.authn.impl;
+
+import java.time.Instant;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.webflow.execution.Event;
+import org.springframework.webflow.execution.RequestContext;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.net.UrlEscapers;
+
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
+import net.shibboleth.idp.profile.testing.ActionTestingSupport;
+import net.shibboleth.idp.profile.testing.RequestContextBuilder;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.net.CookieManager;
+import net.shibboleth.shared.primitive.NonnullSupplier;
+import net.shibboleth.shared.resource.Resource;
+import net.shibboleth.shared.security.DataSealer;
+import net.shibboleth.shared.security.DataSealerException;
+import net.shibboleth.shared.security.impl.BasicKeystoreKeyStrategy;
+import net.shibboleth.sp.context.AgentRequestContext;
+import net.shibboleth.sp.impl.BasicAgent;
+
+/**
+ * Unit test for {@link ValidateCachedAuthentication} action.
+ */
+ at SuppressWarnings("javadoc")
+public class ValidateCachedAuthenticationTest {
+
+ @Nonnull @NotEmpty protected static final String COOKIE_NAME = "_cookieName";
+
+ private Resource keystoreResource;
+ private Resource versionResource;
+ private CookieManager cookieManager;
+ private DataSealer dataSealer;
+
+ private MockHttpServletRequest request;
+ private MockHttpServletResponse response;
+
+ private RequestContext src;
+ private ProfileRequestContext prc;
+ private BasicAgent agent;
+ private ValidateCachedAuthentication action;
+
+ private DataSealer createDataSealer(@Nullable @NotEmpty final String nodePrefix)
+ throws DataSealerException, ComponentInitializationException {
+ final BasicKeystoreKeyStrategy strategy = new BasicKeystoreKeyStrategy();
+
+ strategy.setKeyAlias("secret");
+ strategy.setKeyPassword("kpassword");
+
+ strategy.setKeystorePassword("password");
+ strategy.setKeystoreResource(keystoreResource);
+
+ strategy.setKeyVersionResource(versionResource);
+
+ strategy.initialize();
+
+ final DataSealer sealer = new DataSealer();
+ sealer.setKeyStrategy(strategy);
+ sealer.setNodePrefix(nodePrefix);
+ sealer.initialize();
+ return sealer;
+ }
+
+ @BeforeClass
+ public void beforeClass() throws DataSealerException, ComponentInitializationException {
+ ClassPathResource resource =
+ new ClassPathResource("/net/shibboleth/sp/authn/impl/SealerKeyStore.jks");
+ Assert.assertTrue(resource.exists());
+ keystoreResource = TestResourceConverter.of(resource);
+
+ resource =
+ new ClassPathResource("/net/shibboleth/sp/authn/impl/SealerKeyStore.kver");
+ Assert.assertTrue(resource.exists());
+ versionResource = TestResourceConverter.of(resource);
+
+ dataSealer = createDataSealer(null);
+ }
+
+ @BeforeMethod
+ public void setUp() throws ComponentInitializationException {
+ src = new RequestContextBuilder().buildRequestContext();
+ prc = new WebflowRequestContextProfileRequestContextLookup().apply(src);
+ prc.ensureSubcontext(AuthenticationContext.class);
+
+ request = new MockHttpServletRequest();
+ request.setRemoteAddr("127.0.0.1");
+ response = new MockHttpServletResponse();
+
+ cookieManager = new CookieManager();
+ cookieManager.setHttpServletRequestSupplier(new NonnullSupplier<>() { @Nonnull public HttpServletRequest get() {return request;}});
+ cookieManager.setHttpServletResponseSupplier(new NonnullSupplier<>() { @Nonnull public HttpServletResponse get() {return response;}});
+ cookieManager.initialize();
+
+ agent = new BasicAgent();
+ agent.setId("foo");
+ agent.initialize();
+
+ action = new ValidateCachedAuthentication();
+ action.setHttpServletRequestSupplier(new NonnullSupplier<>() { @Nonnull public HttpServletRequest get() {return request;}});
+ action.setCookieName(COOKIE_NAME);
+ action.setCookieManager(cookieManager);
+ action.setDataSealer(dataSealer);
+ action.initialize();
+ }
+ @Test
+ public void testNoAgent() {
+ final Event event = action.execute(src);
+ ActionTestingSupport.assertEvent(event, EventIds.INVALID_PROFILE_CTX);
+ }
+
+ @Test
+ public void testNoCaching() throws ComponentInitializationException {
+ agent = new BasicAgent();
+ agent.setId("foo");
+ agent.setSupportsCachedAuthentication(false);
+ agent.initialize();
+ prc.ensureSubcontext(AgentRequestContext.class).setAgent(agent);
+ final Event event = action.execute(src);
+ ActionTestingSupport.assertProceedEvent(event);
+ }
+
+ @Test
+ public void testNoCookie() throws ComponentInitializationException {
+ prc.ensureSubcontext(AgentRequestContext.class).setAgent(agent);
+ final Event event = action.execute(src);
+ ActionTestingSupport.assertProceedEvent(event);
+ }
+
+ @Test
+ public void testBadCookie() throws ComponentInitializationException {
+ prc.ensureSubcontext(AgentRequestContext.class).setAgent(agent);
+
+ request.setCookies(new Cookie(COOKIE_NAME, "zork"));
+
+ final Event event = action.execute(src);
+ ActionTestingSupport.assertProceedEvent(event);
+ }
+
+ @Test
+ public void testExoiredCookie() throws ComponentInitializationException, DataSealerException {
+ prc.ensureSubcontext(AgentRequestContext.class).setAgent(agent);
+
+ request.setCookies(buildCookie(agent.getId(), "127.0.0.1", Instant.now().minusSeconds(30)));
+
+ final Event event = action.execute(src);
+ ActionTestingSupport.assertProceedEvent(event);
+ }
+
+ @Test
+ public void testWrongAgent() throws ComponentInitializationException, DataSealerException {
+ prc.ensureSubcontext(AgentRequestContext.class).setAgent(agent);
+
+ request.setCookies(buildCookie("wrong", "127.0.0.1", Instant.now().plusSeconds(3600)));
+
+ final Event event = action.execute(src);
+ ActionTestingSupport.assertProceedEvent(event);
+ }
+
+ @Test
+ public void testWrongAddress() throws ComponentInitializationException, DataSealerException {
+ prc.ensureSubcontext(AgentRequestContext.class).setAgent(agent);
+
+ request.setCookies(buildCookie(agent.getId(), "127.0.0.2", Instant.now().plusSeconds(3600)));
+
+ final Event event = action.execute(src);
+ ActionTestingSupport.assertProceedEvent(event);
+ }
+
+ @Test
+ public void testWrongFormat() throws ComponentInitializationException, DataSealerException {
+ prc.ensureSubcontext(AgentRequestContext.class).setAgent(agent);
+
+ request.setCookies(buildCookie(null, "127.0.0.1", Instant.now().plusSeconds(3600)));
+
+ final Event event = action.execute(src);
+ ActionTestingSupport.assertProceedEvent(event);
+ }
+
+ @Test
+ public void testSuccess() throws ComponentInitializationException, DataSealerException {
+ prc.ensureSubcontext(AgentRequestContext.class).setAgent(agent);
+
+ request.setCookies(buildCookie(agent.getId(), "127.0.0.1", Instant.now().plusSeconds(3600)));
+
+ final Event event = action.execute(src);
+ ActionTestingSupport.assertEvent(event, ValidateCachedAuthentication.BYPASS_AUTHENTICATION);
+ }
+
+ /**
+ * Build a sealed cookie to order.
+ *
+ * @param agentId agent ID
+ * @param address client adddress
+ * @param expires data expiration
+ *
+ * @return the cookie
+ *
+ * @throws DataSealerException if sealer fails
+ */
+ @Nonnull private Cookie buildCookie(@Nullable final String agentId, @Nullable final String address,
+ @Nonnull final Instant expires) throws DataSealerException {
+
+ final StringBuilder builder = new StringBuilder();
+ if (agentId != null) {
+ builder.append(agentId);
+ if (address != null) {
+ builder.append('!').append(address);
+ }
+ } else if (address != null) {
+ builder.append(address);
+ }
+
+ final String wrapped = dataSealer.wrap(builder.toString(), expires);
+ return new Cookie(COOKIE_NAME, UrlEscapers.urlFormParameterEscaper().escape(wrapped));
+ }
+
+}
\ No newline at end of file
diff --git a/sp-server-impl/src/test/resources/net/shibboleth/sp/authn/impl/SealerKeyStore.jks b/sp-server-impl/src/test/resources/net/shibboleth/sp/authn/impl/SealerKeyStore.jks
new file mode 100644
index 0000000..147d92b
Binary files /dev/null and b/sp-server-impl/src/test/resources/net/shibboleth/sp/authn/impl/SealerKeyStore.jks differ
diff --git a/sp-server-impl/src/test/resources/net/shibboleth/sp/authn/impl/SealerKeyStore.kver b/sp-server-impl/src/test/resources/net/shibboleth/sp/authn/impl/SealerKeyStore.kver
new file mode 100644
index 0000000..2cd48df
--- /dev/null
+++ b/sp-server-impl/src/test/resources/net/shibboleth/sp/authn/impl/SealerKeyStore.kver
@@ -0,0 +1 @@
+CurrentVersion = 1
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list