[java-plugin-shibd] branch main updated: WIP on cached authentication.
Scott Cantor
cantor.2 at osu.edu
Tue May 21 20:54:54 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=c02548640ddc7d26a400b70e9509cbed284e9374
The following commit(s) were added to refs/heads/main by this push:
new c025486 WIP on cached authentication.
c025486 is described below
commit c02548640ddc7d26a400b70e9509cbed284e9374
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue May 21 16:54:51 2024 -0400
WIP on cached authentication.
---
.../shibboleth/idp/module/conf/sp/sp.properties | 2 +
.../net/shibboleth/sp/conf/agents-system.xml | 3 +-
.../src/main/java/net/shibboleth/sp/Agent.java | 7 +
.../sp/authn/impl/ValidateAgentAddress.java | 1 +
.../authn/impl/ValidateCachedAuthentication.java | 194 +++++++++++++++++++++
.../java/net/shibboleth/sp/impl/BasicAgent.java | 24 +++
6 files changed, 230 insertions(+), 1 deletion(-)
diff --git a/sp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/sp.properties b/sp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/sp.properties
index 3f3c11b..4581ed4 100644
--- a/sp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/sp.properties
+++ b/sp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/sp.properties
@@ -23,3 +23,5 @@ sp.encryption.cert = %{idp.home}/credentials/sp/sp-encryption.crt
# Set to Basic to require shared secret authentication
#sp.agent.authentication = None
+# Set false to globally disable cookie-based authentication by agents
+#sp.agent.cachedAuhentication = true
\ No newline at end of file
diff --git a/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/agents-system.xml b/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/agents-system.xml
index 38da7b3..a4ac0e1 100644
--- a/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/agents-system.xml
+++ b/sp-conf-impl/src/main/resources/net/shibboleth/sp/conf/agents-system.xml
@@ -21,7 +21,8 @@
<!-- Parent beans for Agents and Applications. -->
<bean id="shibboleth.Agent" class="net.shibboleth.sp.impl.BasicAgent" abstract="true"
- p:allowedAddressRanges="#{{ '127.0.0.1/32', '::1/128' }}" />
+ p:allowedAddressRanges="#{{ '127.0.0.1/32', '::1/128' }}"
+ p:supportsCachedAuthentication="%{sp.agent.cachedAuhentication:true}" />
<bean id="shibboleth.Application" class="net.shibboleth.sp.impl.BasicApplication" abstract="true"
p:metadataResolver-ref="shibboleth.MetadataResolverService"
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/Agent.java b/sp-server-api/src/main/java/net/shibboleth/sp/Agent.java
index 04e4a3d..981e94d 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/Agent.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/Agent.java
@@ -55,6 +55,13 @@ public interface Agent extends IdentifiedComponent {
* @return true iff the supplied address matches one of the allowed ranges
*/
boolean isAllowed(@Nonnull final InetAddress address);
+
+ /**
+ * Returns true iff the agent supports cached authentication via cookie.
+ *
+ * @return true iff the agent supports cached authentication via cookie
+ */
+ boolean isSupportsCachedAuthentication();
/**
* Get the issuer value to use in various identity protocols when identifying this agent.
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/authn/impl/ValidateAgentAddress.java b/sp-server-impl/src/main/java/net/shibboleth/sp/authn/impl/ValidateAgentAddress.java
index ff93203..6256a03 100644
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/authn/impl/ValidateAgentAddress.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/authn/impl/ValidateAgentAddress.java
@@ -86,6 +86,7 @@ public class ValidateAgentAddress extends AbstractProfileAction {
agent = agentCtx != null ? agentCtx.getAgent() : null;
if (agent == null) {
+ log.error("{} No Agent found in context", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
return false;
}
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
new file mode 100644
index 0000000..6358694
--- /dev/null
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/authn/impl/ValidateCachedAuthentication.java
@@ -0,0 +1,194 @@
+/*
+ * 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.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+
+import jakarta.servlet.http.HttpServletRequest;
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+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.DataSealer;
+import net.shibboleth.shared.security.DataSealerException;
+import net.shibboleth.shared.servlet.HttpServletSupport;
+import net.shibboleth.sp.Agent;
+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>TODO: adding a key proof via MAC in some way</p>
+ *
+ * @event {@link EventIds#PROCEED_EVENT_ID}
+ * @event {@link EventIds#INVALID_PROFILE_CTX}
+ * @pre <pre>ProfileRequestContext.ensureSubcontext(AgentRequestContext.class).getAgent() != null</pre>
+ */
+public class ValidateCachedAuthentication extends AbstractProfileAction {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateCachedAuthentication.class);
+
+ /** Lookup strategy for {@link AgentRequestContext}. */
+ @Nonnull private Function<ProfileRequestContext,AgentRequestContext> agentRequestContextLookupStrategy;
+
+ /** Cookie name to use. */
+ @NonnullAfterInit private String cookieName;
+
+ /** CookieManager to use. */
+ @NonnullAfterInit private CookieManager cookieManager;
+
+ /** DataSealer to use. */
+ @NonnullAfterInit private DataSealer dataSealer;
+
+ /** Cached agent from context. */
+ @NonnullBeforeExec private Agent agent;
+
+ /** Constructor. */
+ public ValidateCachedAuthentication() {
+ agentRequestContextLookupStrategy = new ChildContextLookup<>(AgentRequestContext.class);
+ }
+
+ /**
+ * Sets the lookup strategy for the {@link AgentRequestContext}.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setAgentRequestContextLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext,AgentRequestContext> strategy) {
+ checkSetterPreconditions();
+
+ agentRequestContextLookupStrategy = Constraint.isNotNull(strategy,
+ "AgentRequestContext lookup strategy cannot be null");
+ }
+
+ /**
+ * Sets the cookie name to use for cached authentication.
+ *
+ * @param name cookie name
+ */
+ public void setCookieName(@Nonnull @NotEmpty final String name) {
+ checkSetterPreconditions();
+
+ cookieName = Constraint.isNotNull(StringSupport.trimOrNull(name), "Cookie name cannot be null or empty");
+ }
+
+ /**
+ * Sets the {@link CookieManager} to use.
+ *
+ * @param manager cookie manager
+ */
+ public void setCookieManager(@Nonnull final CookieManager manager) {
+ checkSetterPreconditions();
+
+ cookieManager = Constraint.isNotNull(manager, "CookieManager cannot be null");
+ }
+
+ /**
+ * Sets the {@link DataSealer} to use.
+ *
+ * @param sealer data sealer
+ */
+ public void setDataSealer(@Nonnull final DataSealer sealer) {
+ checkSetterPreconditions();
+
+ dataSealer = Constraint.isNotNull(sealer, "DataSealer cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ if (cookieName == null || cookieManager == null || dataSealer == null) {
+ throw new ComponentInitializationException("CookieManager, DataSealer, and cookie name must be set");
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+ if (!super.doPreExecute(profileRequestContext)) {
+ return false;
+ }
+
+ final AgentRequestContext agentCtx = agentRequestContextLookupStrategy.apply(profileRequestContext);
+ agent = agentCtx != null ? agentCtx.getAgent() : null;
+ if (agent == null) {
+ log.error("{} No Agent found in context", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ return false;
+ }
+
+ if (!agent.isSupportsCachedAuthentication()) {
+ log.debug("{} Agent '{}' does not support cached authentication, skipping", getLogPrefix(), agent.getId());
+ ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
+ return false;
+ }
+
+ return true;
+ }
+
+ /** {@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(),
+ agent.getId());
+ ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
+ return;
+ }
+
+ final String unwrapped = dataSealer.unwrap(URISupport.doURLDecode(wrapped));
+
+ // TODO parse and validate...
+
+ } catch (final DataSealerException e) {
+ log.warn("{} Error decrypting cookie from agent '{}', authentication not bypassed", getLogPrefix(), agent.getId(), e);
+ ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/impl/BasicAgent.java b/sp-server-impl/src/main/java/net/shibboleth/sp/impl/BasicAgent.java
index 8b93a4b..191ad54 100644
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/impl/BasicAgent.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/impl/BasicAgent.java
@@ -45,6 +45,9 @@ import net.shibboleth.sp.Application;
*/
public class BasicAgent extends DefaultRelyingPartyConfigurationResolver implements Agent {
+ /** Whether cached authentication is supported. */
+ private boolean supportsCachedAuthentication;
+
/** Allowed address ranges. */
@Nonnull private Set<IPRange> allowedAddressRanges;
@@ -56,6 +59,7 @@ public class BasicAgent extends DefaultRelyingPartyConfigurationResolver impleme
/** Constructor. */
public BasicAgent() {
+ supportsCachedAuthentication = true;
allowedAddressRanges = CollectionSupport.emptySet();
applicationMap = CollectionSupport.emptyMap();
issuerLookupStrategy = FunctionSupport.constant(null);
@@ -79,9 +83,29 @@ public class BasicAgent extends DefaultRelyingPartyConfigurationResolver impleme
/** {@inheritDoc} */
public boolean isAllowed(@Nonnull final InetAddress address) {
+ checkComponentActive();
+
return allowedAddressRanges.stream().anyMatch(r -> r.contains(address));
}
+ /** {@inheritDoc} */
+ public boolean isSupportsCachedAuthentication() {
+ return supportsCachedAuthentication;
+ }
+
+ /**
+ * Sets whether this agent supports cached authentication via cookie.
+ *
+ * <p>Defaults to true.</p>
+ *
+ * @param flag flag to set
+ */
+ public void setSupportsCachedAuthentication(final boolean flag) {
+ checkSetterPreconditions();
+
+ supportsCachedAuthentication = flag;
+ }
+
/** {@inheritDoc} */
@Nullable @NotEmpty public String getIssuer(@Nullable final ProfileRequestContext profileRequestContext) {
return issuerLookupStrategy.apply(profileRequestContext);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list