[java-idp-plugin-webauthn] branch main updated: JWEBAUTHN-16 - Add auditing
Phil Smart
philip.smart at jisc.ac.uk
Fri Jul 19 09:34:31 UTC 2024
This is an automated email from the git hooks/post-receive script.
philsmart pushed a commit to branch main
in repository java-idp-plugin-webauthn.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-webauthn.git;a=commit;h=78d9804a4d72662985531bc29b4fb7a724fc0f79
The following commit(s) were added to refs/heads/main by this push:
new 78d9804 JWEBAUTHN-16 - Add auditing
78d9804 is described below
commit 78d9804a4d72662985531bc29b4fb7a724fc0f79
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Jul 19 10:34:27 2024 +0100
JWEBAUTHN-16 - Add auditing
- Add auditing support to management beans. Using a new
AbstractWebAuthnAuditingAction.
- Refactor actions to support a simplified parent hierarchy that can
include the new auditing action.
https://shibboleth.atlassian.net/browse/JWEBAUTHN-16
---
.../audit/AbstractWebAuthnAuditExtractor.java | 6 +-
... AbstractWebAuthnManagementAuditExtractor.java} | 31 ++--
...bstractWebAuthnRegistrationAuditExtractor.java} | 31 ++--
.../authn/webauthn/audit/WebAuthnAuditFields.java | 15 ++
.../impl/AbstractWebAuthnManagementAction.java | 139 --------------
.../impl/AbstractWebAuthnRegistrationAction.java | 139 --------------
.../impl/AddAttestationConveyancePreference.java | 5 +-
.../AddAuthenticatorAttachmentRequirement.java | 5 +-
.../authn/webauthn/admin/impl/AddDisplayName.java | 9 +-
.../admin/impl/AddResidentKeyRequirement.java | 5 +-
.../authn/webauthn/admin/impl/AddUserId.java | 23 ++-
.../admin/impl/AdminDeletePublicKeyCredential.java | 36 +++-
.../CreatePublicKeyCredentialCreationOptions.java | 11 +-
.../admin/impl/DeletePublicKeyCredential.java | 36 +++-
...licKeyCredentialAttestationFromFormRequest.java | 12 +-
.../impl/ExtractUsernameSearchFromFormRequest.java | 7 +-
.../admin/impl/LookupCredentialsForUser.java | 29 ++-
.../admin/impl/StorePublicKeyCredential.java | 31 +++-
.../ValidateAuthenticatorAttestationResponse.java | 10 +-
.../audit/impl/AbstractWebAuthnAuditingAction.java | 205 +++++++++++++++++++++
.../WebAuthnCredentialToRemoveAuditExtractor.java | 59 ++++++
.../impl/WebAuthnSearchUserAuditExtractor.java | 47 +++++
.../WebAuthnUserCredentialAddedAuditExtractor.java | 63 +++++++
...bAuthnUserCredentialToRemoveAuditExtractor.java | 59 ++++++
...tionAction.java => AbstractWebAuthnAction.java} | 145 ++++++++-------
.../webauthn/impl/AbstractWebAuthnBaseAction.java | 142 --------------
.../impl/AddUserVerificationRequirement.java | 6 +-
.../authn/webauthn/impl/BaseWebAuthnAction.java | 115 ------------
.../CreatePublicKeyCredentialRequestOptions.java | 10 +-
.../impl/EnsureAllowedCredentialsIsEmpty.java | 10 +-
...ublicKeyCredentialAssertionFromFormRequest.java | 11 +-
.../webauthn/impl/GenerateServerChallenge.java | 6 +-
.../webauthn/impl/LookupRegisteredCredentials.java | 26 ++-
.../LookupRegisteredCredentialsFromUserHandle.java | 33 ++--
.../webauthn-management-beans.xml | 42 ++++-
.../webauthn-registration-beans.xml | 52 +++++-
.../authn/WebAuthn/webauthn-abstract-beans.xml | 45 ++++-
.../idp/flows/authn/WebAuthn/webauthn-beans.xml | 12 +-
.../authn/webauthn/conf/authn/webauthn.properties | 7 +
.../authn/webauthn/views/webauthn-register.vm | 2 +-
40 files changed, 977 insertions(+), 700 deletions(-)
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/AbstractWebAuthnAuditExtractor.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/AbstractWebAuthnAuditExtractor.java
index d2ba8c5..7476204 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/AbstractWebAuthnAuditExtractor.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/AbstractWebAuthnAuditExtractor.java
@@ -37,7 +37,11 @@ public abstract class AbstractWebAuthnAuditExtractor<T> implements Function<Prof
@Nonnull
private final Function<ProfileRequestContext,BaseWebAuthnContext> webAuthnBaseContextLookupStrategy;
- /** Constructor.*/
+ /**
+ * Constructor.
+ *
+ * @param strategy the strategy used to locate the {@link BaseWebAuthnContext}.
+ */
protected AbstractWebAuthnAuditExtractor(
@Nonnull final Function<ProfileRequestContext,BaseWebAuthnContext> strategy) {
webAuthnBaseContextLookupStrategy = Constraint.isNotNull(strategy,
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/AbstractWebAuthnAuditExtractor.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/AbstractWebAuthnManagementAuditExtractor.java
similarity index 56%
copy from webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/AbstractWebAuthnAuditExtractor.java
copy to webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/AbstractWebAuthnManagementAuditExtractor.java
index d2ba8c5..655b263 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/AbstractWebAuthnAuditExtractor.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/AbstractWebAuthnManagementAuditExtractor.java
@@ -22,26 +22,30 @@ import javax.annotation.concurrent.ThreadSafe;
import org.opensaml.profile.context.ProfileRequestContext;
-import net.shibboleth.idp.plugin.authn.webauthn.context.BaseWebAuthnContext;
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnManagementContext;
import net.shibboleth.shared.logic.Constraint;
/**
- * An abstract audit extractor function to extract values directly from various WebAuthn contexts.
+ * An abstract audit extractor function to extract values directly from the WebAuthn management context.
*
* @param <T> the type to extract
*/
@ThreadSafe
-public abstract class AbstractWebAuthnAuditExtractor<T> implements Function<ProfileRequestContext, T> {
+public abstract class AbstractWebAuthnManagementAuditExtractor<T> implements Function<ProfileRequestContext, T> {
- /** Lookup strategy to locate the WebAuthn base context. */
+ /** Lookup strategy to locate the WebAuthn management context. */
@Nonnull
- private final Function<ProfileRequestContext,BaseWebAuthnContext> webAuthnBaseContextLookupStrategy;
+ private final Function<ProfileRequestContext,WebAuthnManagementContext> webAuthnManagementContextLookupStrategy;
- /** Constructor.*/
- protected AbstractWebAuthnAuditExtractor(
- @Nonnull final Function<ProfileRequestContext,BaseWebAuthnContext> strategy) {
- webAuthnBaseContextLookupStrategy = Constraint.isNotNull(strategy,
- "BaseWebAuthnContext lookup strategy can not be null");
+ /**
+ * Constructor.
+ *
+ * @param strategy the strategy used to locate the {@link WebAuthnManagementContext}.
+ */
+ protected AbstractWebAuthnManagementAuditExtractor(
+ @Nonnull final Function<ProfileRequestContext,WebAuthnManagementContext> strategy) {
+ webAuthnManagementContextLookupStrategy = Constraint.isNotNull(strategy,
+ "WebAuthnManagementContext lookup strategy can not be null");
}
@@ -51,7 +55,8 @@ public abstract class AbstractWebAuthnAuditExtractor<T> implements Function<Prof
if (profileRequestContext == null) {
return null;
}
- final BaseWebAuthnContext webAuthnContext = webAuthnBaseContextLookupStrategy.apply(profileRequestContext);
+ final WebAuthnManagementContext webAuthnContext =
+ webAuthnManagementContextLookupStrategy.apply(profileRequestContext);
if (webAuthnContext == null) {
return null;
}
@@ -61,10 +66,10 @@ public abstract class AbstractWebAuthnAuditExtractor<T> implements Function<Prof
/**
* Implemented to perform the actual lookup.
*
- * @param context the base WebAuthn context
+ * @param context the base WebAuthn management context
*
* @return lookup value or {@code null} if not found
*/
- @Nullable protected abstract T doLookup(@Nonnull final BaseWebAuthnContext context);
+ @Nullable protected abstract T doLookup(@Nonnull final WebAuthnManagementContext context);
}
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/AbstractWebAuthnAuditExtractor.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/AbstractWebAuthnRegistrationAuditExtractor.java
similarity index 55%
copy from webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/AbstractWebAuthnAuditExtractor.java
copy to webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/AbstractWebAuthnRegistrationAuditExtractor.java
index d2ba8c5..c657a9f 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/AbstractWebAuthnAuditExtractor.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/AbstractWebAuthnRegistrationAuditExtractor.java
@@ -22,26 +22,30 @@ import javax.annotation.concurrent.ThreadSafe;
import org.opensaml.profile.context.ProfileRequestContext;
-import net.shibboleth.idp.plugin.authn.webauthn.context.BaseWebAuthnContext;
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
import net.shibboleth.shared.logic.Constraint;
/**
- * An abstract audit extractor function to extract values directly from various WebAuthn contexts.
+ * An abstract audit extractor function to extract values directly from the WebAuthn registration context.
*
* @param <T> the type to extract
*/
@ThreadSafe
-public abstract class AbstractWebAuthnAuditExtractor<T> implements Function<ProfileRequestContext, T> {
+public abstract class AbstractWebAuthnRegistrationAuditExtractor<T> implements Function<ProfileRequestContext, T> {
- /** Lookup strategy to locate the WebAuthn base context. */
+ /** Lookup strategy to locate the WebAuthn registration context. */
@Nonnull
- private final Function<ProfileRequestContext,BaseWebAuthnContext> webAuthnBaseContextLookupStrategy;
+ private final Function<ProfileRequestContext,WebAuthnRegistrationContext> webAuthnRegistrationContextLookupStrategy;
- /** Constructor.*/
- protected AbstractWebAuthnAuditExtractor(
- @Nonnull final Function<ProfileRequestContext,BaseWebAuthnContext> strategy) {
- webAuthnBaseContextLookupStrategy = Constraint.isNotNull(strategy,
- "BaseWebAuthnContext lookup strategy can not be null");
+ /**
+ * Constructor.
+ *
+ * @param strategy the strategy used to locate the {@link WebAuthnRegistrationContext}.
+ */
+ protected AbstractWebAuthnRegistrationAuditExtractor(
+ @Nonnull final Function<ProfileRequestContext,WebAuthnRegistrationContext> strategy) {
+ webAuthnRegistrationContextLookupStrategy = Constraint.isNotNull(strategy,
+ "WebAuthnRegistrationContext lookup strategy can not be null");
}
@@ -51,7 +55,8 @@ public abstract class AbstractWebAuthnAuditExtractor<T> implements Function<Prof
if (profileRequestContext == null) {
return null;
}
- final BaseWebAuthnContext webAuthnContext = webAuthnBaseContextLookupStrategy.apply(profileRequestContext);
+ final WebAuthnRegistrationContext webAuthnContext =
+ webAuthnRegistrationContextLookupStrategy.apply(profileRequestContext);
if (webAuthnContext == null) {
return null;
}
@@ -61,10 +66,10 @@ public abstract class AbstractWebAuthnAuditExtractor<T> implements Function<Prof
/**
* Implemented to perform the actual lookup.
*
- * @param context the base WebAuthn context
+ * @param context the WebAuthn registration context
*
* @return lookup value or {@code null} if not found
*/
- @Nullable protected abstract T doLookup(@Nonnull final BaseWebAuthnContext context);
+ @Nullable protected abstract T doLookup(@Nonnull final WebAuthnRegistrationContext context);
}
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/WebAuthnAuditFields.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/WebAuthnAuditFields.java
index 79de0e4..68eebfd 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/WebAuthnAuditFields.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/WebAuthnAuditFields.java
@@ -36,5 +36,20 @@ public final class WebAuthnAuditFields {
/** What mode produced the final result e.g. 'passwordless'. */
@Nonnull @NotEmpty public static final String FLOW_MODE = "WebAuthnFM";
+
+ /** The user the admin performed an action on. */
+ @Nonnull @NotEmpty public static final String ADMIN_AFFECTED_USER = "WebAuthnAdminAU";
+
+ /** The outcome of an action, typically success or failure.*/
+ @Nonnull @NotEmpty public static final String ACTION_OUTCOME = "WebAuthnAdminAO";
+
+ /** The type of action performed. Controlled by the action, not by an audit extractor.*/
+ @Nonnull @NotEmpty public static final String ACTION = "WebAuthnAdminAction";
+
+ /** The Id of the credential that has been removed.*/
+ @Nonnull @NotEmpty public static final String CRED_REMOVED = "WebAuthnAdminCR";
+
+ /** The Id of the credential that has been added.*/
+ @Nonnull @NotEmpty public static final String CRED_ADDED = "WebAuthnAdminCA";
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AbstractWebAuthnManagementAction.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AbstractWebAuthnManagementAction.java
deleted file mode 100644
index 6e8e8b0..0000000
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AbstractWebAuthnManagementAction.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * 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.idp.plugin.authn.webauthn.admin.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 net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnManagementContext;
-import net.shibboleth.idp.plugin.authn.webauthn.impl.BaseWebAuthnAction;
-import net.shibboleth.idp.profile.AbstractProfileAction;
-import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
-
-/**
- * <p>A base class for WebAuthn administrative management function actions.</p>
- *
- * <p>In addition to the work performed by {@link AbstractProfileAction}, this action also looks up
- * and makes available the {@link WebAuthnManagementContext}.</p>
- *
- * <p>WebAuthn administrative action implementations should override the
- * {@link #doExecute(ProfileRequestContext, WebAuthnManagementContext)} method.</p>
- *
- * @event {@link EventIds#INVALID_PROFILE_CTX}
- * @pre <pre>ProfileRequestContext.getSubcontext(ProfileRequestContext.class) != null</pre>
- * @post <pre>AuthenticationContext.getSubcontext(WebAuthnManagementContext.class) != null</pre>
- */
-public abstract class AbstractWebAuthnManagementAction extends BaseWebAuthnAction {
-
- /** Class logger. */
- @Nonnull @NotEmpty private final Logger log = LoggerFactory.getLogger(AbstractWebAuthnManagementAction.class);
-
- /** Lookup strategy to locate the webauthn management context. */
- @Nonnull
- private Function<ProfileRequestContext,WebAuthnManagementContext> webauthnManagementContextLookupStrategy;
-
- /** The WebAuthn management Context.*/
- @NonnullBeforeExec private WebAuthnManagementContext webauthnManagementContext;
-
- /** Constructor.*/
- protected AbstractWebAuthnManagementAction() {
- //prc -> WebAuthnContext
- webauthnManagementContextLookupStrategy = new ChildContextLookup<>(WebAuthnManagementContext.class);
- }
-
- /**
- * Set WebAuthn management context lookup strategy to use.
- *
- * @param strategy lookup strategy
- */
- public void setWebauthnManagementContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,WebAuthnManagementContext> strategy) {
- checkSetterPreconditions();
-
- webauthnManagementContextLookupStrategy =
- Constraint.isNotNull(strategy, "WebauthnManagementContextLookupStrategy strategy cannot be null");
- }
-
-
- /** {@inheritDoc} */
- @Override
- protected final boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-
- if (!super.doPreExecute(profileRequestContext)) {
- return false;
- }
-
- webauthnManagementContext = webauthnManagementContextLookupStrategy.apply(profileRequestContext);
- if (webauthnManagementContext == null) {
- log.warn("{} No WebAuthn management context returned by lookup strategy",getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return false;
-
- }
- assert webauthnManagementContext != null;
- return doPreExecute(profileRequestContext, webauthnManagementContext);
- }
-
- /**
- * Delegates to {@link #doExecute(ProfileRequestContext, WebAuthnManagementContext)} to perform the
- * actual action. Implementations can not override this method.
- *
- * @param profileRequestContext the current IdP profile request context
- */
- @Override
- protected final void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- assert webauthnManagementContext != null;
- doExecute(profileRequestContext, webauthnManagementContext);
- }
-
- /**
- * Performs this admin action's pre-execute step. Default implementation just returns true.
- *
- * @param profileRequestContext the current IdP profile request context
- * @param context the WebAuthn management context
- *
- * @return true iff execution should continue
- */
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final WebAuthnManagementContext context) {
- return true;
- }
-
- /**
- * Performs this webauthn authentication action using the supplied WebAuthn management context. Implementations
- * should override this method.
- *
- * @param profileRequestContext the current IdP profile request context
- * @param context the WebAuthn management context
- */
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final WebAuthnManagementContext context) {
-
- }
-
-}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AbstractWebAuthnRegistrationAction.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AbstractWebAuthnRegistrationAction.java
deleted file mode 100644
index ba61bd5..0000000
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AbstractWebAuthnRegistrationAction.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * 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.idp.plugin.authn.webauthn.admin.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 net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
-import net.shibboleth.idp.plugin.authn.webauthn.impl.BaseWebAuthnAction;
-import net.shibboleth.idp.profile.AbstractProfileAction;
-import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
-
-/**
- * <p>A base class for WebAuthn registration related administration actions.</p>
- *
- * <p>In addition to the work performed by {@link AbstractProfileAction}, this action also looks up
- * and makes available the {@link WebAuthnRegistrationContext}.</p>
- *
- * <p>WebAuthn registration action implementations should override the
- * {@link #doExecute(ProfileRequestContext, WebAuthnRegistrationContext)} method.</p>
- *
- * @event {@link EventIds#INVALID_PROFILE_CTX}
- * @pre <pre>ProfileRequestContext.getSubcontext(ProfileRequestContext.class) != null</pre>
- * @post <pre>AuthenticationContext.getSubcontext(WebAuthnRegistrationContext.class) != null</pre>
- */
-public abstract class AbstractWebAuthnRegistrationAction extends BaseWebAuthnAction {
-
- /** Class logger. */
- @Nonnull @NotEmpty private final Logger log = LoggerFactory.getLogger(AbstractWebAuthnRegistrationAction.class);
-
- /** Lookup strategy to locate the webauthn registration context. */
- @Nonnull
- private Function<ProfileRequestContext,WebAuthnRegistrationContext> webauthnRegistrationContextLookupStrategy;
-
- /** The WebAuthn registration Context.*/
- @NonnullBeforeExec private WebAuthnRegistrationContext webauthnRegistrationContext;
-
-
- /** Constructor.*/
- protected AbstractWebAuthnRegistrationAction() {
- //prc -> WebAuthnContext
- webauthnRegistrationContextLookupStrategy = new ChildContextLookup<>(WebAuthnRegistrationContext.class);
- }
-
- /**
- * Set WebAuthn registration context lookup strategy to use.
- *
- * @param strategy lookup strategy
- */
- public void setWebauthnRegistrationContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,WebAuthnRegistrationContext> strategy) {
- checkSetterPreconditions();
-
- webauthnRegistrationContextLookupStrategy =
- Constraint.isNotNull(strategy, "WebauthnContextLookuplookup strategy cannot be null");
- }
-
- /** {@inheritDoc} */
- @Override
- protected final boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-
- if (!super.doPreExecute(profileRequestContext)) {
- return false;
- }
-
- webauthnRegistrationContext = webauthnRegistrationContextLookupStrategy.apply(profileRequestContext);
- if (webauthnRegistrationContext == null) {
- log.warn("{} No WebAuthn registration context returned by lookup strategy",getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return false;
-
- }
- assert webauthnRegistrationContext != null;
- return doPreExecute(profileRequestContext, webauthnRegistrationContext);
- }
-
- /**
- * Delegates to {@link #doExecute(ProfileRequestContext, WebAuthnRegistrationContext)} to perform the
- * actual action. Implementations can not override this method.
- *
- * @param profileRequestContext the current IdP profile request context
- */
- @Override
- protected final void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- assert webauthnRegistrationContext != null;
- doExecute(profileRequestContext, webauthnRegistrationContext);
- }
-
- /**
- * Performs this registration action's pre-execute step. Default implementation just returns true.
- *
- * @param profileRequestContext the current IdP profile request context
- * @param context the WebAuthn registration context
- *
- * @return true iff execution should continue
- */
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final WebAuthnRegistrationContext context) {
- return true;
- }
-
- /**
- * Performs this webauthn registration action using the supplied webauthn context. Implementations
- * should override this method.
- *
- * @param profileRequestContext the current IdP profile request context
- * @param context the WebAuthn registration context
- */
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final WebAuthnRegistrationContext context) {
-
- }
-
-}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddAttestationConveyancePreference.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddAttestationConveyancePreference.java
index ce7085c..58b0005 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddAttestationConveyancePreference.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddAttestationConveyancePreference.java
@@ -18,6 +18,7 @@ import java.util.stream.Stream;
import javax.annotation.Nonnull;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
@@ -25,6 +26,7 @@ import com.yubico.webauthn.data.AttestationConveyancePreference;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnAuthenticationContext;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
+import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnAction;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.logic.ConstraintViolationException;
@@ -38,7 +40,7 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* @post <pre>WebAuthnRegistrationContext.getAttestationConveyancePreference() != null</pre>
*
*/
-public class AddAttestationConveyancePreference extends AbstractWebAuthnRegistrationAction {
+public class AddAttestationConveyancePreference extends AbstractWebAuthnAction<WebAuthnRegistrationContext> {
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(AddAttestationConveyancePreference.class);
@@ -48,6 +50,7 @@ public class AddAttestationConveyancePreference extends AbstractWebAuthnRegistra
/** Constructor.*/
public AddAttestationConveyancePreference() {
+ super(new ChildContextLookup<>(WebAuthnRegistrationContext.class));
attestationConveyancePreference = AttestationConveyancePreference.NONE;
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddAuthenticatorAttachmentRequirement.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddAuthenticatorAttachmentRequirement.java
index c3ad7a8..bba98a8 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddAuthenticatorAttachmentRequirement.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddAuthenticatorAttachmentRequirement.java
@@ -19,12 +19,14 @@ import java.util.stream.Stream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import com.yubico.webauthn.data.AuthenticatorAttachment;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
+import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnAction;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.logic.ConstraintViolationException;
@@ -37,7 +39,7 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* @pre <pre>ProfileRequestContext.getSubcontext(WebAuthnRegistrationContext.class) != null</pre>
* @post <pre>WebAuthnRegistrationContext.getAuthenticatorAttachmentRequirement() != null</pre>
*/
-public class AddAuthenticatorAttachmentRequirement extends AbstractWebAuthnRegistrationAction {
+public class AddAuthenticatorAttachmentRequirement extends AbstractWebAuthnAction<WebAuthnRegistrationContext> {
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(AddAuthenticatorAttachmentRequirement.class);
@@ -47,6 +49,7 @@ public class AddAuthenticatorAttachmentRequirement extends AbstractWebAuthnRegis
/** Constructor.*/
public AddAuthenticatorAttachmentRequirement() {
+ super(new ChildContextLookup<>(WebAuthnRegistrationContext.class));
authenticatorAttachmentRequirement = null;
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddDisplayName.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddDisplayName.java
index 5de9b5d..a1e2e87 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddDisplayName.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddDisplayName.java
@@ -22,12 +22,14 @@ 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.context.ProfileRequestContext;
import org.slf4j.Logger;
import net.shibboleth.idp.plugin.authn.webauthn.admin.WebAuthnRegistrationEventIds;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
+import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnAction;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
import net.shibboleth.shared.component.ComponentInitializationException;
@@ -41,7 +43,7 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* @pre <pre>ProfileRequestContext.getSubcontext(WebAuthnRegistrationContext.class) != null</pre>
* @post a displayName is added to the registration context
*/
-public class AddDisplayName extends AbstractWebAuthnRegistrationAction {
+public class AddDisplayName extends AbstractWebAuthnAction<WebAuthnRegistrationContext> {
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(AddDisplayName.class);
@@ -51,6 +53,11 @@ public class AddDisplayName extends AbstractWebAuthnRegistrationAction {
/** The stashed username.*/
@NonnullBeforeExec private String username;
+
+ /** Constructor.*/
+ protected AddDisplayName() {
+ super(new ChildContextLookup<>(WebAuthnRegistrationContext.class));
+ }
/**
* Set the strategy used to generate the user.displayName.
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddResidentKeyRequirement.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddResidentKeyRequirement.java
index 94e5724..9c202a8 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddResidentKeyRequirement.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddResidentKeyRequirement.java
@@ -18,12 +18,14 @@ import java.util.stream.Stream;
import javax.annotation.Nonnull;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import com.yubico.webauthn.data.ResidentKeyRequirement;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
+import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnAction;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.logic.ConstraintViolationException;
@@ -35,7 +37,7 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* @pre <pre>ProfileRequestContext.getSubcontext(WebAuthnRegistrationContext.class) != null</pre>
* @post <pre>WebAuthnRegistrationContext.getResidentKeyRequirement() != null</pre>
*/
-public class AddResidentKeyRequirement extends AbstractWebAuthnRegistrationAction {
+public class AddResidentKeyRequirement extends AbstractWebAuthnAction<WebAuthnRegistrationContext> {
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(AddResidentKeyRequirement.class);
@@ -45,6 +47,7 @@ public class AddResidentKeyRequirement extends AbstractWebAuthnRegistrationActio
/** Constructor.*/
public AddResidentKeyRequirement() {
+ super(new ChildContextLookup<>(WebAuthnRegistrationContext.class));
residentKeyRequirement = ResidentKeyRequirement.PREFERRED;
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddUserId.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddUserId.java
index a78adc4..468b0ae 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddUserId.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AddUserId.java
@@ -22,6 +22,7 @@ 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.context.ProfileRequestContext;
import org.slf4j.Logger;
@@ -30,9 +31,13 @@ import com.yubico.webauthn.data.ByteArray;
import net.shibboleth.idp.plugin.authn.webauthn.admin.WebAuthnRegistrationEventIds;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
+import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnAction;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.StorageServiceCredentialRepository;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
import net.shibboleth.shared.codec.Base64Support;
import net.shibboleth.shared.codec.EncodingException;
+import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.LoggerFactory;
@@ -53,7 +58,7 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* @pre <pre>ProfileRequestContext.getSubcontext(WebAuthnRegistrationContext.class) != null</pre>
* @post a user ID is added to the registration context
*/
-public class AddUserId extends AbstractWebAuthnRegistrationAction {
+public class AddUserId extends AbstractWebAuthnAction<WebAuthnRegistrationContext> {
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(AddUserId.class);
@@ -64,8 +69,22 @@ public class AddUserId extends AbstractWebAuthnRegistrationAction {
/** The stashed username.*/
@NonnullBeforeExec private String username;
+ /** The credential repository to use.*/
+ @NonnullAfterInit private StorageServiceCredentialRepository repository;
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+ repository = getCredentialRepository();
+ if (repository == null) {
+ throw new ComponentInitializationException("Credential repository can not be null");
+ }
+ }
+
/** Constructor. */
public AddUserId() {
+ super(new ChildContextLookup<>(WebAuthnRegistrationContext.class));
userIdGeneratorStrategy = new RandomUserIdGenerator();
}
@@ -105,7 +124,7 @@ public class AddUserId extends AbstractWebAuthnRegistrationAction {
@Nonnull final WebAuthnRegistrationContext context) {
// Any existing user handle is the user.id
- final Optional<ByteArray> existingUserHandle = getCredentialRepository().getUserHandleForUsername(username);
+ final Optional<ByteArray> existingUserHandle = repository.getUserHandleForUsername(username);
if (existingUserHandle.isPresent()) {
final byte[] handleAsBytes = existingUserHandle.get().getBytes();
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AdminDeletePublicKeyCredential.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AdminDeletePublicKeyCredential.java
index bc23277..a97a09b 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AdminDeletePublicKeyCredential.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AdminDeletePublicKeyCredential.java
@@ -19,6 +19,7 @@ package net.shibboleth.idp.plugin.authn.webauthn.admin.impl;
import javax.annotation.Nonnull;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
@@ -26,9 +27,13 @@ import org.slf4j.Logger;
import com.yubico.webauthn.data.ByteArray;
import net.shibboleth.idp.plugin.authn.webauthn.admin.WebAuthnRegistrationEventIds;
+import net.shibboleth.idp.plugin.authn.webauthn.audit.impl.AbstractWebAuthnAuditingAction;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnManagementContext;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.StorageServiceCredentialRepository;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.codec.Base64Support;
import net.shibboleth.shared.codec.EncodingException;
+import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.primitive.LoggerFactory;
/**
@@ -39,11 +44,30 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* @pre <pre>ProfileRequestContext.getSubcontext(WebAuthnManagementContext.class) != null</pre>
* @post a credential is removed from credential repository
*/
-public class AdminDeletePublicKeyCredential extends AbstractWebAuthnManagementAction {
+//TODO merge with the non admin delete action?
+public class AdminDeletePublicKeyCredential extends AbstractWebAuthnAuditingAction<WebAuthnManagementContext> {
/** Class logger. */
@Nonnull
- private final Logger log = LoggerFactory.getLogger(AdminDeletePublicKeyCredential.class);
+ private final Logger log = LoggerFactory.getLogger(AdminDeletePublicKeyCredential.class);
+
+ /** The credential repository to use.*/
+ @NonnullAfterInit private StorageServiceCredentialRepository repository;
+
+ /** Constructor.*/
+ public AdminDeletePublicKeyCredential() {
+ super(new ChildContextLookup<>(WebAuthnManagementContext.class));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+ repository = getCredentialRepository();
+ if (repository == null) {
+ throw new ComponentInitializationException("Credential repository can not be null");
+ }
+ }
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
@@ -63,7 +87,7 @@ public class AdminDeletePublicKeyCredential extends AbstractWebAuthnManagementAc
return;
}
- final boolean removed = getCredentialRepository().removeRegistrationByUsernameAndCredentialId(
+ final boolean removed = repository.removeRegistrationByUsernameAndCredentialId(
credentialUsername, new ByteArray(credentialId));
if (log.isDebugEnabled()) {
@@ -74,7 +98,11 @@ public class AdminDeletePublicKeyCredential extends AbstractWebAuthnManagementAc
// Do nothing if the encoding fails.
}
}
-
+ if (removed) {
+ auditSuccess(profileRequestContext, "credential-removed");
+ } else {
+ auditFailure(profileRequestContext, "credential-removed");
+ }
// Remove the key to be deleted in-case the context state is re-used
context.setCredentialIdToRemove(null);
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CreatePublicKeyCredentialCreationOptions.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CreatePublicKeyCredentialCreationOptions.java
index 8705f05..d63809f 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CreatePublicKeyCredentialCreationOptions.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CreatePublicKeyCredentialCreationOptions.java
@@ -21,6 +21,7 @@ import java.util.stream.Collectors;
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;
@@ -37,6 +38,7 @@ import net.shibboleth.idp.plugin.authn.webauthn.admin.WebAuthnRegistrationEventI
import net.shibboleth.idp.plugin.authn.webauthn.client.WebAuthnAuthenticationClient;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
import net.shibboleth.idp.plugin.authn.webauthn.exception.WebAuthnAuthenticationClientException;
+import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnAction;
import net.shibboleth.shared.logic.ConstraintViolationException;
import net.shibboleth.shared.primitive.LoggerFactory;
@@ -49,10 +51,15 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* @pre <pre>ProfileRequestContext.getSubcontext(WebAuthnRegistrationContext.class) != null</pre>
* @post a PublicKeyCredentialCreationOptions is added to the registration context
*/
-public class CreatePublicKeyCredentialCreationOptions extends AbstractWebAuthnRegistrationAction {
+public class CreatePublicKeyCredentialCreationOptions extends AbstractWebAuthnAction<WebAuthnRegistrationContext> {
/** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(CreatePublicKeyCredentialCreationOptions.class);
+ @Nonnull private final Logger log = LoggerFactory.getLogger(CreatePublicKeyCredentialCreationOptions.class);
+
+ /** Constructor.*/
+ protected CreatePublicKeyCredentialCreationOptions() {
+ super(new ChildContextLookup<>(WebAuthnRegistrationContext.class));
+ }
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/DeletePublicKeyCredential.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/DeletePublicKeyCredential.java
index ab00805..95cbd16 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/DeletePublicKeyCredential.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/DeletePublicKeyCredential.java
@@ -21,6 +21,7 @@ import java.util.Optional;
import javax.annotation.Nonnull;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
@@ -28,8 +29,12 @@ import org.slf4j.Logger;
import com.yubico.webauthn.data.ByteArray;
import net.shibboleth.idp.plugin.authn.webauthn.admin.WebAuthnRegistrationEventIds;
+import net.shibboleth.idp.plugin.authn.webauthn.audit.impl.AbstractWebAuthnAuditingAction;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRegistration;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.StorageServiceCredentialRepository;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.primitive.LoggerFactory;
/**
@@ -39,11 +44,29 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* @pre <pre>ProfileRequestContext.getSubcontext(WebAuthnRegistrationContext.class) != null</pre>
* @post a credential is removed from credential repository
*/
-public class DeletePublicKeyCredential extends AbstractWebAuthnRegistrationAction {
+public class DeletePublicKeyCredential extends AbstractWebAuthnAuditingAction<WebAuthnRegistrationContext> {
/** Class logger. */
@Nonnull
- private final Logger log = LoggerFactory.getLogger(DeletePublicKeyCredential.class);
+ private final Logger log = LoggerFactory.getLogger(DeletePublicKeyCredential.class);
+
+ /** The credential repository to use.*/
+ @NonnullAfterInit private StorageServiceCredentialRepository repository;
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+ repository = getCredentialRepository();
+ if (repository == null) {
+ throw new ComponentInitializationException("Credential repository can not be null");
+ }
+ }
+
+ /** Constructor.*/
+ protected DeletePublicKeyCredential() {
+ super(new ChildContextLookup<>(WebAuthnRegistrationContext.class));
+ }
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
@@ -67,16 +90,21 @@ public class DeletePublicKeyCredential extends AbstractWebAuthnRegistrationActio
}
final Optional<CredentialRegistration> credential =
- getCredentialRepository().getRegistrationByUsernameAndCredentialId(
+ repository.getRegistrationByUsernameAndCredentialId(
username, new ByteArray(credentialId));
if (credential.isEmpty()) {
// This is not an error
log.debug("{} Unable to find credential to remove, nothing to remove", getLogPrefix());
} else {
- final boolean removed = getCredentialRepository().removeRegistrationByUsername(username, credential.get());
+ final boolean removed = repository.removeRegistrationByUsername(username, credential.get());
log.debug("{} Credential '{}' {} removed", getLogPrefix(), credential.get().getCredentialIdBase64Url(),
removed ? "was" : "was not");
+ if(removed) {
+ auditSuccess(profileRequestContext, "credential-removed");
+ } else {
+ auditFailure(profileRequestContext, "credential-removed");
+ }
}
// Remove the key to be deleted in-case the context state is re-used
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ExtractPublicKeyCredentialAttestationFromFormRequest.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ExtractPublicKeyCredentialAttestationFromFormRequest.java
index 0bfe579..2b1b1f4 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ExtractPublicKeyCredentialAttestationFromFormRequest.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ExtractPublicKeyCredentialAttestationFromFormRequest.java
@@ -21,6 +21,7 @@ import java.io.IOException;
import javax.annotation.Nonnull;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
@@ -33,6 +34,7 @@ import jakarta.servlet.http.HttpServletRequest;
import net.shibboleth.idp.authn.AuthnEventIds;
import net.shibboleth.idp.plugin.authn.webauthn.admin.WebAuthnRegistrationEventIds;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
+import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnAction;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.LoggerFactory;
@@ -41,14 +43,15 @@ import net.shibboleth.shared.primitive.StringSupport;
/**
* An action that extracts the PublicKeyCredential containing the authenticator attestation response from the incoming
- * HTTP request. Also extracts the user entered credential nickname.
+ * HTTP request. Also extracts the user entered credential nickname. Failure to find either results in an error event.
*
* @event {WebAuthnRegistrationEventIds#INVALID_REGISTRATION}
* @event {AuthnEventIds#NO_CREDENTIALS}
* @pre <pre>ProfileRequestContext.getSubcontext(WebAuthnRegistrationContext.class) != null</pre>
- * @post add an AuthenticatorAttestationResponse to the registration context
+ * @post add an AuthenticatorAttestationResponse and Credential Nickname to the registration context
*/
-public class ExtractPublicKeyCredentialAttestationFromFormRequest extends AbstractWebAuthnRegistrationAction {
+public class ExtractPublicKeyCredentialAttestationFromFormRequest
+ extends AbstractWebAuthnAction<WebAuthnRegistrationContext> {
/** Default public key credential attestation parameter name. */
@Nonnull @NotEmpty public static final String DEFAULT_PARAMETER_NAME = "publicKeyCredential";
@@ -68,6 +71,7 @@ public class ExtractPublicKeyCredentialAttestationFromFormRequest extends Abstra
/** Constructor. */
public ExtractPublicKeyCredentialAttestationFromFormRequest() {
+ super(new ChildContextLookup<>(WebAuthnRegistrationContext.class));
publicKeyCredentialAttestationParameterName = DEFAULT_PARAMETER_NAME;
credentialNicknameParameterName = DEFAULT_NICKNAME_FIELD_NAME;
}
@@ -130,7 +134,7 @@ public class ExtractPublicKeyCredentialAttestationFromFormRequest extends Abstra
context.setCredentialNickname(credNickname);
} catch (final IOException e) {
log.debug("{} Could not parse PublicKeyCredential response from request parameter '{}'", getLogPrefix(),
- credentialNicknameParameterName, e);
+ publicKeyCredentialAttestationParameterName, e);
ActionSupport.buildEvent(profileRequestContext, WebAuthnRegistrationEventIds.INVALID_REGISTRATION);
return;
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ExtractUsernameSearchFromFormRequest.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ExtractUsernameSearchFromFormRequest.java
index af6b960..83d5ce4 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ExtractUsernameSearchFromFormRequest.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ExtractUsernameSearchFromFormRequest.java
@@ -19,6 +19,7 @@ package net.shibboleth.idp.plugin.authn.webauthn.admin.impl;
import javax.annotation.Nonnull;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
@@ -26,6 +27,7 @@ import org.slf4j.Logger;
import jakarta.servlet.http.HttpServletRequest;
import net.shibboleth.idp.authn.AuthnEventIds;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnManagementContext;
+import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnAction;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.logic.Constraint;
@@ -42,7 +44,7 @@ import net.shibboleth.shared.primitive.StringSupport;
* @post add the parameter value to the context
*/
//TODO just make this generic? would need a consumer to set the information?
-public class ExtractUsernameSearchFromFormRequest extends AbstractWebAuthnManagementAction {
+public class ExtractUsernameSearchFromFormRequest extends AbstractWebAuthnAction<WebAuthnManagementContext> {
/** Default credential Id parameter name. */
@Nonnull @NotEmpty public static final String DEFAULT_PARAMETER_NAME = "username_search";
@@ -54,7 +56,8 @@ public class ExtractUsernameSearchFromFormRequest extends AbstractWebAuthnManage
@NonnullAfterInit @NotEmpty private String requestParameterToExtract;
/** Constructor. */
- public ExtractUsernameSearchFromFormRequest() {
+ public ExtractUsernameSearchFromFormRequest() {
+ super(new ChildContextLookup<>(WebAuthnManagementContext.class));
requestParameterToExtract = DEFAULT_PARAMETER_NAME;
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/LookupCredentialsForUser.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/LookupCredentialsForUser.java
index 6c31eb4..0b2942e 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/LookupCredentialsForUser.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/LookupCredentialsForUser.java
@@ -18,13 +18,18 @@ import java.util.Collection;
import javax.annotation.Nonnull;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import net.shibboleth.idp.authn.AuthnEventIds;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnManagementContext;
+import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnAction;
import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRegistration;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.StorageServiceCredentialRepository;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.primitive.LoggerFactory;
import net.shibboleth.shared.primitive.StringSupport;
@@ -36,11 +41,29 @@ import net.shibboleth.shared.primitive.StringSupport;
* @post BaseWebAuthnContext.setExistingCredentials() is either null if no existing credentials are found, or contains
* the credentials from the credential repository
*/
-public class LookupCredentialsForUser extends AbstractWebAuthnManagementAction {
+public class LookupCredentialsForUser extends AbstractWebAuthnAction<WebAuthnManagementContext> {
/** Class logger. */
@Nonnull
- private final Logger log = LoggerFactory.getLogger(LookupCredentialsForUser.class);
+ private final Logger log = LoggerFactory.getLogger(LookupCredentialsForUser.class);
+
+ /** The credential repository to use.*/
+ @NonnullAfterInit private StorageServiceCredentialRepository repository;
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+ repository = getCredentialRepository();
+ if (repository == null) {
+ throw new ComponentInitializationException("Credential repository can not be null");
+ }
+ }
+
+ /** Constructor. */
+ public LookupCredentialsForUser() {
+ super(new ChildContextLookup<>(WebAuthnManagementContext.class));
+ }
/** {@inheritDoc} */
@Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
@@ -54,7 +77,7 @@ public class LookupCredentialsForUser extends AbstractWebAuthnManagementAction {
log.trace("{} Finding registered credentials for '{}'",getLogPrefix(), userToSearchFor);
assert userToSearchFor != null;
final Collection<CredentialRegistration> credentials =
- getCredentialRepository().getRegistrationsByUsername(userToSearchFor);
+ repository.getRegistrationsByUsername(userToSearchFor);
log.debug("{} Found '{}' credentials", getLogPrefix(), credentials.size());
context.setFoundCredentials(credentials);
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/StorePublicKeyCredential.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/StorePublicKeyCredential.java
index aeead6b..004ffc7 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/StorePublicKeyCredential.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/StorePublicKeyCredential.java
@@ -24,6 +24,7 @@ import java.util.TreeSet;
import javax.annotation.Nonnull;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
@@ -37,13 +38,17 @@ import com.yubico.webauthn.data.UserIdentity;
import net.shibboleth.idp.plugin.authn.webauthn.admin.RegistrationResult;
import net.shibboleth.idp.plugin.authn.webauthn.admin.WebAuthnRegistrationEventIds;
+import net.shibboleth.idp.plugin.authn.webauthn.audit.impl.AbstractWebAuthnAuditingAction;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRegistration;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.StorageServiceCredentialRepository;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.annotation.constraint.NonnullElements;
import net.shibboleth.shared.annotation.constraint.NotLive;
import net.shibboleth.shared.codec.Base64Support;
import net.shibboleth.shared.codec.EncodingException;
import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.primitive.LoggerFactory;
/**
@@ -57,10 +62,28 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* @pre <pre>ProfileRequestContext.getSubcontext(WebAuthnRegistrationContext.class) != null</pre>
* @post the credential from the registration context is added to the credential repository
*/
-public class StorePublicKeyCredential extends AbstractWebAuthnRegistrationAction {
+public class StorePublicKeyCredential extends AbstractWebAuthnAuditingAction<WebAuthnRegistrationContext> {
/** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(StorePublicKeyCredential.class);
+ @Nonnull private final Logger log = LoggerFactory.getLogger(StorePublicKeyCredential.class);
+
+ /** The credential repository to use.*/
+ @NonnullAfterInit private StorageServiceCredentialRepository repository;
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+ repository = getCredentialRepository();
+ if (repository == null) {
+ throw new ComponentInitializationException("Credential repository can not be null");
+ }
+ }
+
+ /** Constructor. */
+ protected StorePublicKeyCredential() {
+ super(new ChildContextLookup<>(WebAuthnRegistrationContext.class));
+ }
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
@@ -119,7 +142,7 @@ public class StorePublicKeyCredential extends AbstractWebAuthnRegistrationAction
.withUserVerified(registrationResult.isUserVerified())
.build();
- getCredentialRepository().addRegistrationByUsername(username, registration);
+ repository.addRegistrationByUsername(username, registration);
if (log.isInfoEnabled()) {
@@ -136,12 +159,14 @@ public class StorePublicKeyCredential extends AbstractWebAuthnRegistrationAction
getLogPrefix(), username, userIdBase64, registrationResult.getKeyId().getId().getBase64Url(),
registrationResult.isDiscoverable().isPresent() ? registrationResult.isDiscoverable() :
"unknown", registrationResult.isUserVerified());
+ auditSuccess(profileRequestContext, "credential-added");
}
} catch (final Exception e) {
log.error("{} Unable to store registration for key '{}'",getLogPrefix(),
registrationResult.getKeyId().getId().getBase64Url(), e);
ActionSupport.buildEvent(profileRequestContext, WebAuthnRegistrationEventIds.INVALID_REGISTRATION);
+ auditFailure(profileRequestContext, "credential-added");
return;
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ValidateAuthenticatorAttestationResponse.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ValidateAuthenticatorAttestationResponse.java
index 618f498..b5e45d3 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ValidateAuthenticatorAttestationResponse.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ValidateAuthenticatorAttestationResponse.java
@@ -19,6 +19,7 @@ package net.shibboleth.idp.plugin.authn.webauthn.admin.impl;
import javax.annotation.Nonnull;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
@@ -34,6 +35,7 @@ import net.shibboleth.idp.plugin.authn.webauthn.admin.WebAuthnRegistrationEventI
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationErrorContext;
import net.shibboleth.idp.plugin.authn.webauthn.exception.RegistrationFailureException;
+import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnAction;
import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
import net.shibboleth.shared.primitive.LoggerFactory;
@@ -44,7 +46,7 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* @event {WebAuthnRegistrationEventIds#INVALID_REGISTRATION}
* @pre <pre>ProfileRequestContext.getSubcontext(WebAuthnRegistrationContext.class) != null</pre>
*/
-public class ValidateAuthenticatorAttestationResponse extends AbstractWebAuthnRegistrationAction {
+public class ValidateAuthenticatorAttestationResponse extends AbstractWebAuthnAction<WebAuthnRegistrationContext> {
/** Class logger. */
@Nonnull
@@ -55,8 +57,12 @@ public class ValidateAuthenticatorAttestationResponse extends AbstractWebAuthnRe
/** The stashed public key credential authenticator attestation response.*/
@NonnullBeforeExec
- private PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> attestation;
+ private PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> attestation;
+ /** Constructor.*/
+ public ValidateAuthenticatorAttestationResponse() {
+ super(new ChildContextLookup<>(WebAuthnRegistrationContext.class));
+ }
/** {@inheritDoc} */
@Override
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/impl/AbstractWebAuthnAuditingAction.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/impl/AbstractWebAuthnAuditingAction.java
new file mode 100644
index 0000000..6756a22
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/impl/AbstractWebAuthnAuditingAction.java
@@ -0,0 +1,205 @@
+/*
+ * 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.idp.plugin.authn.webauthn.audit.impl;
+
+import java.util.Map;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.context.EventContext;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.springframework.webflow.execution.Event;
+import org.springframework.webflow.execution.RequestContext;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.webauthn.audit.WebAuthnAuditFields;
+import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnAction;
+import net.shibboleth.idp.profile.audit.impl.PopulateAuditContext;
+import net.shibboleth.idp.profile.audit.impl.WriteAuditLog;
+import net.shibboleth.profile.context.AuditContext;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * A base class for actions that include audit logging support.
+ */
+public abstract class AbstractWebAuthnAuditingAction<T> extends AbstractWebAuthnAction<T> {
+
+ /** Strategy used to locate or create the {@link AuditContext} to populate. */
+ @Nonnull private Function<ProfileRequestContext,AuditContext> auditContextCreationStrategy;
+
+ /** Optional audit extraction action. */
+ @Nullable private PopulateAuditContext populateAuditContextAction;
+
+ /** Optional audit output action. */
+ @Nullable private WriteAuditLog writeAuditLogAction;
+
+ /** The Spring RequestContext to operate on. */
+ @Nullable private RequestContext requestContext;
+
+ /** Constructor. */
+ protected AbstractWebAuthnAuditingAction(@Nonnull final Function<ProfileRequestContext,T> defaultStrategy) {
+ super(defaultStrategy);
+ auditContextCreationStrategy =
+ new ChildContextLookup<>(AuditContext.class, true).compose(
+ new ChildContextLookup<>(AuthenticationContext.class));
+ }
+
+ /**
+ * Set the strategy used to locate the {@link AuditContext} associated with a given
+ * {@link ProfileRequestContext}.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setAuditContextCreationStrategy(@Nonnull final Function<ProfileRequestContext,AuditContext> strategy) {
+ checkSetterPreconditions();
+ auditContextCreationStrategy = Constraint.isNotNull(strategy, "AuditContext creation strategy cannot be null");
+ }
+
+ /**
+ * Sets an audit context population action to run.
+ *
+ * @param action optional action to use to populate audit context
+ */
+ public void setPopulateAuditContextAction(@Nullable final PopulateAuditContext action) {
+ checkSetterPreconditions();
+ populateAuditContextAction = action;
+ }
+
+ /**
+ * Sets an audit output action to run.
+ *
+ * @param action optional action to use to write to audit log
+ */
+ public void setWriteAuditLogAction(@Nullable final WriteAuditLog action) {
+ checkSetterPreconditions();
+ writeAuditLogAction = action;
+ }
+
+ /**
+ * Create or locate the {@link AuditContext} via the defined strategy.
+ *
+ * @param profileRequestContext profile request context
+ *
+ * @return the audit context
+ */
+ @Nullable protected AuditContext getAuditContext(@Nonnull final ProfileRequestContext profileRequestContext) {
+ return auditContextCreationStrategy.apply(profileRequestContext);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected Event doExecute(@Nonnull final RequestContext springRequestContext,
+ @Nonnull final ProfileRequestContext profileRequestContext) {
+
+ requestContext = springRequestContext;
+ return super.doExecute(springRequestContext, profileRequestContext);
+ }
+
+ /**
+ * Audit a successful operation. This will automatically add a 'success' field to the audit context.
+ *
+ * @param profileRequestContext the profile request context
+ * @param action a descriptive string of the action that was performed. Can be {@code null} if not used.
+ */
+ protected void auditSuccess(@Nonnull final ProfileRequestContext profileRequestContext,
+ @Nullable final String action) {
+ doAudit(profileRequestContext, true, action);
+ }
+
+ /**
+ * Audit a failed operation. This will automatically add a 'failure' field to the audit context.
+ *
+ * @param profileRequestContext the profile request context
+ * @param action a descriptive string of the action that was performed. Can be {@code null} if not used.
+ */
+ protected void auditFailure(@Nonnull final ProfileRequestContext profileRequestContext,
+ @Nullable final String action) {
+ doAudit(profileRequestContext, false, action);
+ }
+
+// Checkstyle: CyclomaticComplexity OFF
+ /**
+ * Do audit extraction and output.
+ *
+ * @param profileRequestContext profile request context
+ * @param success true iff this is an audit of successful action, false otherwise
+ * @param action the type of action that was performed
+ */
+ protected void doAudit(@Nonnull final ProfileRequestContext profileRequestContext, final boolean success,
+ @Nullable final String action) {
+
+ if (populateAuditContextAction != null && writeAuditLogAction != null) {
+ final EventContext existingEvent = profileRequestContext.getSubcontext(EventContext.class);
+
+ try {
+ assert populateAuditContextAction != null;
+ populateAuditContextAction.execute(requestContext);
+
+ final AuditContext ac = getAuditContext(profileRequestContext);
+ if (ac != null) {
+ final Map<String,String> fields = getAuditFields(profileRequestContext);
+ if (fields != null) {
+ for (final Map.Entry<String,String> field : fields.entrySet()) {
+ final String key = field.getKey();
+ assert key != null;
+ ac.getFieldValues(key).add(field.getValue());
+ }
+ }
+
+ // Add a success or failure field
+ if (success) {
+ ac.getFields().put(WebAuthnAuditFields.ACTION_OUTCOME, "success");
+ } else {
+ // TODO this could come from an error context
+ ac.getFields().put(WebAuthnAuditFields.ACTION_OUTCOME, "failure");
+ }
+ if (action != null && !action.isEmpty()) {
+ ac.getFields().put(WebAuthnAuditFields.ACTION, action);
+ }
+ }
+ } finally {
+ if (existingEvent != null) {
+ profileRequestContext.addSubcontext(existingEvent);
+ }
+ }
+
+ try {
+ assert writeAuditLogAction != null;
+ writeAuditLogAction.execute(requestContext);
+ } finally {
+ if (existingEvent != null) {
+ profileRequestContext.addSubcontext(existingEvent);
+ }
+ }
+ }
+ }
+// Checkstyle: CyclomaticComplexity ON
+
+ /**
+ * Subclasses can override this method to supply additional audit fields to store.
+ *
+ * @param profileRequestContext profile request context
+ * @return audit fields
+ */
+ @Nullable @Unmodifiable @NotLive protected Map<String,String> getAuditFields(
+ @Nonnull final ProfileRequestContext profileRequestContext) {
+ return null;
+ }
+}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/impl/WebAuthnCredentialToRemoveAuditExtractor.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/impl/WebAuthnCredentialToRemoveAuditExtractor.java
new file mode 100644
index 0000000..a685af3
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/impl/WebAuthnCredentialToRemoveAuditExtractor.java
@@ -0,0 +1,59 @@
+/*
+ * 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.idp.plugin.authn.webauthn.audit.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.plugin.authn.webauthn.audit.AbstractWebAuthnManagementAuditExtractor;
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnManagementContext;
+import net.shibboleth.shared.codec.Base64Support;
+import net.shibboleth.shared.codec.EncodingException;
+
+/**
+ * {@link Function} that returns the base64 encoded credential identifier of the credential that has been removed.
+ */
+public class WebAuthnCredentialToRemoveAuditExtractor extends AbstractWebAuthnManagementAuditExtractor<String> {
+
+ /**
+ * Constructor.
+ *
+ * @param strategy the strategy used to locate the {@link WebAuthnManagementContext}.
+ */
+ public WebAuthnCredentialToRemoveAuditExtractor(
+ @Nonnull final Function<ProfileRequestContext, WebAuthnManagementContext> strategy) {
+ super(strategy);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String doLookup(@Nonnull final WebAuthnManagementContext context) {
+ final byte[] credentialToRemove = context.getCredentialIdToRemove();
+ if (credentialToRemove == null) {
+ return null;
+ }
+
+ try {
+ return Base64Support.encodeURLSafe(credentialToRemove);
+ } catch (final EncodingException e) {
+ return null;
+ }
+
+ }
+
+}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/impl/WebAuthnSearchUserAuditExtractor.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/impl/WebAuthnSearchUserAuditExtractor.java
new file mode 100644
index 0000000..f2a7321
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/impl/WebAuthnSearchUserAuditExtractor.java
@@ -0,0 +1,47 @@
+/*
+ * 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.idp.plugin.authn.webauthn.audit.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.plugin.authn.webauthn.audit.AbstractWebAuthnManagementAuditExtractor;
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnManagementContext;
+
+/**
+ * {@link Function} that returns which username that the admin user searched for.
+ */
+public class WebAuthnSearchUserAuditExtractor extends AbstractWebAuthnManagementAuditExtractor<String> {
+
+ /**
+ * Constructor.
+ *
+ * @param strategy the strategy used to locate the {@link WebAuthnManagementContext}.
+ */
+ public WebAuthnSearchUserAuditExtractor(
+ @Nonnull final Function<ProfileRequestContext, WebAuthnManagementContext> strategy) {
+ super(strategy);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String doLookup(@Nonnull final WebAuthnManagementContext context) {
+ return context.getSearchUsername();
+ }
+
+}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/impl/WebAuthnUserCredentialAddedAuditExtractor.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/impl/WebAuthnUserCredentialAddedAuditExtractor.java
new file mode 100644
index 0000000..c612c4e
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/impl/WebAuthnUserCredentialAddedAuditExtractor.java
@@ -0,0 +1,63 @@
+/*
+ * 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.idp.plugin.authn.webauthn.audit.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.plugin.authn.webauthn.audit.AbstractWebAuthnRegistrationAuditExtractor;
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
+import net.shibboleth.shared.codec.Base64Support;
+import net.shibboleth.shared.codec.EncodingException;
+
+/**
+ * {@link Function} that returns the base64 encoded credential identifier of the credential that has been removed.
+ */
+public class WebAuthnUserCredentialAddedAuditExtractor extends AbstractWebAuthnRegistrationAuditExtractor<String> {
+
+ /**
+ * Constructor.
+ *
+ * @param strategy the strategy used to locate the {@link WebAuthnRegistrationContext}.
+ */
+ public WebAuthnUserCredentialAddedAuditExtractor(
+ @Nonnull final Function<ProfileRequestContext, WebAuthnRegistrationContext> strategy) {
+ super(strategy);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String doLookup(@Nonnull final WebAuthnRegistrationContext context) {
+ final var result = context.getRegistrationResult();
+ if (result == null) {
+ return null;
+ }
+ final byte[] credentialToRemove = result.getKeyId().getId().getBytes();
+ if (credentialToRemove == null) {
+ return null;
+ }
+
+ try {
+ return Base64Support.encodeURLSafe(credentialToRemove);
+ } catch (final EncodingException e) {
+ return null;
+ }
+
+ }
+
+}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/impl/WebAuthnUserCredentialToRemoveAuditExtractor.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/impl/WebAuthnUserCredentialToRemoveAuditExtractor.java
new file mode 100644
index 0000000..ba9e954
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/audit/impl/WebAuthnUserCredentialToRemoveAuditExtractor.java
@@ -0,0 +1,59 @@
+/*
+ * 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.idp.plugin.authn.webauthn.audit.impl;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.plugin.authn.webauthn.audit.AbstractWebAuthnRegistrationAuditExtractor;
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
+import net.shibboleth.shared.codec.Base64Support;
+import net.shibboleth.shared.codec.EncodingException;
+
+/**
+ * {@link Function} that returns the base64 encoded credential identifier of the credential that has been removed.
+ */
+public class WebAuthnUserCredentialToRemoveAuditExtractor extends AbstractWebAuthnRegistrationAuditExtractor<String> {
+
+ /**
+ * Constructor.
+ *
+ * @param strategy the strategy used to locate the {@link WebAuthnRegistrationContext}.
+ */
+ public WebAuthnUserCredentialToRemoveAuditExtractor(
+ @Nonnull final Function<ProfileRequestContext, WebAuthnRegistrationContext> strategy) {
+ super(strategy);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String doLookup(@Nonnull final WebAuthnRegistrationContext context) {
+ final byte[] credentialToRemove = context.getCredentialIdToRemove();
+ if (credentialToRemove == null) {
+ return null;
+ }
+
+ try {
+ return Base64Support.encodeURLSafe(credentialToRemove);
+ } catch (final EncodingException e) {
+ return null;
+ }
+
+ }
+
+}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnAuthenticationAction.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnAction.java
similarity index 57%
rename from webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnAuthenticationAction.java
rename to webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnAction.java
index 269d6e6..95d73d2 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnAuthenticationAction.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnAction.java
@@ -1,10 +1,7 @@
/*
- * 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
+ * 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
*
@@ -22,17 +19,18 @@ import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-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 net.shibboleth.idp.authn.AbstractAuthenticationAction;
+import com.yubico.fido.metadata.FidoMetadataService;
+
import net.shibboleth.idp.authn.AuthnEventIds;
-import net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.plugin.authn.webauthn.client.WebAuthnAuthenticationClient;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnAuthenticationContext;
import net.shibboleth.idp.plugin.authn.webauthn.storage.StorageServiceCredentialRepository;
+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;
@@ -41,37 +39,54 @@ import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.LoggerFactory;
/**
- * <p>A base class for WebAuthn authentication related actions.</p>
+ * <p>A base class for WebAuthn actions. Makes available the WebAuthn client, credential repository, and
+ * metadata service</p>
+ *
+ * <p>In addition to the work performed by {@link AbstractProfileAction}, this action also looks up
+ * and makes available a WebAuthn context <T>.</p>
*
- * <p>In addition to the work performed by {@link AbstractAuthenticationAction}, this action also looks up
- * and makes available the {@link WebAuthnAuthenticationContext}.</p>
+ * <p>WebAuthn implementations should override the {@link #doExecute(ProfileRequestContext, T)} method.</p>
*
- * <p>Webauthn authentication action implementations should override the
- * {@link #doExecute(ProfileRequestContext, AuthenticationContext, WebAuthnAuthenticationContext)}
- * method.</p>
+ * @event {@link EventIds#INVALID_PROFILE_CTX}
+ * @pre <pre>ProfileRequestContext.getSubcontext(ProfileRequestContext.class) != null</pre>
*
- * @event {@link AuthnEventIds#INVALID_AUTHN_CTX}
- * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class) != null</pre>
- * @post <pre>AuthenticationContext.getSubcontext(WebAuthnAuthenticationContext.class) != null</pre>
+ * @param <T> the WebAuthn context to lookup
*/
-//TODO somehow this should be a child of the base webauthn action (BaseWebAuthnAction)?
-public abstract class AbstractWebAuthnAuthenticationAction extends AbstractAuthenticationAction {
+public class AbstractWebAuthnAction<T> extends AbstractProfileAction {
/** Class logger. */
- @Nonnull @NotEmpty private final Logger log = LoggerFactory.getLogger(AbstractWebAuthnAuthenticationAction.class);
+ @Nonnull @NotEmpty private final Logger log = LoggerFactory.getLogger(AbstractWebAuthnAction.class);
- /** Lookup strategy to locate the WebAuthn authentication context. */
- @Nonnull private Function<ProfileRequestContext,WebAuthnAuthenticationContext> webauthnContextLookupStrategy;
+ /** Lookup strategy to locate the WebAuthn context. */
+ @Nonnull private Function<ProfileRequestContext,T> webauthnContextLookupStrategy;
- /** The WebAuthn authentication Context.*/
- @NonnullBeforeExec private WebAuthnAuthenticationContext webauthnContext;
+ /** The WebAuthn Context.*/
+ @NonnullBeforeExec private T webauthnContext;
/** The WebAuthn client to use.*/
@NonnullAfterInit private WebAuthnAuthenticationClient webAuthnClient;
- /** The credential repository to store WebAuthn credentials in.*/
+ /** The credential repository to store valid credentials in.*/
@Nullable private StorageServiceCredentialRepository credentialRepository;
+ /** Optional FIDO metadata service resolver.*/
+ @Nullable private FidoMetadataService fidoMetadataService;
+
+ /** Constructor.*/
+ protected AbstractWebAuthnAction(@Nonnull final Function<ProfileRequestContext,T> defaultStrategy) {
+ webauthnContextLookupStrategy =
+ Constraint.isNotNull(defaultStrategy, "Default WebAuthn lookup strategy can not be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ if (webAuthnClient == null) {
+ throw new ComponentInitializationException("WebAuthn Client can not be null");
+ }
+ }
/**
* Set the WebAuthn client used to handle registration and authentication ceremonies.
@@ -88,11 +103,31 @@ public abstract class AbstractWebAuthnAuthenticationAction extends AbstractAuthe
*
* @return the WebAuthn client.
*/
- @NonnullBeforeExec protected WebAuthnAuthenticationClient getWebAuthnClient() {
+ @NonnullAfterInit protected WebAuthnAuthenticationClient getWebAuthnClient() {
checkComponentActive();
return webAuthnClient;
}
+ /**
+ * Set the FIDO Alliance metadata service resolver to use as the attestation trust source.
+ *
+ * @param service The FIDO metadata service to set.
+ */
+ public void setFidoMetadataService(@Nullable final FidoMetadataService service) {
+ checkSetterPreconditions();
+ fidoMetadataService = service;
+ }
+
+ /**
+ * Get the FIDO Alliance metadata service resolver to use as the attestation trust source.
+ *
+ * @return the FIDO metadata service.
+ */
+ @Nullable protected FidoMetadataService getFidoMetadataService() {
+ checkComponentActive();
+ return fidoMetadataService;
+ }
+
/**
* Set the credential repository used to store WebAuthn credentials.
*
@@ -110,23 +145,15 @@ public abstract class AbstractWebAuthnAuthenticationAction extends AbstractAuthe
*/
@Nullable protected StorageServiceCredentialRepository getCredentialRepository() {
return credentialRepository;
- }
-
- /** Constructor.*/
- protected AbstractWebAuthnAuthenticationAction() {
- //prc -> ac -> dc
- webauthnContextLookupStrategy = new ChildContextLookup<>(WebAuthnAuthenticationContext.class).
- compose(new ChildContextLookup<>(AuthenticationContext.class));
}
-
/**
- * Set the WebAuthn authentication context lookup strategy to use.
+ * Set the WebAuthn context lookup strategy to use.
*
* @param strategy lookup strategy
*/
- public void setWebauthnContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,WebAuthnAuthenticationContext> strategy) {
+ public void setWebAuthnContextLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext,T> strategy) {
checkSetterPreconditions();
webauthnContextLookupStrategy =
@@ -135,61 +162,46 @@ public abstract class AbstractWebAuthnAuthenticationAction extends AbstractAuthe
/** {@inheritDoc} */
@Override
- protected void doInitialize() throws ComponentInitializationException {
- super.doInitialize();
-
- if (webAuthnClient == null) {
- throw new ComponentInitializationException("WebAuthn Client can not be null");
- }
- }
-
- /** {@inheritDoc} */
- @Override
- protected final boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext) {
+ protected final boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- if (!super.doPreExecute(profileRequestContext, authenticationContext)) {
+ if (!super.doPreExecute(profileRequestContext)) {
return false;
}
webauthnContext = webauthnContextLookupStrategy.apply(profileRequestContext);
if (webauthnContext == null) {
- log.warn("{} No WebAuthn authentication context returned by lookup strategy",getLogPrefix());
+ log.warn("{} No WebAuthn context returned by lookup strategy",getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_AUTHN_CTX);
return false;
}
assert webauthnContext != null;
- return doPreExecute(profileRequestContext, authenticationContext, webauthnContext);
+ return doPreExecute(profileRequestContext, webauthnContext);
}
/**
- * Delegates to {@link #doExecute(ProfileRequestContext, AuthenticationContext,
- * WebAuthnAuthenticationContext)} to perform the actual authentication. Implementations can not
+ * Delegates to {@link #doExecute(ProfileRequestContext, WebAuthnAuthenticationContext)}. Implementations can not
* override this method.
*
* @param profileRequestContext the current IdP profile request context
* @param authenticationContext the current authentication context
*/
@Override
- protected final void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext) {
+ protected final void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
assert webauthnContext != null;
- doExecute(profileRequestContext,authenticationContext, webauthnContext);
+ doExecute(profileRequestContext, webauthnContext);
}
/**
* Performs this authentication action's pre-execute step. Default implementation just returns true.
*
* @param profileRequestContext the current IdP profile request context
- * @param authenticationContext the current authentication context
- * @param context the WebAuthn authentication context
+ * @param context the WebAuthn context
*
* @return true iff execution should continue
*/
protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext,
- @Nonnull final WebAuthnAuthenticationContext context) {
+ @Nonnull final T context) {
return true;
}
@@ -198,13 +210,12 @@ public abstract class AbstractWebAuthnAuthenticationAction extends AbstractAuthe
* should override this method.
*
* @param profileRequestContext the current IdP profile request context
- * @param authenticationContext the current authentication context
- * @param context the WebAuthn authentication context
+ * @param context the WebAuthn context
*/
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext,
- @Nonnull final WebAuthnAuthenticationContext context) {
-
+ @Nonnull final T context) {
+ // Default does nothing
}
+
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnBaseAction.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnBaseAction.java
deleted file mode 100644
index 34d30a7..0000000
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnBaseAction.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * 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.idp.plugin.authn.webauthn.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 net.shibboleth.idp.authn.context.AuthenticationContext;
-import net.shibboleth.idp.plugin.authn.webauthn.context.BaseWebAuthnContext;
-import net.shibboleth.idp.profile.AbstractProfileAction;
-import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
-
-/**
- * <p>A base class for WebAuthn actions that require access to the base WebAuthn context.</p>
- *
- * <p>In addition to the work performed by {@link AbstractProfileAction}, this action also looks up
- * and makes available the {@link BaseWebAuthnContext}.</p>
- *
- * <p>WebAuthn action implementations should override the
- * {@link #doExecute(ProfileRequestContext, BaseWebAuthnContext)} method.</p>
- *
- * @event {@link EventIds#INVALID_PROFILE_CTX}
- * @pre <pre>ProfileRequestContext.getSubcontext(ProfileRequestContext.class) != null</pre>
- * @post <pre>AuthenticationContext.getSubcontext(BaseWebAuthnContext.class) != null</pre>
- */
-//TODO a few abstract action types, remove overlap
-public abstract class AbstractWebAuthnBaseAction extends BaseWebAuthnAction {
-
- /** Class logger. */
- @Nonnull @NotEmpty private final Logger log = LoggerFactory.getLogger(AbstractWebAuthnBaseAction.class);
-
- /** Lookup strategy to locate the WebAuthn base context. */
- @Nonnull
- private Function<ProfileRequestContext,BaseWebAuthnContext> webAuthnBaseContextLookupStrategy;
-
- /** The WebAuthn base Context.*/
- @NonnullBeforeExec private BaseWebAuthnContext webAuthnBaseContext;
-
-
- /** Constructor.*/
- protected AbstractWebAuthnBaseAction() {
- webAuthnBaseContextLookupStrategy = new ChildContextLookup<>(BaseWebAuthnContext.class).
- compose(new ChildContextLookup<>(AuthenticationContext.class));
- }
-
-
- /**
- * Set WebAuthn base context lookup strategy to use.
- *
- * @param strategy lookup strategy
- */
- public void setWebAuthnBaseContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,BaseWebAuthnContext> strategy) {
- checkSetterPreconditions();
-
- webAuthnBaseContextLookupStrategy =
- Constraint.isNotNull(strategy, "WebauthnContextLookuplookup strategy cannot be null");
- }
-
-
- /** {@inheritDoc} */
- @Override
- protected final boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-
- if (!super.doPreExecute(profileRequestContext)) {
- return false;
- }
-
- webAuthnBaseContext = webAuthnBaseContextLookupStrategy.apply(profileRequestContext);
- if (webAuthnBaseContext == null) {
- log.warn("{} No WebAuthn base context returned by lookup strategy",getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return false;
-
- }
- assert webAuthnBaseContext != null;
- return doPreExecute(profileRequestContext, webAuthnBaseContext);
- }
-
- /**
- * Delegates to {@link #doExecute(ProfileRequestContext, BaseWebAuthnContext)} to perform the
- * actual action. Implementations can not override this method.
- *
- * @param profileRequestContext the current IdP profile request context
- */
- @Override
- protected final void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- assert webAuthnBaseContext != null;
- doExecute(profileRequestContext, webAuthnBaseContext);
- }
-
- /**
- * Performs this action's pre-execute step. Default implementation just returns true.
- *
- * @param profileRequestContext the current IdP profile request context
- * @param context the WebAuthn base context
- *
- * @return true iff execution should continue
- */
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final BaseWebAuthnContext context) {
- return true;
- }
-
- /**
- * Performs this WebAuthn authentication action using the supplied WebAuthn context. Implementations
- * should override this method.
- *
- * @param profileRequestContext the current IdP profile request context
- * @param context the WebAuthn base context
- */
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final BaseWebAuthnContext context) {
-
- }
-
-}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AddUserVerificationRequirement.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AddUserVerificationRequirement.java
index 2e485e9..731d212 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AddUserVerificationRequirement.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AddUserVerificationRequirement.java
@@ -18,11 +18,13 @@ import java.util.stream.Stream;
import javax.annotation.Nonnull;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import com.yubico.webauthn.data.UserVerificationRequirement;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.plugin.authn.webauthn.context.BaseWebAuthnContext;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnAuthenticationContext;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
@@ -36,7 +38,7 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* @pre <pre>ProfileRequestContext.getSubcontext(BaseWebAuthnContext.class) != null</pre>
* @post <pre>WebAuthnRegistrationContext.getUserVerificationRequirement() != null</pre>
*/
-public class AddUserVerificationRequirement extends AbstractWebAuthnBaseAction {
+public class AddUserVerificationRequirement extends AbstractWebAuthnAction<BaseWebAuthnContext> {
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(AddUserVerificationRequirement.class);
@@ -46,6 +48,8 @@ public class AddUserVerificationRequirement extends AbstractWebAuthnBaseAction {
/** Constructor.*/
public AddUserVerificationRequirement() {
+ super(new ChildContextLookup<>(BaseWebAuthnContext.class).
+ compose(new ChildContextLookup<>(AuthenticationContext.class)));
userVerificationRequirement = UserVerificationRequirement.PREFERRED;
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/BaseWebAuthnAction.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/BaseWebAuthnAction.java
deleted file mode 100644
index 4ff3e87..0000000
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/BaseWebAuthnAction.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.idp.plugin.authn.webauthn.impl;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import com.yubico.fido.metadata.FidoMetadataService;
-
-import net.shibboleth.idp.plugin.authn.webauthn.client.WebAuthnAuthenticationClient;
-import net.shibboleth.idp.plugin.authn.webauthn.storage.StorageServiceCredentialRepository;
-import net.shibboleth.idp.profile.AbstractProfileAction;
-import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.logic.Constraint;
-
-/**
- * A base action for WebAuthn actions that require access to the client, credential repository, and metadata service.
- */
-public abstract class BaseWebAuthnAction extends AbstractProfileAction {
-
- /** The WebAuthn client to use.*/
- @NonnullAfterInit private WebAuthnAuthenticationClient webAuthnClient;
-
- /** The credential repository to store valid credentials in.*/
- @NonnullAfterInit private StorageServiceCredentialRepository credentialRepository;
-
- /** Optional FIDO metadata service resolver.*/
- @Nullable private FidoMetadataService fidoMetadataService;
-
- /** {@inheritDoc} */
- @Override
- protected void doInitialize() throws ComponentInitializationException {
- super.doInitialize();
-
- if (webAuthnClient == null) {
- throw new ComponentInitializationException("WebAuthn Client can not be null");
- }
- if (credentialRepository == null) {
- throw new ComponentInitializationException("CredentialRepository can not be null");
- }
- }
-
- /**
- * Set the WebAuthn client used to handle registration and authentication ceremonies.
- *
- * @param client The WebAuthn client to set.
- */
- public void setWebAuthnClient(@Nonnull final WebAuthnAuthenticationClient client) {
- checkSetterPreconditions();
- webAuthnClient = Constraint.isNotNull(client, "WebAuthn client can not be null");
- }
-
- /**
- * Get the WebAuthn client used to handle registration and authentication ceremonies.
- *
- * @return the webAuthnClient.
- */
- @NonnullAfterInit protected WebAuthnAuthenticationClient getWebAuthnClient() {
- checkComponentActive();
- return webAuthnClient;
- }
-
- /**
- * Set the FIDO Alliance metadata service resolver to use as the attestation trust source.
- *
- * @param service The FIDO metadata service to set.
- */
- public void setFidoMetadataService(@Nullable final FidoMetadataService service) {
- checkSetterPreconditions();
- fidoMetadataService = service;
- }
-
- /**
- * Get the FIDO Alliance metadata service resolver to use as the attestation trust source.
- *
- * @return the FIDO metadata service.
- */
- @Nullable protected FidoMetadataService getFidoMetadataService() {
- checkComponentActive();
- return fidoMetadataService;
- }
-
- /**
- * Set the credential repository used to store WebAuthn credentials.
- *
- * @param repository The respository to set.
- */
- public void setCredentialRepository(@Nonnull final StorageServiceCredentialRepository repository) {
- checkSetterPreconditions();
- credentialRepository = Constraint.isNotNull(repository, "Credential respository can not be null");
- }
-
- /**
- * Get the credential repository used to store WebAuthn credentials.
- *
- * @return the credential repository.
- */
- @NonnullAfterInit protected StorageServiceCredentialRepository getCredentialRepository() {
- return credentialRepository;
- }
-
-}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/CreatePublicKeyCredentialRequestOptions.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/CreatePublicKeyCredentialRequestOptions.java
index 1fc493c..4b9838f 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/CreatePublicKeyCredentialRequestOptions.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/CreatePublicKeyCredentialRequestOptions.java
@@ -22,6 +22,7 @@ import java.util.stream.Collectors;
import javax.annotation.Nonnull;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
@@ -47,14 +48,19 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* @pre <pre>ProfileRequestContext.getSubcontext(WebAuthnAuthenticationContext.class) != null</pre>
* @post A PublicKeyCredentialRequestOptions is added to the authentication context
*/
-public class CreatePublicKeyCredentialRequestOptions extends AbstractWebAuthnAuthenticationAction {
+public class CreatePublicKeyCredentialRequestOptions extends AbstractWebAuthnAction<WebAuthnAuthenticationContext> {
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(CreatePublicKeyCredentialRequestOptions.class);
+ /** Constructor. */
+ protected CreatePublicKeyCredentialRequestOptions() {
+ super(new ChildContextLookup<>(WebAuthnAuthenticationContext.class).
+ compose(new ChildContextLookup<>(AuthenticationContext.class)));
+ }
+
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext,
@Nonnull final WebAuthnAuthenticationContext context) {
final WebAuthnAuthenticationClient client = getWebAuthnClient();
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/EnsureAllowedCredentialsIsEmpty.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/EnsureAllowedCredentialsIsEmpty.java
index 5425528..fd0656a 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/EnsureAllowedCredentialsIsEmpty.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/EnsureAllowedCredentialsIsEmpty.java
@@ -16,6 +16,7 @@ package net.shibboleth.idp.plugin.authn.webauthn.impl;
import javax.annotation.Nonnull;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
@@ -29,15 +30,20 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* @pre <pre>ProfileRequestContext.getSubcontext(WebAuthnAuthenticationContext.class) != null</pre>
* @post <pre>WebAuthnAuthenticationContext.getExistingCredentials() == null</pre>
*/
-public class EnsureAllowedCredentialsIsEmpty extends AbstractWebAuthnAuthenticationAction {
+public class EnsureAllowedCredentialsIsEmpty extends AbstractWebAuthnAction<WebAuthnAuthenticationContext> {
/** Class logger. */
@Nonnull
private final Logger log = LoggerFactory.getLogger(EnsureAllowedCredentialsIsEmpty.class);
+
+ /** Constructor. */
+ protected EnsureAllowedCredentialsIsEmpty() {
+ super(new ChildContextLookup<>(WebAuthnAuthenticationContext.class).
+ compose(new ChildContextLookup<>(AuthenticationContext.class)));
+ }
/** {@inheritDoc} */
@Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext,
@Nonnull final WebAuthnAuthenticationContext context) {
log.trace("{} Removing any existing credentials found from the authentication request", getLogPrefix());
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ExtractPublicKeyCredentialAssertionFromFormRequest.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ExtractPublicKeyCredentialAssertionFromFormRequest.java
index 6333e31..1e5be8d 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ExtractPublicKeyCredentialAssertionFromFormRequest.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ExtractPublicKeyCredentialAssertionFromFormRequest.java
@@ -21,6 +21,7 @@ import java.io.IOException;
import javax.annotation.Nonnull;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
@@ -50,7 +51,8 @@ import net.shibboleth.shared.primitive.StringSupport;
* @pre <pre>ProfileRequestContext.getSubcontext(WebAuthnAuthenticationContext.class) != null</pre>
* @post <pre>WebAuthnAuthenticationContext.getAuthenticatorAssertionResponse() == null</pre>
*/
-public class ExtractPublicKeyCredentialAssertionFromFormRequest extends AbstractWebAuthnAuthenticationAction {
+public class ExtractPublicKeyCredentialAssertionFromFormRequest
+ extends AbstractWebAuthnAction<WebAuthnAuthenticationContext> {
/** Default public key credential assertion parameter name. */
@Nonnull @NotEmpty public static final String DEFAULT_PARAMETER_NAME = "publicKeyCredential";
@@ -65,7 +67,9 @@ public class ExtractPublicKeyCredentialAssertionFromFormRequest extends Abstract
@NonnullAfterInit private ObjectMapper objectMapper;
/** Constructor. */
- public ExtractPublicKeyCredentialAssertionFromFormRequest() {
+ public ExtractPublicKeyCredentialAssertionFromFormRequest() {
+ super(new ChildContextLookup<>(WebAuthnAuthenticationContext.class).
+ compose(new ChildContextLookup<>(AuthenticationContext.class)));
publicKeyCredentialAssertionParameterName = DEFAULT_PARAMETER_NAME;
}
@@ -102,7 +106,6 @@ public class ExtractPublicKeyCredentialAssertionFromFormRequest extends Abstract
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext,
@Nonnull final WebAuthnAuthenticationContext context) {
final HttpServletRequest request = getHttpServletRequest();
@@ -114,7 +117,7 @@ public class ExtractPublicKeyCredentialAssertionFromFormRequest extends Abstract
final String pkCredAssertionJson = request.getParameter(publicKeyCredentialAssertionParameterName);
if (pkCredAssertionJson == null) {
- log.debug("{} No PublicKeyCredential with authenticator attestation response in request",getLogPrefix());
+ log.debug("{} No PublicKeyCredential with authenticator assertion response in request",getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
return;
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/GenerateServerChallenge.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/GenerateServerChallenge.java
index 2c05b29..156ee37 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/GenerateServerChallenge.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/GenerateServerChallenge.java
@@ -24,11 +24,13 @@ import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+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 net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.plugin.authn.webauthn.context.BaseWebAuthnContext;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.LoggerFactory;
@@ -43,7 +45,7 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* @event {@link org.opensaml.profile.action.EventIds#INVALID_PROFILE_CTX}
* @post a challenge is added to the {@link BaseWebAuthnContext base context}
*/
-public class GenerateServerChallenge extends AbstractWebAuthnBaseAction {
+public class GenerateServerChallenge extends AbstractWebAuthnAction<BaseWebAuthnContext> {
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(GenerateServerChallenge.class);
@@ -53,6 +55,8 @@ public class GenerateServerChallenge extends AbstractWebAuthnBaseAction {
/** Constructor. */
public GenerateServerChallenge() {
+ super(new ChildContextLookup<>(BaseWebAuthnContext.class).
+ compose(new ChildContextLookup<>(AuthenticationContext.class)));
challengeGeneratorStrategy = new DefaultChallengeGenerator();
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/LookupRegisteredCredentials.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/LookupRegisteredCredentials.java
index abcadef..16ba7d5 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/LookupRegisteredCredentials.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/LookupRegisteredCredentials.java
@@ -20,6 +20,7 @@ import java.util.function.Predicate;
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;
@@ -28,10 +29,14 @@ import org.slf4j.Logger;
import com.yubico.webauthn.data.ByteArray;
import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.plugin.authn.webauthn.authn.WebAuthnAuthenticationEventIds;
import net.shibboleth.idp.plugin.authn.webauthn.context.BaseWebAuthnContext;
import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRegistration;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.StorageServiceCredentialRepository;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.logic.PredicateSupport;
import net.shibboleth.shared.primitive.LoggerFactory;
@@ -46,7 +51,7 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* the credentials from the credential repository. {@link BaseWebAuthnContext#getUserId()} != null if the userHandle is
* found from the username.
*/
-public class LookupRegisteredCredentials extends AbstractWebAuthnBaseAction {
+public class LookupRegisteredCredentials extends AbstractWebAuthnAction<BaseWebAuthnContext> {
/** Class logger. */
@Nonnull
@@ -61,8 +66,23 @@ public class LookupRegisteredCredentials extends AbstractWebAuthnBaseAction {
*/
@Nonnull @NotEmpty private String noCredentialsEventId;
+ /** The credential repository to use.*/
+ @NonnullAfterInit private StorageServiceCredentialRepository repository;
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+ repository = getCredentialRepository();
+ if (repository == null) {
+ throw new ComponentInitializationException("Credential repository can not be null");
+ }
+ }
+
/** Constructor. */
public LookupRegisteredCredentials() {
+ super(new ChildContextLookup<>(BaseWebAuthnContext.class).
+ compose(new ChildContextLookup<>(AuthenticationContext.class)));
noCredentialsEventId = WebAuthnAuthenticationEventIds.NO_REGISTERED_WEBAUTHN_CREDENTIALS;
triggerEventOnNoCredentialsPredicate = PredicateSupport.alwaysFalse();
}
@@ -111,12 +131,12 @@ public class LookupRegisteredCredentials extends AbstractWebAuthnBaseAction {
}
final Collection<CredentialRegistration> credentials =
- getCredentialRepository().getRegistrationsByUsername(username);
+ repository.getRegistrationsByUsername(username);
log.debug("{} Found '{}' registered credentials for '{}'", getLogPrefix(), credentials.size(), username);
context.setExistingCredentials(credentials);
- final Optional<ByteArray> userHandle = getCredentialRepository().getUserHandleForUsername(username);
+ final Optional<ByteArray> userHandle = repository.getUserHandleForUsername(username);
if (userHandle.isPresent()) {
final byte[] userId = userHandle.get().getBytes();
assert userId != null;
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/LookupRegisteredCredentialsFromUserHandle.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/LookupRegisteredCredentialsFromUserHandle.java
index b0fe054..d739e5e 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/LookupRegisteredCredentialsFromUserHandle.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/LookupRegisteredCredentialsFromUserHandle.java
@@ -20,6 +20,7 @@ import java.util.function.Predicate;
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;
@@ -36,6 +37,7 @@ import net.shibboleth.idp.plugin.authn.webauthn.authn.WebAuthnAuthenticationEven
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnAuthenticationContext;
import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRegistration;
import net.shibboleth.idp.plugin.authn.webauthn.storage.StorageServiceCredentialRepository;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.logic.Constraint;
@@ -53,7 +55,7 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* @post BaseWebAuthnContext.setExistingCredentials() is either null if no existing credentials are found, or contains
* the credentials from the credential repository
*/
-public class LookupRegisteredCredentialsFromUserHandle extends AbstractWebAuthnAuthenticationAction {
+public class LookupRegisteredCredentialsFromUserHandle extends AbstractWebAuthnAction<WebAuthnAuthenticationContext> {
/** Class logger. */
@Nonnull
@@ -68,8 +70,23 @@ public class LookupRegisteredCredentialsFromUserHandle extends AbstractWebAuthnA
*/
@Nonnull @NotEmpty private String noCredentialsEventId;
+ /** The credential repository to use.*/
+ @NonnullAfterInit private StorageServiceCredentialRepository repository;
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+ repository = getCredentialRepository();
+ if (repository == null) {
+ throw new ComponentInitializationException("Credential repository can not be null");
+ }
+ }
+
/** Constructor. */
public LookupRegisteredCredentialsFromUserHandle() {
+ super(new ChildContextLookup<>(WebAuthnAuthenticationContext.class).
+ compose(new ChildContextLookup<>(AuthenticationContext.class)));
noCredentialsEventId = WebAuthnAuthenticationEventIds.NO_REGISTERED_WEBAUTHN_CREDENTIALS;
triggerEventOnNoCredentialsPredicate = PredicateSupport.alwaysFalse();
}
@@ -105,20 +122,9 @@ public class LookupRegisteredCredentialsFromUserHandle extends AbstractWebAuthnA
checkSetterPreconditions();
noCredentialsEventId = Constraint.isNotEmpty(eventId, "NoCredentialsEventId can not be null or empty");
}
-
- /** {@inheritDoc} */
- @Override
- protected void doInitialize() throws ComponentInitializationException {
- super.doInitialize();
-
- if (getCredentialRepository() == null) {
- throw new ComponentInitializationException("The credential repository can not be null");
- }
- }
/** {@inheritDoc} */
@Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final AuthenticationContext authenticationContext,
@Nonnull final WebAuthnAuthenticationContext context) {
final PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs> assertion =
@@ -130,9 +136,6 @@ public class LookupRegisteredCredentialsFromUserHandle extends AbstractWebAuthnA
}
final String username = context.getUsername();
- final StorageServiceCredentialRepository repository = getCredentialRepository();
- assert repository != null;
-
final Optional<ByteArray> userHandle = assertion.getResponse().getUserHandle();
boolean credentialsFound = false;
if (userHandle.isEmpty()) {
diff --git a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-management/webauthn-management-beans.xml b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-management/webauthn-management-beans.xml
index 42c252d..b8c70ba 100644
--- a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-management/webauthn-management-beans.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-management/webauthn-management-beans.xml
@@ -58,7 +58,7 @@
<bean id="LookupCredentialsForUser" parent="AbstractWebAuthnBaseAction" scope="prototype"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.LookupCredentialsForUser"
- p:webauthnManagementContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnManagementContext" />
+ p:webAuthnContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnManagementContext" />
<bean id="ExtractKeyRemovalInformationFromFormRequest" scope="prototype"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.ExtractKeyRemovalInformationFromFormRequest"
@@ -69,7 +69,11 @@
</bean>
<bean id="DeletePublicKeyCredential" parent="AbstractWebAuthnManagementAction" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.AdminDeletePublicKeyCredential" />
+ class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.AdminDeletePublicKeyCredential"
+ p:populateAuditContextAction="#{%{idp.authn.webauthn.admin.management.audit.enabled:false} ? getObject('PostAdminFunctionPopulateAuditContext') : null}"
+ p:writeAuditLogAction="#{%{idp.authn.webauthn.admin.management.audit.enabled:false} ? getObject('WriteAdminAuditLog') : null}"
+ p:auditContextCreationStrategy-ref="AdminAuditContextLookup" />
+
<!-- Default functions to produce messages for the management view. -->
<bean id="DefaultAdminInfoMessageFunction" class="net.shibboleth.idp.plugin.authn.webauthn.context.navigate.RegistrationInfoMessageLookupFunction" lazy-init="true"
@@ -81,6 +85,40 @@
<alias alias="AdminInfoMessageFunction" name="%{idp.authn.webauthn.admin.management.infoMessageFunction:DefaultAdminInfoMessageFunction}" />
<alias alias="AdminErrorMessageFunction" name="%{idp.authn.webauthn.admin.management.errorMessageFunction:DefaultAdminErrorMessageFunction}" />
+ <!-- Audit logging beans -->
+
+ <util:map id="shibboleth.admin.AuditFormattingMap">
+ <entry key="#{'%{idp.authn.webauthn.admin.management.audit.category:Shibboleth-Audit.WebAuthnManagment}'.trim()}"
+ value="#{'%{idp.authn.webauthn.admin.management.audit.format:%a|%T|%u|%WebAuthnAdminAO|%WebAuthnAdminAction|%WebAuthnAdminCR|%WebAuthnAdminAU|%UA}'.trim()}" />
+ </util:map>
+
+ <bean id="PostAdminFunctionPopulateAuditContext" parent="shibboleth.admin.AbstractPopulateAuditContext"
+ p:fieldExtractors="#{getObject('shibboleth.authn.webauthn.admin.management.audit.AuditExtractors') ?: getObject('shibboleth.authn.webauthn.admin.management.DefaultAuditExtractors')}"
+ p:clearAuditContext="true"/>
+
+ <bean id="shibboleth.authn.webauthn.admin.management.DefaultAuditExtractors" parent="shibboleth.admin.DefaultAuditExtractors"
+ lazy-init="true" class="org.springframework.beans.factory.config.MapFactoryBean">
+ <property name="sourceMap">
+ <map merge="true">
+ <entry>
+ <key>
+ <util:constant
+ static-field="net.shibboleth.idp.plugin.authn.webauthn.audit.WebAuthnAuditFields.ADMIN_AFFECTED_USER" />
+ </key>
+ <bean class="net.shibboleth.idp.plugin.authn.webauthn.audit.impl.WebAuthnSearchUserAuditExtractor"
+ c:_0-ref="shibboleth.ChildLookup.WebAuthnManagementContext" />
+ </entry>
+ <entry>
+ <key>
+ <util:constant
+ static-field="net.shibboleth.idp.plugin.authn.webauthn.audit.WebAuthnAuditFields.CRED_REMOVED" />
+ </key>
+ <bean class="net.shibboleth.idp.plugin.authn.webauthn.audit.impl.WebAuthnCredentialToRemoveAuditExtractor"
+ c:_0-ref="shibboleth.ChildLookup.WebAuthnManagementContext" />
+ </entry>
+ </map>
+ </property>
+ </bean>
</beans>
diff --git a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-beans.xml b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-beans.xml
index 3ec5428..17f1127 100644
--- a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-beans.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-beans.xml
@@ -70,16 +70,16 @@
<bean id="AddUserVerificationRequired" parent="AbstractWebAuthnBaseAction"
class="net.shibboleth.idp.plugin.authn.webauthn.impl.AddUserVerificationRequirement" scope="prototype"
- p:webAuthnBaseContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnRegistrationContext"
+ p:webAuthnContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnRegistrationContext"
p:userVerificationRequirement="%{idp.authn.webauthn.registration.userVerification:discouraged}" />
<bean id="LookupRegisteredCredentials" parent="AbstractWebAuthnBaseAction" scope="prototype"
class="net.shibboleth.idp.plugin.authn.webauthn.impl.LookupRegisteredCredentials"
- p:webAuthnBaseContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnRegistrationContext" />
+ p:webAuthnContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnRegistrationContext" />
<bean id="GenerateServerChallenge" parent="AbstractWebAuthnBaseAction" scope="prototype"
class="net.shibboleth.idp.plugin.authn.webauthn.impl.GenerateServerChallenge"
- p:webAuthnBaseContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnRegistrationContext" />
+ p:webAuthnContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnRegistrationContext" />
<bean id="AddUserId" parent="AbstractWebAuthnRegistrationAction" scope="prototype"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.AddUserId"
@@ -119,14 +119,21 @@
</bean>
<bean id="DeletePublicKeyCredential" parent="AbstractWebAuthnRegistrationAction" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.DeletePublicKeyCredential" />
+ class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.DeletePublicKeyCredential"
+ p:populateAuditContextAction="#{%{idp.authn.webauthn.registration.audit.enabled:false} ? getObject('RegistrationOperationPopulateAuditContext') : null}"
+ p:writeAuditLogAction="#{%{idp.authn.webauthn.registration.audit.enabled:false} ? getObject('WriteAdminAuditLog') : null}"
+ p:auditContextCreationStrategy-ref="AdminAuditContextLookup" />
<bean id="ValidateAuthenticatorAttestationResponse" parent="AbstractWebAuthnRegistrationAction"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.ValidateAuthenticatorAttestationResponse" />
<bean id="StorePublicKeyCredential" parent="AbstractWebAuthnRegistrationAction" scope="prototype"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.StorePublicKeyCredential"
- p:credentialRepository-ref="shibboleth.authn.webauthn.DefaultCredentialRepository" />
+ p:credentialRepository-ref="shibboleth.authn.webauthn.DefaultCredentialRepository"
+ p:populateAuditContextAction="#{%{idp.authn.webauthn.registration.audit.enabled:false} ? getObject('RegistrationOperationPopulateAuditContext') : null}"
+ p:writeAuditLogAction="#{%{idp.authn.webauthn.registration.audit.enabled:false} ? getObject('WriteAdminAuditLog') : null}"
+ p:auditContextCreationStrategy-ref="AdminAuditContextLookup" />
+
<!-- Default functions to produce messages for the registration view. -->
<bean id="DefaultRegistrationInfoMessageFunction" class="net.shibboleth.idp.plugin.authn.webauthn.context.navigate.RegistrationInfoMessageLookupFunction" lazy-init="true"
@@ -137,5 +144,40 @@
<alias alias="RegistrationInfoMessageFunction" name="%{idp.authn.webauthn.registration.infoMessageFunction:DefaultRegistrationInfoMessageFunction}" />
<alias alias="RegistrationErrorMessageFunction" name="%{idp.authn.webauthn.registration.errorMessageFunction:DefaultRegistrationErrorMessageFunction}" />
+ <!-- Audit logging beans -->
+
+ <util:map id="shibboleth.admin.AuditFormattingMap">
+ <entry key="#{'%{idp.authn.webauthn.registration.audit.category:Shibboleth-Audit.WebAuthnRegistration}'.trim()}"
+ value="#{'%{idp.authn.webauthn.registration.audit.format:%a|%T|%u|%WebAuthnAdminAO|%WebAuthnAdminAction|%WebAuthnAdminCR|%WebAuthnAdminCA|%WebAuthnAdminAU|%UA}'.trim()}" />
+ </util:map>
+
+
+ <bean id="RegistrationOperationPopulateAuditContext" parent="shibboleth.admin.AbstractPopulateAuditContext"
+ p:fieldExtractors="#{getObject('shibboleth.authn.webauthn.registration.audit.AuditExtractors') ?: getObject('shibboleth.authn.webauthn.registration.DefaultAuditExtractors')}"
+ p:clearAuditContext="true"/>
+
+ <bean id="shibboleth.authn.webauthn.registration.DefaultAuditExtractors" parent="shibboleth.admin.DefaultAuditExtractors"
+ lazy-init="true" class="org.springframework.beans.factory.config.MapFactoryBean">
+ <property name="sourceMap">
+ <map merge="true">
+ <entry>
+ <key>
+ <util:constant
+ static-field="net.shibboleth.idp.plugin.authn.webauthn.audit.WebAuthnAuditFields.CRED_REMOVED" />
+ </key>
+ <bean class="net.shibboleth.idp.plugin.authn.webauthn.audit.impl.WebAuthnUserCredentialToRemoveAuditExtractor"
+ c:_0-ref="shibboleth.ChildLookup.WebAuthnRegistrationContext" />
+ </entry>
+ <entry>
+ <key>
+ <util:constant
+ static-field="net.shibboleth.idp.plugin.authn.webauthn.audit.WebAuthnAuditFields.CRED_ADDED" />
+ </key>
+ <bean class="net.shibboleth.idp.plugin.authn.webauthn.audit.impl.WebAuthnUserCredentialAddedAuditExtractor"
+ c:_0-ref="shibboleth.ChildLookup.WebAuthnRegistrationContext" />
+ </entry>
+ </map>
+ </property>
+ </bean>
</beans>
diff --git a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-abstract-beans.xml b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-abstract-beans.xml
index d500b4b..2980666 100644
--- a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-abstract-beans.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-abstract-beans.xml
@@ -55,8 +55,51 @@
</bean>
</constructor-arg>
</bean>
+
+ <bean id="WriteAdminAuditLog"
+ class="net.shibboleth.idp.profile.audit.impl.WriteAuditLog" scope="prototype" lazy-init="true"
+ p:formattingMap-ref="shibboleth.admin.AuditFormattingMap"
+ p:dateTimeFormat="#{getObject('shibboleth.AuditDateTimeFormat')}"
+ p:useDefaultTimeZone="#{getObject('shibboleth.AuditDefaultTimeZone') ?: false}"
+ p:includeProfileLoggingId="false"
+ p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
+ p:activationCondition="%{idp.authn.audit.enabled:false}"
+ p:auditContextLookupStrategy-ref="AdminAuditContextLookup"/>
+
+ <!-- Private copy of AuditContext for admin flows. TODO this is the default, is there somewhere better to put it?-->
+ <bean id="AdminAuditContextLookup"
+ class="org.opensaml.messaging.context.navigate.ChildContextLookup"
+ c:type="#{ T(net.shibboleth.profile.context.AuditContext) }"
+ c:createContext="true" />
+
+ <bean id="shibboleth.admin.AuditFormattingMapParser" scope="prototype" lazy-init="true"
+ class="net.shibboleth.idp.profile.audit.impl.PopulateAuditContext.FormattingMapParser"
+ c:_0-ref="shibboleth.admin.AuditFormattingMap" />
-
+ <bean id="shibboleth.admin.AbstractPopulateAuditContext" abstract="true"
+ class="net.shibboleth.idp.profile.audit.impl.PopulateAuditContext" scope="prototype"
+ p:auditContextCreationStrategy-ref="AdminAuditContextLookup"
+ p:formattingMapParser-ref="shibboleth.admin.AuditFormattingMapParser"
+ p:dateTimeFormat="#{getObject('shibboleth.AuditDateTimeFormat')}"
+ p:useDefaultTimeZone="#{getObject('shibboleth.AuditDefaultTimeZone') ?: false}"
+ p:fieldReplacements="#{getObject('shibboleth.AuditFieldReplacementMap')}" />
+
+
+ <bean id="shibboleth.admin.DefaultAuditExtractors" lazy-init="true"
+ class="org.springframework.beans.factory.config.MapFactoryBean">
+ <property name="sourceMap">
+ <map>
+ <entry>
+ <key>
+ <util:constant static-field="net.shibboleth.idp.profile.IdPAuditFields.USERNAME"/>
+ </key>
+ <bean parent="shibboleth.Functions.Compose"
+ c:g-ref="shibboleth.PrincipalNameLookup.Subject"
+ c:f-ref="shibboleth.ChildLookup.SubjectContext" />
+ </entry>
+ </map>
+ </property>
+ </bean>
</beans>
diff --git a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-beans.xml b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-beans.xml
index 3ffcd13..8e26f81 100644
--- a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-beans.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/WebAuthn/webauthn-beans.xml
@@ -87,23 +87,23 @@
<bean id="AddUserVerificationRequired" parent="AbstractWebAuthnBaseAction"
class="net.shibboleth.idp.plugin.authn.webauthn.impl.AddUserVerificationRequirement" scope="prototype"
- p:webAuthnBaseContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnAuthenticationContextFromAuthenticationContext"
+ p:webAuthnContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnAuthenticationContextFromAuthenticationContext"
p:userVerificationRequirement="required" />
<bean id="AddUserVerificationNotRequired" parent="AbstractWebAuthnBaseAction"
class="net.shibboleth.idp.plugin.authn.webauthn.impl.AddUserVerificationRequirement" scope="prototype"
- p:webAuthnBaseContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnAuthenticationContextFromAuthenticationContext"
+ p:webAuthnContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnAuthenticationContextFromAuthenticationContext"
p:userVerificationRequirement="discouraged" />
<bean id="LookupRegisteredCredentials" scope="prototype" parent="AbstractWebAuthnBaseAction"
class="net.shibboleth.idp.plugin.authn.webauthn.impl.LookupRegisteredCredentials"
p:triggerEventOnNoCredentials="%{idp.authn.webauthn.passwordless.signalEventOnNoCredentials:false}"
p:noCredentialsEventId="%{idp.authn.webauthn.passwordless.noCredentialsEventId:NoRegisteredWebAuthnCredentials}"
- p:webAuthnBaseContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnAuthenticationContextFromAuthenticationContext" />
+ p:webAuthnContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnAuthenticationContextFromAuthenticationContext" />
<bean id="GenerateServerChallenge" scope="prototype" parent="AbstractWebAuthnBaseAction"
class="net.shibboleth.idp.plugin.authn.webauthn.impl.GenerateServerChallenge"
- p:webAuthnBaseContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnAuthenticationContextFromAuthenticationContext" />
+ p:webAuthnContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnAuthenticationContextFromAuthenticationContext" />
<bean id="CreatePublicKeyCredentialRequestOptions" scope="prototype" parent="AbstractWebAuthnAuthenticationAction"
class="net.shibboleth.idp.plugin.authn.webauthn.impl.CreatePublicKeyCredentialRequestOptions"/>
@@ -128,6 +128,10 @@
p:writeAuditLogAction="#{%{idp.authn.webauthn.audit.enabled:%{idp.authn.audit.enabled:false}} ? getObject('WriteAuthnAuditLog') : null}"/>
<!-- Audit logging beans. -->
+
+ <bean id="AdminActionPopulateAuditContext" parent="shibboleth.authn.AbstractPopulateAuditContext"
+ p:fieldExtractors="#{getObject('shibboleth.authn.DuoOIDC.AdminActionPopulateAuditExtractors') ?: getObject('shibboleth.authn.DuoOIDC.DefaultAdminActionPopulateAuditExtractors')}"
+ p:clearAuditContext="true"/>
<!-- Default audit format and extractors -->
<util:map id="shibboleth.authn.AuditFormattingMap">
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties
index 55e4e53..106f601 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn.properties
@@ -100,6 +100,10 @@ idp.authn.webauthn.supportedPrincipals = \
#idp.authn.webauthn.registration.infoMessageFunction = DefaultRegistrationInfoMessageFunction
#idp.authn.webauthn.registration.errorMessageFunction = DefaultRegistrationErrorMessageFunction
+#idp.authn.webauthn.registration.audit.enabled = false
+#idp.authn.webauthn.registration.audit.category = Shibboleth-Audit.WebAuthnRegistration
+#idp.authn.webauthn.registration.audit.format = %a|%T|%u|%WebAuthnAdminAO|%WebAuthnAdminAction|%WebAuthnAdminCR|%WebAuthnAdminCA|%WebAuthnAdminAU|%UA
+
#### Administrator properties
@@ -114,6 +118,9 @@ idp.authn.webauthn.supportedPrincipals = \
#idp.authn.webauthn.admin.management.genericMessageID =
#idp.authn.webauthn.admin.management.infoMessageFunction = DefaultAdminInfoMessageFunction
#idp.authn.webauthn.admin.management.errorMessageFunction = DefaultAdminErrorMessageFunction
+#idp.authn.webauthn.admin.management.audit.enabled = false
+#idp.authn.webauthn.admin.management.audit.format = %a|%T|%u|%WebAuthnAdminAO|%WebAuthnAdminAction|%WebAuthnAdminCR|%WebAuthnAdminAU|%UA
+#idp.authn.webauthn.admin.management.audit.category = Shibboleth-Audit.WebAuthnManagment
#### Authentication properties
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm
index c16626b..d42e105 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm
@@ -21,7 +21,7 @@
## Add CSP directives
#set ($areYouSure = "return confirm('#springMessageText('idp.webauthn.register.credential.remove.confirm', 'Are you sure')');")
#set ($nonce = $cspNonce.generateIdentifier())
-##$response.addHeader("Content-Security-Policy", "default-src 'none'; style-src 'self'; img-src 'self'; script-src-elem 'nonce-$nonce'; script-src-attr 'unsafe-hashes' 'sha256-$cspDigester.apply($areYouSure)'")
+$response.addHeader("Content-Security-Policy", "default-src 'none'; style-src 'self'; img-src 'self'; script-src-elem 'nonce-$nonce'; script-src-attr 'unsafe-hashes' 'sha256-$cspDigester.apply($areYouSure)'")
##
<!DOCTYPE html>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list