[java-idp-plugin-webauthn] 01/04: JWEBAUTHN-27 - Add basic authenticator policy
Phil Smart
philip.smart at jisc.ac.uk
Fri Nov 8 16:24:44 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=ced1ec9f10bf136da43d175a4d86ec4e77831a21
commit ced1ec9f10bf136da43d175a4d86ec4e77831a21
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Thu Nov 7 17:32:54 2024 +0000
JWEBAUTHN-27 - Add basic authenticator policy
- Remove authenticator capabilities from credential record
- Remove the Inspectors
- Make the registration and authentication policies dynamic
- Make the credential labelling (for the view) dynamic.
https://shibboleth.atlassian.net/browse/JWEBAUTHN-27
---
.../policy/AuthenticatorCapabilitiesInspector.java | 47 --------
.../authn/webauthn/policy/CredentialPolicy.java | 4 +-
.../authn/webauthn/storage/CredentialRecord.java | 45 --------
.../impl/AdminSearchCredentialsLookupFunction.java | 48 ++++++++
.../impl/AttachAuthenticatorCapabilities.java | 124 ---------------------
.../impl/AuthenticatorCapabilitiesLabeller.java | 89 ---------------
...torPolicy.java => CheckRegistrationPolicy.java} | 10 +-
...stingRegistrationCredentialsLookupStrategy.java | 49 ++++++++
.../admin/impl/LabelAdminCredentialRecords.java | 90 ---------------
.../admin/impl/LabelCredentialRecords.java | 41 +++++--
.../admin/impl/LookupCredentialsForUser.java | 24 +---
.../SecondFactorOnlyCredentialLabeller.java} | 66 +++++++----
.../admin/impl/StorePublicKeyCredential.java | 1 -
...AbstractAuthenticatorCapabilitiesInspector.java | 121 --------------------
...ChainingAuthenticatorCapabilitiesInspector.java | 89 ---------------
.../impl/FunctionalAuthenticatorInspectorRule.java | 69 ------------
.../webauthn/impl/AbstractWebAuthnAction.java | 33 ++++++
.../authn/webauthn/impl/CheckCredentialPolicy.java | 23 +++-
...ingAuthenticationCredentialsLookupStrategy.java | 53 +++++++++
.../webauthn/impl/LookupRegisteredCredentials.java | 21 +---
.../LookupRegisteredCredentialsFromUserHandle.java | 9 +-
.../policy/impl/AbstractCredentialPolicyRule.java | 10 +-
.../policy/impl/ChainingCredentialPolicyRule.java | 12 +-
.../impl/FunctionalCredentialPolicyRule.java | 8 +-
.../impl/SecondFactorOnlyCredentialPolicyRule.java | 73 +++++++++---
.../webauthn-management-beans.xml | 19 ++--
.../webauthn-registration-beans.xml | 46 +++-----
.../webauthn-registration-flow.xml | 3 +-
.../authn/WebAuthn/webauthn-abstract-beans.xml | 9 +-
.../idp/flows/authn/WebAuthn/webauthn-beans.xml | 3 +-
...nfig.xml => webauthn-authentication-config.xml} | 5 +-
.../authn/webauthn/conf/authn/webauthn-config.xml | 19 +++-
.../conf/authn/webauthn-management-config.xml | 25 -----
.../conf/authn/webauthn-registration-config.xml | 36 +-----
.../conf/authn/webauthn-registration.properties | 11 +-
.../authn/webauthn/conf/authn/webauthn.properties | 2 +
.../idp/plugin/authn/webauthn/module.properties | 46 ++++----
.../admin/impl/CheckAuthenticatorPolicyTest.java | 6 +-
.../webauthn/flow/AbstractWebAuthnFlowTest.java | 22 +---
.../webauthn/flow/TestAdminManagementFlow.java | 3 +-
.../authn/webauthn/flow/TestPasswordlessFlow.java | 3 +-
.../authn/webauthn/flow/TestRegistrationFlow.java | 3 +-
.../authn/webauthn/flow/TestSecondFactorFlow.java | 3 +-
.../authn/webauthn/flow/TestUsernameslessFlow.java | 3 +-
.../flow/TestUsernameslessFlowWithPolicy.java | 9 +-
...essWithPolicyApplicationContextInitializer.java | 4 +-
.../authn/webauthn/impl/AbstractWebAuthnTest.java | 14 ---
.../impl/ChainingCredentialPolicyRuleTest.java | 24 ++--
.../SecondFactorOnlyCredentialPolicyRuleTest.java | 67 -----------
.../webauthn/storage/impl/MockAuthenticator.java | 5 +-
.../authn/webauthn/test-beans-authentication.xml | 14 +--
.../authn/webauthn/test-beans-registration.xml | 30 -----
...est-beans-authentication.xml => test-beans.xml} | 24 ++--
53 files changed, 508 insertions(+), 1109 deletions(-)
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/AuthenticatorCapabilitiesInspector.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/AuthenticatorCapabilitiesInspector.java
deleted file mode 100644
index a736bd5..0000000
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/AuthenticatorCapabilitiesInspector.java
+++ /dev/null
@@ -1,47 +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.admin.policy;
-
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-
-import com.yubico.fido.metadata.AAGUID;
-
-import net.shibboleth.shared.annotation.constraint.NotLive;
-import net.shibboleth.shared.annotation.constraint.Unmodifiable;
-import net.shibboleth.shared.component.IdentifiedComponent;
-
-/**
- * An API for inspecting the capabilities of an authenticator and describing that in a Map. The authenticator is
- * identified by its AAGUID.
- */
-public interface AuthenticatorCapabilitiesInspector extends IdentifiedComponent {
-
- /**
- * Inspect the capabilities of this authenticator and return any capabilities/properties/labels/tags appropriate.
- *
- * @param aaguid the authenticator attestation GUID.
- * @param prc the profile request context
- *
- * @return a map of capabilities/properties/labels/tags relating to the authenticator, could be empty.
- */
- @Nonnull @NotLive @Unmodifiable
- Map<String, String> inspect(@Nullable final AAGUID aaguid, @Nonnull final ProfileRequestContext prc);
-
-}
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/CredentialPolicy.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/CredentialPolicy.java
index 3c37cf4..374ed7a 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/CredentialPolicy.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/CredentialPolicy.java
@@ -18,7 +18,7 @@ import javax.annotation.Nonnull;
import org.opensaml.profile.context.ProfileRequestContext;
-import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRecord;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.EnhancedCredentialRecord;
import net.shibboleth.shared.component.IdentifiedComponent;
/**
@@ -57,7 +57,7 @@ public interface CredentialPolicy extends IdentifiedComponent {
* @return {@link CredentialPolicyOutcome#ACCEPT} if allowed, {@link CredentialPolicyOutcome#REJECT} if rejected,
* and {@link CredentialPolicyOutcome#IGNORE} otherwise.
*/
- CredentialPolicyOutcome evaluate(@Nonnull final CredentialRecord credential,
+ CredentialPolicyOutcome evaluate(@Nonnull final EnhancedCredentialRecord credential,
@Nonnull final ProfileRequestContext prc);
}
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/CredentialRecord.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/CredentialRecord.java
index a84896a..0f61bc5 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/CredentialRecord.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/CredentialRecord.java
@@ -16,7 +16,6 @@ package net.shibboleth.idp.plugin.authn.webauthn.storage;
import java.time.Instant;
import java.util.Collections;
-import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.SortedSet;
@@ -37,9 +36,7 @@ import com.yubico.webauthn.data.PublicKeyCredentialDescriptor;
import com.yubico.webauthn.data.UserIdentity;
import net.shibboleth.shared.annotation.constraint.NonnullElements;
-import net.shibboleth.shared.annotation.constraint.NotLive;
import net.shibboleth.shared.annotation.constraint.Unmodifiable;
-import net.shibboleth.shared.collection.CollectionSupport;
import net.shibboleth.shared.logic.Constraint;
/**
@@ -82,12 +79,6 @@ public final class CredentialRecord {
/** Was the user verified during registration. */
private final boolean userVerified;
- /**
- * Capabilities of the authenticator as determined by the registration process and any configured authenticator
- * inspectors (not necessarily just from FIDO metadata).
- */
- @Nonnull private final Map<String,String> authenticatorCapabilities;
-
/**
*
* Builder constructor.
@@ -104,8 +95,6 @@ public final class CredentialRecord {
this.discoverable = builder.discoverable;
this.userVerified = builder.userVerified;
this.aaguid = builder.aaguid;
- this.authenticatorCapabilities = builder.authenticatorCapabilities;
-
}
/**
@@ -234,16 +223,6 @@ public final class CredentialRecord {
return credential.getCredentialId().getHex();
}
- /**
- * Get the capabilities of the authenticator that created this credential.
- *
- * @return the authenticatorCapabilities.
- */
- @JsonGetter("authenticatorCapabilities")
- @Nonnull @NotLive @Unmodifiable public Map<String, String> getAuthenticatorCapabilities() {
- return CollectionSupport.copyToMap(authenticatorCapabilities);
- }
-
/**
* Convert the credential registration into a {@link PublicKeyCredentialDescriptor}.
*
@@ -401,15 +380,6 @@ public final class CredentialRecord {
* @return the next builder stage
*/
@Nonnull public IBuildStage withAaguid(byte[] aaguid);
-
- /**
- * Set the capabilities of the authenticator as determined by the registration process
- * (not FIDO necessarily FIDO metadata).
- *
- * @param capabilities the capabilities of the authenticator
- * @return the next builder stage
- */
- @Nonnull public IBuildStage withAuthenticatorCapabilities(@Nullable final Map<String,String> capabilities);
/**
* Build this credential registration.
@@ -441,11 +411,6 @@ public final class CredentialRecord {
private boolean userVerified;
/** The AAGUID of the authenticator.*/
@Nullable private byte[] aaguid;
- /**
- * Capabilities of the authenticator as determined by the registration process
- * (not FIDO neccisarily FIDO metadata).
- */
- @Nonnull private Map<String,String> authenticatorCapabilities;
/** Constructor.*/
@SuppressWarnings("null")
@@ -454,7 +419,6 @@ public final class CredentialRecord {
discoverable = Optional.empty();
userVerified = false;
transports = Collections.emptySortedSet();
- authenticatorCapabilities = Collections.emptyMap();
}
@Override
@@ -529,15 +493,6 @@ public final class CredentialRecord {
aaguid = authenticatorGuid;
return this;
}
-
- @Override
- @JsonProperty("authenticatorCapabilities")
- public IBuildStage withAuthenticatorCapabilities(@Nullable final Map<String, String> capabilities) {
- if (capabilities != null) {
- authenticatorCapabilities = CollectionSupport.copyToMap(capabilities);
- }
- return this;
- }
}
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AdminSearchCredentialsLookupFunction.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AdminSearchCredentialsLookupFunction.java
new file mode 100644
index 0000000..023074f
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AdminSearchCredentialsLookupFunction.java
@@ -0,0 +1,48 @@
+/*
+ * 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.admin.impl;
+
+import java.util.Collection;
+import java.util.function.Function;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnManagementContext;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.EnhancedCredentialRecord;
+import net.shibboleth.shared.collection.CollectionSupport;
+
+/**
+ * A credential lookup function that returns the credentials belonging to the user that has been searched for in the
+ * management interface.
+ */
+public class AdminSearchCredentialsLookupFunction
+ implements Function<ProfileRequestContext, Collection<EnhancedCredentialRecord>>{
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<EnhancedCredentialRecord> apply(@Nullable final ProfileRequestContext input) {
+ if (input == null) {
+ return CollectionSupport.emptyList();
+ }
+ final WebAuthnManagementContext manCtx = input.getSubcontext(WebAuthnManagementContext.class);
+ if (manCtx == null) {
+ return CollectionSupport.emptyList();
+ }
+ return manCtx.getFoundCredentials();
+ }
+
+}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AttachAuthenticatorCapabilities.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AttachAuthenticatorCapabilities.java
deleted file mode 100644
index 89da113..0000000
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AttachAuthenticatorCapabilities.java
+++ /dev/null
@@ -1,124 +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.admin.impl;
-
-import java.util.Map;
-import java.util.Optional;
-
-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.context.ProfileRequestContext;
-import org.slf4j.Logger;
-
-import com.yubico.fido.metadata.AAGUID;
-import com.yubico.webauthn.data.AttestedCredentialData;
-import com.yubico.webauthn.data.AuthenticatorAttestationResponse;
-import com.yubico.webauthn.data.ByteArray;
-import com.yubico.webauthn.data.ClientRegistrationExtensionOutputs;
-import com.yubico.webauthn.data.PublicKeyCredential;
-
-import net.shibboleth.idp.plugin.authn.webauthn.admin.WebAuthnRegistrationEventIds;
-import net.shibboleth.idp.plugin.authn.webauthn.admin.policy.AuthenticatorCapabilitiesInspector;
-import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
-import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnAction;
-import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.primitive.LoggerFactory;
-
-/**
- * Attach capabilities to the authenticator that has created the credential registration. For example, this
- * authenticator should only be used for 2FA.
- */
-public class AttachAuthenticatorCapabilities extends AbstractWebAuthnAction<WebAuthnRegistrationContext> {
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(AttachAuthenticatorCapabilities.class);
-
- /** The stashed attestation response.*/
- @NonnullBeforeExec
- private PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> attestation;
-
- /** The inspector that checks the capabilities of the authenticator.*/
- @Nullable private AuthenticatorCapabilitiesInspector authenticatorInspector;
-
- /**
- * Constructor.
- */
- protected AttachAuthenticatorCapabilities() {
- super(new ChildContextLookup<>(WebAuthnRegistrationContext.class));
- }
-
- /**
- * Set the inspector that checks the capabilities of the authenticator.
- *
- * @param inspector the authenticator inspector to set.
- */
- public void setAuthenticatorInspector(@Nullable final AuthenticatorCapabilitiesInspector inspector) {
- checkSetterPreconditions();
- authenticatorInspector = inspector;
- }
-
- /** {@inheritDoc} */
- @Override
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final WebAuthnRegistrationContext context) {
-
- if (!super.doPreExecute(profileRequestContext, context)) {
- return false;
- }
-
- attestation = context.getPublicKeyCredentialAttestationResponse();
- if (attestation == null) {
- log.error("{} Attestaion not available in registration context", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, WebAuthnRegistrationEventIds.INVALID_REGISTRATION_CTX);
- return false;
- }
-
- return true;
- }
-
- @Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final WebAuthnRegistrationContext context) {
-
- log.trace("{} Attaching authenticator capabilities", getLogPrefix());
-
- final AuthenticatorCapabilitiesInspector localInspector = authenticatorInspector;
- if (localInspector == null) {
- // If no inspector, there is nothing to do
- log.trace("{} No authenticator inspector to apply", getLogPrefix());
- return;
- }
-
- final Optional<AttestedCredentialData> attestedCredData =
- attestation.getResponse().getParsedAuthenticatorData().getAttestedCredentialData();
-
- if (attestedCredData.isEmpty()) {
- log.warn("{} Attested Credential Data can not assess authenticator capabilities", getLogPrefix());
- return;
-
- }
-
- final ByteArray aaguid = attestedCredData.get().getAaguid();
- final AAGUID authenticatorAttestationGUID = new AAGUID(aaguid);
- final Map<String, String> capabilities =
- localInspector.inspect(authenticatorAttestationGUID, profileRequestContext);
- context.setAuthenticatorCapabilities(capabilities);
-
-
- }
-
-}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AuthenticatorCapabilitiesLabeller.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AuthenticatorCapabilitiesLabeller.java
deleted file mode 100644
index f1667e3..0000000
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/AuthenticatorCapabilitiesLabeller.java
+++ /dev/null
@@ -1,89 +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.admin.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.function.BiFunction;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.ThreadSafe;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-
-import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRecord;
-import net.shibboleth.idp.plugin.authn.webauthn.storage.EnhancedCredentialRecord;
-import net.shibboleth.shared.annotation.ParameterName;
-import net.shibboleth.shared.annotation.constraint.NonnullElements;
-import net.shibboleth.shared.annotation.constraint.NotLive;
-import net.shibboleth.shared.annotation.constraint.Unmodifiable;
-import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.collection.Pair;
-
-/**
- * An authenticator capabilities labeller function that maps authenticator capabilities to labels for display.
- */
- at ThreadSafe
-public class AuthenticatorCapabilitiesLabeller
- implements BiFunction<EnhancedCredentialRecord, ProfileRequestContext, List<String>> {
-
- /** Map authenticator capabilities to labels to display in the registration pages.*/
- @Nonnull @NonnullElements @Unmodifiable @NotLive private Map<Pair<String,String>,List<String>> capabilityToLabelMap;
-
- /**
- *
- * Constructor.
- *
- * @param map the authenticator to capabilities map. The key is a {@link Pair} where the first entry is the
- * capability and the second entry is the value of that capability to match on. The Value of the map is the label
- * to set.
- */
- public AuthenticatorCapabilitiesLabeller(
- @Nullable @ParameterName(name="capabilityToLabelMap") final Map<Pair<String,String>,List<String>> map) {
- if (map != null) {
- capabilityToLabelMap = CollectionSupport.copyToMap(map);
- } else {
- capabilityToLabelMap = CollectionSupport.emptyMap();
- }
-
- }
-
- /** {@inheritDoc} */
- @Override
- public List<String> apply(@Nullable final EnhancedCredentialRecord cred,
- @Nullable final ProfileRequestContext prc) {
-
- if (cred == null) {
- return CollectionSupport.emptyList();
- }
- final List<String> labels = new ArrayList<>();
- final CredentialRecord credential = cred.getCredentialRecord();
-
- final Map<String, String> capabilities = credential.getAuthenticatorCapabilities();
- for (final Entry<Pair<String,String>, List<String>> entry : capabilityToLabelMap.entrySet()) {
- if (capabilities.containsKey(entry.getKey().getFirst())) {
- final String value = capabilities.get(entry.getKey().getFirst());
- if (value != null && value.equals(entry.getKey().getSecond())) {
- labels.addAll(entry.getValue());
- }
-
- }
- }
- return labels;
- }
-}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CheckAuthenticatorPolicy.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CheckRegistrationPolicy.java
similarity index 94%
rename from webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CheckAuthenticatorPolicy.java
rename to webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CheckRegistrationPolicy.java
index b893eec..d45b721 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CheckAuthenticatorPolicy.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CheckRegistrationPolicy.java
@@ -41,17 +41,17 @@ import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
import net.shibboleth.shared.primitive.LoggerFactory;
/**
- * A policy engine action that checks with the configured policy if the authenticator can be used to register
- * credentials with the IdP.
+ * A registration policy engine action that checks with the configured policy if the authenticator can be used to
+ * register credentials with the IdP.
*
* @event {WebAuthnRegistrationEventIds#INVALID_REGISTRATION_CTX}
* @pre <pre>ProfileRequestContext.getSubcontext(WebAuthnRegistrationContext.class) != null</pre>
* @post the authenticator is allowed to register a credential, or an error event is triggered
*/
-public class CheckAuthenticatorPolicy extends AbstractWebAuthnAction<WebAuthnRegistrationContext> {
+public class CheckRegistrationPolicy extends AbstractWebAuthnAction<WebAuthnRegistrationContext> {
/** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(CheckAuthenticatorPolicy.class);
+ @Nonnull private final Logger log = LoggerFactory.getLogger(CheckRegistrationPolicy.class);
/** The stashed attestation response.*/
@NonnullBeforeExec
@@ -63,7 +63,7 @@ public class CheckAuthenticatorPolicy extends AbstractWebAuthnAction<WebAuthnReg
/**
* Constructor.
*/
- protected CheckAuthenticatorPolicy() {
+ protected CheckRegistrationPolicy() {
super(new ChildContextLookup<>(WebAuthnRegistrationContext.class));
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ExistingRegistrationCredentialsLookupStrategy.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ExistingRegistrationCredentialsLookupStrategy.java
new file mode 100644
index 0000000..789183f
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/ExistingRegistrationCredentialsLookupStrategy.java
@@ -0,0 +1,49 @@
+/*
+ * 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.admin.impl;
+
+import java.util.Collection;
+import java.util.function.Function;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnRegistrationContext;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.EnhancedCredentialRecord;
+import net.shibboleth.shared.collection.CollectionSupport;
+
+/**
+ * A credential lookup function that returns the list of existing credentials from the
+ * {@link WebAuthnRegistrationContext}.
+ */
+public class ExistingRegistrationCredentialsLookupStrategy
+ implements Function<ProfileRequestContext, Collection<EnhancedCredentialRecord>>{
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<EnhancedCredentialRecord> apply(@Nullable final ProfileRequestContext input) {
+ if (input == null) {
+ return CollectionSupport.emptyList();
+ }
+ final WebAuthnRegistrationContext webAuthnCtx =
+ input.getSubcontext(WebAuthnRegistrationContext.class);
+ if (webAuthnCtx != null) {
+ return webAuthnCtx.getExistingCredentials();
+ }
+ return CollectionSupport.emptyList();
+ }
+
+}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/LabelAdminCredentialRecords.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/LabelAdminCredentialRecords.java
deleted file mode 100644
index ca2c315..0000000
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/LabelAdminCredentialRecords.java
+++ /dev/null
@@ -1,90 +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.admin.impl;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.function.BiFunction;
-
-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.context.AuthenticationContext;
-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.EnhancedCredentialRecord;
-import net.shibboleth.shared.annotation.constraint.NotLive;
-import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
-
-
-/**
- * Use the labelling function to add labels to each {@link EnhancedCredentialRecord} found in the context. The labels
- * can then be displayed in the registration view.
- *
- * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
- * @post Add labels to each enhanced credential record in the context
- */
-// TODO another admin function which is almost identical to the registration variant
-public class LabelAdminCredentialRecords extends AbstractWebAuthnAction<WebAuthnManagementContext> {
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(LabelAdminCredentialRecords.class);
-
- /** The labeller used to generate labels for the credential, for display only.*/
- @Nonnull @NotLive private BiFunction<EnhancedCredentialRecord, ProfileRequestContext, List<String>> labeller;
-
- /**
- * Constructor.
- *
- * @param defaultStrategy
- */
- protected LabelAdminCredentialRecords() {
- super(new ChildContextLookup<>(WebAuthnManagementContext.class).
- compose(new ChildContextLookup<>(AuthenticationContext.class)));
- labeller = (cred, prc) -> CollectionSupport.emptyList();
- }
-
- /**
- * Set the labeller used to generate labels for the credential, for display only.
- *
- * @param labeller The labeller to set.
- */
- public void setLabeller(@Nonnull
- final BiFunction<EnhancedCredentialRecord, ProfileRequestContext, List<String>> labellerIn) {
- checkSetterPreconditions();
- labeller = Constraint.isNotNull(labellerIn, "labeller can not be null");
- }
-
- @Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final WebAuthnManagementContext context) {
-
- final Collection<EnhancedCredentialRecord> credentials = context.getFoundCredentials();
-
- log.trace("{} Labelling '{}' credentials",getLogPrefix(), credentials.size());
- for (final EnhancedCredentialRecord credential : credentials) {
- final List<String> labels = labeller.apply(credential, profileRequestContext);
- if (labels != null) {
- log.trace("{} Added labels '{}' for credential '{}'",
- getLogPrefix(), labels, credential.getCredentialRecord().getCredentialIdBase64Url());
- credential.setLabels(labels);
- }
- }
- }
-}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/LabelCredentialRecords.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/LabelCredentialRecords.java
index 14f71a5..5aa8c9b 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/LabelCredentialRecords.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/LabelCredentialRecords.java
@@ -17,19 +17,20 @@ package net.shibboleth.idp.plugin.authn.webauthn.admin.impl;
import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;
+import java.util.function.Function;
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.AbstractAuthenticationAction;
import net.shibboleth.idp.authn.context.AuthenticationContext;
-import net.shibboleth.idp.plugin.authn.webauthn.context.BaseWebAuthnContext;
-import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnAction;
import net.shibboleth.idp.plugin.authn.webauthn.storage.EnhancedCredentialRecord;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.annotation.constraint.NotLive;
import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.LoggerFactory;
@@ -41,7 +42,7 @@ import net.shibboleth.shared.primitive.LoggerFactory;
* @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
* @post Add labels to each enhanced credential record in the context
*/
-public class LabelCredentialRecords extends AbstractWebAuthnAction<BaseWebAuthnContext> {
+public class LabelCredentialRecords extends AbstractAuthenticationAction {
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(LabelCredentialRecords.class);
@@ -49,17 +50,39 @@ public class LabelCredentialRecords extends AbstractWebAuthnAction<BaseWebAuthnC
/** The labeller used to generate labels for the credential, for display only.*/
@Nonnull @NotLive private BiFunction<EnhancedCredentialRecord, ProfileRequestContext, List<String>> labeller;
+ @NonnullAfterInit
+ private Function<ProfileRequestContext, Collection<EnhancedCredentialRecord>> credentialsLookupStrategy;
+
/**
* Constructor.
*
* @param defaultStrategy
*/
protected LabelCredentialRecords() {
- super(new ChildContextLookup<>(BaseWebAuthnContext.class).
- compose(new ChildContextLookup<>(AuthenticationContext.class)));
labeller = (cred, prc) -> CollectionSupport.emptyList();
}
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+ if (credentialsLookupStrategy == null) {
+ throw new ComponentInitializationException("credentialsLookupStrategy can not be null");
+ }
+ }
+
+ /**
+ * Set the lookup strategy to find credentials to attach capabilities to.
+ *
+ * @param strategy The credentials lookup strategy to set.
+ */
+ public void setCredentialsLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext, Collection<EnhancedCredentialRecord>> strategy) {
+ checkSetterPreconditions();
+ credentialsLookupStrategy = Constraint.isNotNull(strategy,
+ "credentialsLookupStrategy can not be null");
+ }
+
/**
* Set the labeller used to generate labels for the credential, for display only.
*
@@ -72,11 +95,11 @@ public class LabelCredentialRecords extends AbstractWebAuthnAction<BaseWebAuthnC
}
@Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final BaseWebAuthnContext context) {
+ @Nonnull final AuthenticationContext context) {
- final Collection<EnhancedCredentialRecord> credentials = context.getExistingCredentials();
+ final Collection<EnhancedCredentialRecord> credentials = credentialsLookupStrategy.apply(profileRequestContext);
- log.trace("{} Labelling '{}' credentials",getLogPrefix(), credentials.size());
+ log.trace("{} Labelling '{}' credentials, with labeller '{}'",getLogPrefix(), credentials.size(), labeller);
for (final EnhancedCredentialRecord credential : credentials) {
final List<String> labels = labeller.apply(credential, profileRequestContext);
if (labels != null) {
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 58a3b03..e4bbfd0 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
@@ -15,8 +15,6 @@
package net.shibboleth.idp.plugin.authn.webauthn.admin.impl;
import java.util.Collection;
-import java.util.HashSet;
-import java.util.Objects;
import javax.annotation.Nonnull;
@@ -24,14 +22,10 @@ import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
-import com.yubico.fido.metadata.AAGUID;
-import com.yubico.webauthn.data.ByteArray;
-
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.CredentialRecord;
-import net.shibboleth.idp.plugin.authn.webauthn.storage.EnhancedCredentialRecord;
import net.shibboleth.idp.plugin.authn.webauthn.storage.WebAuthnCredentialRepository;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.collection.CollectionSupport;
@@ -84,23 +78,9 @@ public class LookupCredentialsForUser extends AbstractWebAuthnAction<WebAuthnMan
assert userToSearchFor != null;
final Collection<CredentialRecord> credentials =
repository.getRegistrationsByUsername(userToSearchFor);
- log.debug("{} Found '{}' credentials", getLogPrefix(), credentials.size());
-
- final Collection<EnhancedCredentialRecord> enhancedCredentialRegistrations =
- new HashSet<>(credentials.size());
-
- credentials.stream().filter(Objects::nonNull).forEach(cred -> {
- assert cred != null;
- final EnhancedCredentialRecord enhancedRecord = new EnhancedCredentialRecord(cred);
- final byte[] aaguid = cred.getAaguid();
- if (aaguid != null && aaguid.length == 16) {
- enhancedRecord.setAuthenticatorMetadata(getAuthenticatorMetadata(new ByteArray(aaguid)));
- enhancedRecord.setAaguidMetadata(getAaguidMetadata(new AAGUID(new ByteArray(aaguid))));
- }
- enhancedCredentialRegistrations.add(enhancedRecord);
- });
+ log.debug("{} Found '{}' credentials", getLogPrefix(), credentials.size());
- context.setFoundCredentials(enhancedCredentialRegistrations);
+ context.setFoundCredentials(enhancedCredentialRecord(credentials));
}
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/SecondFactorOnlyAuthenticatorInspector.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/SecondFactorOnlyCredentialLabeller.java
similarity index 58%
rename from webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/SecondFactorOnlyAuthenticatorInspector.java
rename to webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/SecondFactorOnlyCredentialLabeller.java
index 7d09a4b..9a98f89 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/SecondFactorOnlyAuthenticatorInspector.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/SecondFactorOnlyCredentialLabeller.java
@@ -12,46 +12,61 @@
* limitations under the License.
*/
-package net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl;
+package net.shibboleth.idp.plugin.authn.webauthn.admin.impl;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.function.BiFunction;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import com.yubico.fido.metadata.AAGUID;
+import com.yubico.webauthn.data.ByteArray;
import net.shibboleth.idp.plugin.authn.webauthn.authn.AuthenticatorSupport;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.EnhancedCredentialRecord;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.annotation.constraint.NotLive;
import net.shibboleth.shared.annotation.constraint.Unmodifiable;
import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.LoggerFactory;
import net.shibboleth.shared.primitive.StringSupport;
/**
- * Tag credentials created by authenticators that should only be used for second-factor authentication.
+ * A credential labeller function that labels credentials as 'SecondFactor' if the authenticator that created the
+ * credential should only be used as a second factor, and not sole factor of authentication.
*/
-public class SecondFactorOnlyAuthenticatorInspector extends AbstractAuthenticatorCapabilitiesInspector {
-
- /** The name of the capability.*/
- @Nonnull @NotEmpty public static final String CAPABILITY_NAME = "2faOnly";
+ at ThreadSafe
+public class SecondFactorOnlyCredentialLabeller
+ implements BiFunction<EnhancedCredentialRecord, ProfileRequestContext, List<String>> {
/** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(SecondFactorOnlyAuthenticatorInspector.class);
+ @Nonnull private final Logger log = LoggerFactory.getLogger(SecondFactorOnlyCredentialLabeller.class);
+
+ /** The default label to use for second factor only credentials.*/
+ @Nonnull @NotEmpty private static final String DEFAULT_LABEL = "SecondFactorOnly";
/** A set of authenticators that should only be used for second factor authentication, based on their AAGUID.*/
@Nonnull @NotLive @Unmodifiable private Set<AAGUID> secondFactorOnlyAuthenticators;
- /** Constructor.*/
- public SecondFactorOnlyAuthenticatorInspector() {
+ /** The label to use for second factor only credentials.*/
+ @Nonnull @NotEmpty private String label;
+
+ /**
+ *
+ * Constructor.
+ **/
+ public SecondFactorOnlyCredentialLabeller() {
secondFactorOnlyAuthenticators = CollectionSupport.emptySet();
+ label = DEFAULT_LABEL;
}
/**
@@ -60,7 +75,6 @@ public class SecondFactorOnlyAuthenticatorInspector extends AbstractAuthenticato
* @param allowed The second factor authenticators to set.
*/
public void setSecondFactorOnlyAuthenticators(final Set<String> allowed) {
- checkSetterPreconditions();
if (allowed != null) {
secondFactorOnlyAuthenticators = allowed.stream().filter(strAAGUID ->
StringSupport.trimOrNull(strAAGUID)!=null)
@@ -75,15 +89,29 @@ public class SecondFactorOnlyAuthenticatorInspector extends AbstractAuthenticato
}).filter(Objects::nonNull).collect(CollectionSupport.nonnullCollector(Collectors.toSet())).get();
}
}
+
+ /**
+ * Set the label to use for second factor only credentials.
+ *
+ * @param label The label to set.
+ */
+ public void setLabel(@Nonnull final String labelIn) {
+ label = Constraint.isNotNull(labelIn, "label can not be null");
+ }
/** {@inheritDoc} */
@Override
- public Map<String, String> doInspect(final AAGUID aaguid, final ProfileRequestContext prc) {
- final boolean secondFactorOnly = secondFactorOnlyAuthenticators.contains(aaguid);
- final HashMap<String,String> capabilities = new HashMap<>();
- capabilities.put(CAPABILITY_NAME, secondFactorOnly ? "true" : "false");
- return CollectionSupport.copyToMap(capabilities);
+ public List<String> apply(@Nullable final EnhancedCredentialRecord cred,
+ @Nullable final ProfileRequestContext prc) {
+
+ if (cred == null) {
+ return CollectionSupport.emptyList();
+ }
+ final AAGUID aaguidToCheck = new AAGUID(new ByteArray(cred.getCredentialRecord().getAaguid()));
+ final boolean secondFactorOnly = secondFactorOnlyAuthenticators.contains(aaguidToCheck);
+ if (secondFactorOnly) {
+ return CollectionSupport.listOf(label);
+ }
+ return CollectionSupport.emptyList();
}
-
-
}
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 88ef6ed..79bf7ae 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
@@ -142,7 +142,6 @@ public class StorePublicKeyCredential extends AbstractWebAuthnAuditingAction<Web
.withCredentialNickname(context.getCredentialNickname())
.withDiscoverable(isDiscoverable)
.withUserVerified(registrationResult.isUserVerified())
- .withAuthenticatorCapabilities(context.getAuthenticatorCapabilities())
.build();
final boolean added = repository.addRegistrationByUsername(username, registration);
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AbstractAuthenticatorCapabilitiesInspector.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AbstractAuthenticatorCapabilitiesInspector.java
deleted file mode 100644
index 830df91..0000000
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/AbstractAuthenticatorCapabilitiesInspector.java
+++ /dev/null
@@ -1,121 +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.admin.policy.impl;
-
-import java.util.Map;
-import java.util.function.BiPredicate;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-
-import com.yubico.fido.metadata.AAGUID;
-import com.yubico.fido.metadata.FidoMetadataService;
-
-import net.shibboleth.idp.plugin.authn.webauthn.admin.policy.AuthenticatorCapabilitiesInspector;
-import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
-
-/**
- * A base class for {@link AuthenticatorCapabilitiesInspector authenticator inspectors}. Can be enabled and disabled
- * by the activiation condition.
- *
- * <p>Returns a map of authenticator capabilities or properties.</p>
- */
-public abstract class AbstractAuthenticatorCapabilitiesInspector extends AbstractIdentifiableInitializableComponent
- implements AuthenticatorCapabilitiesInspector {
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractAuthenticatorCapabilitiesInspector.class);
-
- /** FIDO metadata service resolver.*/
- @Nullable private FidoMetadataService fidoMetadataService;
-
- /** Does this inspector apply? Default is true. */
- @Nonnull private BiPredicate<AAGUID, ProfileRequestContext> activationCondition;
-
- /** Constructor.*/
- protected AbstractAuthenticatorCapabilitiesInspector() {
- //default is always true
- activationCondition = (prc,claims) -> true;
- }
-
- /**
- * Set an activation condition for this inspector.
- *
- * @param condition condition to set
- */
- public void setActivationConditionStrategy(@Nonnull final BiPredicate<AAGUID, ProfileRequestContext> condition) {
- checkSetterPreconditions();
- activationCondition = Constraint.isNotNull(condition, "Activation condition cannot be null");
- }
-
- /**
- * Set an activation condition for this inspector.
- *
- * @param flag the flag to set
- */
- public void setActivationCondition(final boolean flag) {
- checkSetterPreconditions();
- activationCondition = flag ? (prc,claims) -> true : (prc,claims) -> false;
- }
-
- /**
- * Set the FIDO Metadata service.
- *
- * @param trustSource the FIDO Metadata service.
- */
- public void setFidoMetadataService(@Nullable final FidoMetadataService trustSource) {
- checkSetterPreconditions();
- fidoMetadataService = trustSource;
- }
-
- /**
- * Get the metadata service to use.
- *
- * @return the metadata service.
- */
- @Nullable protected FidoMetadataService getFidoMetadataService() {
- return fidoMetadataService;
- }
-
- /** {@inheritDoc} */
- @Override
- public Map<String, String> inspect(@Nullable final AAGUID aaguid, @Nonnull final ProfileRequestContext prc) {
- if (!activationCondition.test(aaguid, prc)) {
- //not active for this request
- log.trace("AuthenticatorCapabilitiesInspector '{}' not active for this request", getId());
- return CollectionSupport.emptyMap();
- }
- return doInspect(aaguid, prc);
- }
-
- /**
- * Execute the inspector. Implementations should override this method.
- *
- * @param aaguid the authenticator attestation GUID.
- * @param prc the profile request context
- *
- * @return a map of capabilities.
- */
- @Nonnull
- protected abstract Map<String, String> doInspect(@Nullable AAGUID aaguid, @Nonnull ProfileRequestContext prc);
-
-
-}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/ChainingAuthenticatorCapabilitiesInspector.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/ChainingAuthenticatorCapabilitiesInspector.java
deleted file mode 100644
index 9e638e2..0000000
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/ChainingAuthenticatorCapabilitiesInspector.java
+++ /dev/null
@@ -1,89 +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.admin.policy.impl;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-
-import com.yubico.fido.metadata.AAGUID;
-
-import net.shibboleth.idp.plugin.authn.webauthn.admin.policy.AuthenticatorCapabilitiesInspector;
-import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.primitive.LoggerFactory;
-
-/**
- * A {@link AbstractAuthenticatorCapabilitiesInspector} implementation that is based on a chain of
- * configured inspectors. The outcomes from each are aggregated together (the last entry for a given key will overwrite
- * the previous entry).
- */
-public class ChainingAuthenticatorCapabilitiesInspector extends AbstractAuthenticatorCapabilitiesInspector {
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(ChainingAuthenticatorCapabilitiesInspector.class);
-
- /** An ordered chain of authenticator inspectors.*/
- private List<AuthenticatorCapabilitiesInspector> authenticatorInspectorChain;
-
- /** {@inheritDoc} */
- @Override
- protected void doInitialize() throws ComponentInitializationException {
- super.doInitialize();
-
- if (authenticatorInspectorChain == null) {
- throw new ComponentInitializationException("List of authenticator inspectors can not be null");
- }
- }
-
- /**
- * Set the chain of inspectors that should be used to evaluate the authenticator.
- *
- * @param chain The authenticator inspector chain to set.
- */
- public void setAuthenticatorInspectorChain(@Nullable final List<AuthenticatorCapabilitiesInspector> chain) {
- checkSetterPreconditions();
- if (chain != null) {
- authenticatorInspectorChain = chain;
- } else {
- authenticatorInspectorChain = CollectionSupport.emptyList();
- }
- }
-
- /** {@inheritDoc} */
- @Override
- protected Map<String,String> doInspect(@Nullable final AAGUID aaguid, @Nonnull final ProfileRequestContext prc) {
-
- final Map<String, String> allOutcomes = new HashMap<>();
- for (final AuthenticatorCapabilitiesInspector inspector : authenticatorInspectorChain) {
- if (log.isTraceEnabled()) {
- log.trace("Trying AuthenticatorCapabilitiesInspector '{}' for authenticator '{}'", inspector.getId(),
- aaguid != null ? aaguid.asGuidString() : "unknown");
- }
- final Map<String,String> outcome = inspector.inspect(aaguid, prc);
- log.trace("AuthenticatorCapabilitiesInspector '{}' produced result '{}'", inspector.getId(), outcome);
- allOutcomes.putAll(outcome);
- }
- log.trace("All AuthenticatorCapabilitiesInspector produced result '{}'", allOutcomes);
- return CollectionSupport.copyToMap(allOutcomes);
- }
-
-}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/FunctionalAuthenticatorInspectorRule.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/FunctionalAuthenticatorInspectorRule.java
deleted file mode 100644
index 6609919..0000000
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/policy/impl/FunctionalAuthenticatorInspectorRule.java
+++ /dev/null
@@ -1,69 +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.admin.policy.impl;
-import java.util.Map;
-import java.util.function.BiFunction;
-
-import javax.annotation.Nonnull;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-
-import com.yubico.fido.metadata.AAGUID;
-
-import net.shibboleth.idp.plugin.authn.webauthn.admin.policy.AuthenticatorCapabilitiesInspector;
-import net.shibboleth.idp.plugin.authn.webauthn.policy.impl.FunctionalRuleContext;
-import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
-
-/**
- * A {@link AuthenticatorCapabilitiesInspector} that uses a {@link BiFunction} to inspect the authenticator that
- * created the credential registration.
- */
-public class FunctionalAuthenticatorInspectorRule extends AbstractAuthenticatorCapabilitiesInspector {
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(FunctionalAuthenticatorInspectorRule.class);
-
- /** The function to run. By default this returns an empty map.*/
- @Nonnull private BiFunction<AAGUID, FunctionalRuleContext, Map<String, String>> inspector;
-
- /** Constructor.*/
- public FunctionalAuthenticatorInspectorRule() {
- inspector = (cred, prc) -> CollectionSupport.emptyMap();
- }
-
- /**
- * Set the inspector function rule to run.
- *
- * @param function the inspector to set.
- */
- public void setInspector(@Nonnull
- final BiFunction<AAGUID, FunctionalRuleContext, Map<String, String>> function) {
- checkSetterPreconditions();
- inspector = Constraint.isNotNull(function, "Inspector function can not be null");
- }
-
- /** {@inheritDoc} */
- @Override
- @Nonnull protected Map<String, String> doInspect(final AAGUID aaguid, final ProfileRequestContext prc) {
- log.debug("Running AuthenticatorInspector rule function '{}'", this.getId());
- final Map<String, String> capabilities = inspector.apply(aaguid,
- new FunctionalRuleContext(prc, getFidoMetadataService()));
- return capabilities == null ? CollectionSupport.emptyMap() : capabilities;
- }
-
-}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnAction.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnAction.java
index a976bfc..8e4570e 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnAction.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnAction.java
@@ -14,6 +14,9 @@
package net.shibboleth.idp.plugin.authn.webauthn.impl;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
@@ -34,6 +37,8 @@ import net.shibboleth.idp.authn.AuthnEventIds;
import net.shibboleth.idp.plugin.authn.webauthn.client.WebAuthnAuthenticationClient;
import net.shibboleth.idp.plugin.authn.webauthn.metadata.AaguidEntry;
import net.shibboleth.idp.plugin.authn.webauthn.metadata.impl.PasskeyAaguidMetadataService;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRecord;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.EnhancedCredentialRecord;
import net.shibboleth.idp.plugin.authn.webauthn.storage.WebAuthnCredentialRepository;
import net.shibboleth.idp.profile.AbstractProfileAction;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
@@ -41,6 +46,7 @@ import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
import net.shibboleth.shared.annotation.constraint.NonnullElements;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
import net.shibboleth.shared.collection.CollectionSupport;
import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.logic.Constraint;
@@ -299,6 +305,33 @@ public class AbstractWebAuthnAction<T> extends AbstractProfileAction {
}
return null;
}
+
+ /**
+ * Create {@link EnhancedCredentialRecord EnhancedCredentialRecords} from the given
+ * {@link CredentialRecord CredentialRecords} and add any appropriate metadata to each record.
+ *
+ * @param credentials the credentials to convert to {@link EnhancedCredentialRecord EnhancedCredentialRecords}.
+ *
+ * @return a collection of enhanced credential records
+ */
+ @NotLive @Unmodifiable @Nonnull protected Collection<EnhancedCredentialRecord> enhancedCredentialRecord(
+ final Collection<CredentialRecord> credentials) {
+
+ final Collection<EnhancedCredentialRecord> enhancedCredentialRegistrations =
+ new HashSet<>(credentials.size());
+
+ credentials.stream().filter(Objects::nonNull).forEach(cred -> {
+ assert cred != null;
+ final EnhancedCredentialRecord enhancedRecord = new EnhancedCredentialRecord(cred);
+ final byte[] aaguid = cred.getAaguid();
+ if (aaguid != null && aaguid.length == 16) {
+ enhancedRecord.setAuthenticatorMetadata(getAuthenticatorMetadata(new ByteArray(aaguid)));
+ enhancedRecord.setAaguidMetadata(getAaguidMetadata(new AAGUID(new ByteArray(aaguid))));
+ }
+ enhancedCredentialRegistrations.add(enhancedRecord);
+ });
+ return CollectionSupport.copyToList(enhancedCredentialRegistrations);
+ }
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/CheckCredentialPolicy.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/CheckCredentialPolicy.java
index bd4cc23..fc89925 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/CheckCredentialPolicy.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/CheckCredentialPolicy.java
@@ -14,6 +14,7 @@
package net.shibboleth.idp.plugin.authn.webauthn.impl;
+import java.util.Collection;
import java.util.Optional;
import javax.annotation.Nonnull;
@@ -37,6 +38,7 @@ import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnAuthenticationCo
import net.shibboleth.idp.plugin.authn.webauthn.policy.CredentialPolicy;
import net.shibboleth.idp.plugin.authn.webauthn.policy.CredentialPolicy.CredentialPolicyOutcome;
import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRecord;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.EnhancedCredentialRecord;
import net.shibboleth.idp.plugin.authn.webauthn.storage.WebAuthnCredentialRepository;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
@@ -139,28 +141,37 @@ public class CheckCredentialPolicy extends AbstractWebAuthnAction<WebAuthnAuthen
return;
}
- final Optional<CredentialRecord> credential =
- repository.getRegistrationByUserHandleAndCredentialId(assertion.getId(), userHandle.get());
+ final Collection<EnhancedCredentialRecord> registeredCredentials = context.getExistingCredentials();
+
+ final Optional<EnhancedCredentialRecord> credential = registeredCredentials.stream().filter(cred -> {
+ final CredentialRecord credRecord = cred.getCredentialRecord();
+ return assertion.getId().equals(credRecord.getCredential().getCredentialId()) &&
+ userHandle.get().equals(credRecord.getUserIdentity().getId());
+ }).findFirst();
+
if (credential.isEmpty()) {
log.trace("{} UserHandle '{}' has no registered credential",getLogPrefix(), userHandle.get().getHex());
return;
}
- final CredentialPolicyOutcome outcome = localPolicy.evaluate(credential.get(), profileRequestContext);
+ final EnhancedCredentialRecord credentialToEvaluate = credential.get();
+ assert credentialToEvaluate != null;
+ final CredentialPolicyOutcome outcome = localPolicy.evaluate(credentialToEvaluate, profileRequestContext);
+
if (outcome == CredentialPolicyOutcome.REJECT) {
log.warn("{} CredentialPolicy '{}' has rejected credential '{}'", getLogPrefix(), localPolicy.getId(),
- credential.get().getCredential().getCredentialId().getBase64Url());
+ credential.get().getCredentialRecord().getCredentialIdBase64Url());
authnContext.ensureSubcontext(AuthenticationErrorContext.class).getClassifiedErrors().add(
WebAuthnAuthenticationEventIds.CREDENTIAL_POLICY_REJECTION);
ActionSupport.buildEvent(profileRequestContext, WebAuthnAuthenticationEventIds.CREDENTIAL_POLICY_REJECTION);
return;
} else if (outcome == CredentialPolicyOutcome.IGNORE) {
log.trace("{} CredentialPolicy '{}' was not active for credential '{}', accepting", getLogPrefix(),
- localPolicy.getId(), credential.get().getCredential().getCredentialId().getBase64Url());
+ localPolicy.getId(), credential.get().getCredentialRecord().getCredentialIdBase64Url());
return;
}
log.debug("{} CredentialPolicy '{}' accepted credential '{}'", getLogPrefix(), localPolicy.getId(),
- credential.get().getCredential().getCredentialId().getHex());
+ credential.get().getCredentialRecord().getCredentialIdBase64Url());
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ExistingAuthenticationCredentialsLookupStrategy.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ExistingAuthenticationCredentialsLookupStrategy.java
new file mode 100644
index 0000000..1f3efcf
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/ExistingAuthenticationCredentialsLookupStrategy.java
@@ -0,0 +1,53 @@
+/*
+ * 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 java.util.Collection;
+import java.util.function.Function;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnAuthenticationContext;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.EnhancedCredentialRecord;
+import net.shibboleth.shared.collection.CollectionSupport;
+
+/**
+ * A credential lookup function that returns the list of existing credentials from the
+ * {@link WebAuthnAuthenticationContext}.
+ */
+public class ExistingAuthenticationCredentialsLookupStrategy
+ implements Function<ProfileRequestContext, Collection<EnhancedCredentialRecord>>{
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<EnhancedCredentialRecord> apply(@Nullable final ProfileRequestContext input) {
+ if (input == null) {
+ return CollectionSupport.emptyList();
+ }
+ final AuthenticationContext authnCtx = input.getSubcontext(AuthenticationContext.class);
+ if (authnCtx != null) {
+ final WebAuthnAuthenticationContext webAuthnCtx =
+ authnCtx.getSubcontext(WebAuthnAuthenticationContext.class);
+ if (webAuthnCtx != null) {
+ return webAuthnCtx.getExistingCredentials();
+ }
+ }
+ return CollectionSupport.emptyList();
+ }
+
+}
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 4b98a4f..3be8627 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
@@ -15,8 +15,6 @@
package net.shibboleth.idp.plugin.authn.webauthn.impl;
import java.util.Collection;
-import java.util.HashSet;
-import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
@@ -28,7 +26,6 @@ import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
-import com.yubico.fido.metadata.AAGUID;
import com.yubico.webauthn.data.ByteArray;
import net.shibboleth.idp.authn.AuthnEventIds;
@@ -36,7 +33,6 @@ 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.CredentialRecord;
-import net.shibboleth.idp.plugin.authn.webauthn.storage.EnhancedCredentialRecord;
import net.shibboleth.idp.plugin.authn.webauthn.storage.WebAuthnCredentialRepository;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
@@ -168,26 +164,13 @@ public class LookupRegisteredCredentials extends AbstractWebAuthnAction<BaseWebA
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
return;
}
-
+ assert username != null;
final Collection<CredentialRecord> credentials =
repository.getRegistrationsByUsername(username);
log.debug("{} Found '{}' registered credentials for '{}'", getLogPrefix(), credentials.size(), username);
- final Collection<EnhancedCredentialRecord> enhancedCredentialRegistrations =
- new HashSet<>(credentials.size());
-
- credentials.stream().filter(Objects::nonNull).forEach(cred -> {
- assert cred != null;
- final EnhancedCredentialRecord enhancedRecord = new EnhancedCredentialRecord(cred);
- final byte[] aaguid = cred.getAaguid();
- if (cred.getAaguid() != null) {
- enhancedRecord.setAuthenticatorMetadata(getAuthenticatorMetadata(new ByteArray(aaguid)));
- enhancedRecord.setAaguidMetadata(getAaguidMetadata(new AAGUID(new ByteArray(aaguid))));
- }
- enhancedCredentialRegistrations.add(enhancedRecord);
- });
- context.setExistingCredentials(enhancedCredentialRegistrations);
+ context.setExistingCredentials(enhancedCredentialRecord(credentials));
final Optional<ByteArray> userHandle = repository.getUserHandleForUsername(username);
if (userHandle.isPresent()) {
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 d17e8d4..a11a656 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
@@ -46,10 +46,8 @@ import net.shibboleth.shared.primitive.LoggerFactory;
/**
* An action that lookups existing registered credentials from the userHandle supplied in the authenticators assertion
- * response. If no credentials exist and the trigger condition is set, an error event will be produced.
- *
- * <p>Does not actually set the found credentials back onto the context. This is a sanity check that credentials exist,
- * and allows flow branching if none are found.</p>
+ * response and sets them back onto the context. If no credentials exist and the trigger condition is set, an error
+ * event will be produced.
*
* <p>Also ensures the username found from the userHandle matches that in the authentication context if supplied e.g.
* for a passwordless authentication.</p>
@@ -162,7 +160,8 @@ public class LookupRegisteredCredentialsFromUserHandle extends AbstractWebAuthnA
if (credentials.isEmpty()) {
log.debug("{} Could not find any registered credentials for userHandle '{}'", getLogPrefix(),
userHandle.get().getBase64());
- } else {
+ } else {
+ context.setExistingCredentials(enhancedCredentialRecord(credentials));
credentialsFound = true;
log.debug("{} Found registered credentials for userHandle '{}' (determined username as '{}')",
getLogPrefix(), userHandle.get().getBase64(), potentialUsername.get());
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/AbstractCredentialPolicyRule.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/AbstractCredentialPolicyRule.java
index 5b06b99..9587377 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/AbstractCredentialPolicyRule.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/AbstractCredentialPolicyRule.java
@@ -29,7 +29,7 @@ import com.yubico.fido.metadata.FidoMetadataService;
import net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnAuthenticationContext;
import net.shibboleth.idp.plugin.authn.webauthn.policy.CredentialPolicy;
-import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRecord;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.EnhancedCredentialRecord;
import net.shibboleth.shared.annotation.constraint.ThreadSafeAfterInit;
import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
import net.shibboleth.shared.logic.Constraint;
@@ -54,7 +54,7 @@ public abstract class AbstractCredentialPolicyRule extends AbstractIdentifiableI
@Nullable private FidoMetadataService fidoMetadataService;
/** Does this policy rule apply? Default is true. */
- @Nonnull private BiPredicate<CredentialRecord, ProfileRequestContext> activationCondition;
+ @Nonnull private BiPredicate<EnhancedCredentialRecord, ProfileRequestContext> activationCondition;
/** Lookup strategy to locate the WebAuthn context. */
@Nonnull private Function<ProfileRequestContext,WebAuthnAuthenticationContext> webauthnContextLookupStrategy;
@@ -87,7 +87,7 @@ public abstract class AbstractCredentialPolicyRule extends AbstractIdentifiableI
* @param condition condition to set
*/
public void setActivationConditionStrategy(
- @Nonnull final BiPredicate<CredentialRecord, ProfileRequestContext> condition) {
+ @Nonnull final BiPredicate<EnhancedCredentialRecord, ProfileRequestContext> condition) {
checkSetterPreconditions();
activationCondition = Constraint.isNotNull(condition, "Activation condition cannot be null");
}
@@ -130,7 +130,7 @@ public abstract class AbstractCredentialPolicyRule extends AbstractIdentifiableI
*/
@Override
public CredentialPolicyOutcome evaluate(
- @Nonnull final CredentialRecord credential, @Nonnull final ProfileRequestContext prc) {
+ @Nonnull final EnhancedCredentialRecord credential, @Nonnull final ProfileRequestContext prc) {
if (!activationCondition.test(credential, prc)) {
//not active for this request
log.trace("CredentialPolicy rule '{}' not active for this request", getId());
@@ -153,7 +153,7 @@ public abstract class AbstractCredentialPolicyRule extends AbstractIdentifiableI
* @return the credential policy outcome
*/
protected abstract CredentialPolicyOutcome doEvaluate(
- @Nonnull final CredentialRecord credential, @Nonnull final ProfileRequestContext prc,
+ @Nonnull final EnhancedCredentialRecord credential, @Nonnull final ProfileRequestContext prc,
@Nonnull final WebAuthnAuthenticationContext webAuthnContext);
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/ChainingCredentialPolicyRule.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/ChainingCredentialPolicyRule.java
index 1a8b8dd..9de34c8 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/ChainingCredentialPolicyRule.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/ChainingCredentialPolicyRule.java
@@ -25,6 +25,7 @@ import org.slf4j.Logger;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnAuthenticationContext;
import net.shibboleth.idp.plugin.authn.webauthn.policy.CredentialPolicy;
import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRecord;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.EnhancedCredentialRecord;
import net.shibboleth.shared.collection.CollectionSupport;
import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.primitive.LoggerFactory;
@@ -69,30 +70,31 @@ public class ChainingCredentialPolicyRule extends AbstractCredentialPolicyRule {
/** {@inheritDoc} */
@Override
protected CredentialPolicyOutcome doEvaluate(
- @Nonnull final CredentialRecord credential, @Nonnull final ProfileRequestContext prc,
+ @Nonnull final EnhancedCredentialRecord credential, @Nonnull final ProfileRequestContext prc,
@Nonnull final WebAuthnAuthenticationContext webAuthnContext) {
+ final CredentialRecord credentialRecord = credential.getCredentialRecord();
for (final CredentialPolicy policy : credentialPolicyChain) {
if (log.isTraceEnabled()) {
log.trace("Trying CredentialPolicy rule '{}' for credential '{}'", policy.getId(),
- credential.getCredentialIdBase64Url());
+ credentialRecord.getCredentialIdBase64Url());
}
final CredentialPolicyOutcome outcome = policy.evaluate(credential, prc);
if (outcome == CredentialPolicyOutcome.REJECT) {
if (log.isDebugEnabled()) {
log.debug("CredentialPolicy rule '{}' rejected credential '{}'", policy.getId(),
- credential.getCredentialIdBase64Url());
+ credentialRecord.getCredentialIdBase64Url());
}
return CredentialPolicyOutcome.REJECT;
} else if (outcome == CredentialPolicyOutcome.IGNORE){
if (log.isDebugEnabled()) {
log.debug("CredentialPolicy rule '{}' was ignored for credential '{}'", policy.getId(),
- credential.getCredentialIdBase64Url());
+ credentialRecord.getCredentialIdBase64Url());
}
} else {
if (log.isTraceEnabled()) {
log.trace("CredentialPolicy rule '{}' accepted credential '{}'", policy.getId(),
- credential.getCredentialIdBase64Url());
+ credentialRecord.getCredentialIdBase64Url());
}
}
}
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/FunctionalCredentialPolicyRule.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/FunctionalCredentialPolicyRule.java
index 936bed5..e397ce7 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/FunctionalCredentialPolicyRule.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/FunctionalCredentialPolicyRule.java
@@ -23,7 +23,7 @@ import org.slf4j.Logger;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnAuthenticationContext;
import net.shibboleth.idp.plugin.authn.webauthn.policy.CredentialPolicy;
-import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRecord;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.EnhancedCredentialRecord;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.LoggerFactory;
@@ -37,7 +37,7 @@ public class FunctionalCredentialPolicyRule extends AbstractCredentialPolicyRule
@Nonnull private final Logger log = LoggerFactory.getLogger(FunctionalCredentialPolicyRule.class);
/** The function to run. By default this returns {@link CredentialPolicyOutcome#IGNORE}.*/
- @Nonnull private BiFunction<CredentialRecord, FunctionalRuleContext, CredentialPolicyOutcome> rule;
+ @Nonnull private BiFunction<EnhancedCredentialRecord, FunctionalRuleContext, CredentialPolicyOutcome> rule;
/** Constructor.*/
public FunctionalCredentialPolicyRule() {
@@ -50,14 +50,14 @@ public class FunctionalCredentialPolicyRule extends AbstractCredentialPolicyRule
* @param function the rule to set.
*/
public void setRule(@Nonnull
- final BiFunction<CredentialRecord, FunctionalRuleContext, CredentialPolicyOutcome> function) {
+ final BiFunction<EnhancedCredentialRecord, FunctionalRuleContext, CredentialPolicyOutcome> function) {
checkSetterPreconditions();
rule = Constraint.isNotNull(function, "Rule function can not be null");
}
/** {@inheritDoc} */
@Override
- protected CredentialPolicyOutcome doEvaluate(final CredentialRecord credential,
+ protected CredentialPolicyOutcome doEvaluate(final EnhancedCredentialRecord credential,
final ProfileRequestContext prc, final WebAuthnAuthenticationContext webAuthnContext) {
log.debug("Running CredentialPolicy rule function '{}'", this.getId());
return rule.apply(credential, new FunctionalRuleContext(prc, getFidoMetadataService()));
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/SecondFactorOnlyCredentialPolicyRule.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/SecondFactorOnlyCredentialPolicyRule.java
index 266a016..9e3b822 100644
--- a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/SecondFactorOnlyCredentialPolicyRule.java
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/SecondFactorOnlyCredentialPolicyRule.java
@@ -13,7 +13,9 @@
*/
package net.shibboleth.idp.plugin.authn.webauthn.policy.impl;
-import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -21,13 +23,20 @@ import javax.annotation.Nullable;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
-import net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.SecondFactorOnlyAuthenticatorInspector;
+import com.yubico.fido.metadata.AAGUID;
+import com.yubico.webauthn.data.ByteArray;
+
+import net.shibboleth.idp.plugin.authn.webauthn.authn.AuthenticatorSupport;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnAuthenticationContext;
import net.shibboleth.idp.plugin.authn.webauthn.policy.CredentialPolicy;
-import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRecord;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.EnhancedCredentialRecord;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
import net.shibboleth.shared.codec.Base64Support;
import net.shibboleth.shared.codec.EncodingException;
+import net.shibboleth.shared.collection.CollectionSupport;
import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.StringSupport;
/**
* A {@link CredentialPolicy} that rejects credentials used in sole-factor mode if created by authenticators which
@@ -38,34 +47,62 @@ public class SecondFactorOnlyCredentialPolicyRule extends AbstractCredentialPoli
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(SecondFactorOnlyCredentialPolicyRule.class);
+ /** A set of authenticators that should only be used for second factor authentication, based on their AAGUID.*/
+ @Nonnull @NotLive @Unmodifiable private Set<AAGUID> secondFactorOnlyAuthenticators;
+
+ /** Constructor.*/
+ public SecondFactorOnlyCredentialPolicyRule() {
+ secondFactorOnlyAuthenticators = CollectionSupport.emptySet();
+ }
+
+ /**
+ * Set the authenticators that should only be used for second factor authentication based on their AAGUID.
+ *
+ * @param allowed The second factor authenticators to set.
+ */
+ public void setSecondFactorOnlyAuthenticators(final Set<String> allowed) {
+ checkSetterPreconditions();
+ if (allowed != null) {
+ secondFactorOnlyAuthenticators = allowed.stream().filter(strAAGUID ->
+ StringSupport.trimOrNull(strAAGUID)!=null)
+ .map(strAAGUID -> {
+ final var aaguidBytes = AuthenticatorSupport.parse(strAAGUID);
+ if (aaguidBytes != null) {
+ return new AAGUID(aaguidBytes);
+ } else {
+ log.trace("AAGUID '{}' is not valid", strAAGUID);
+ return null;
+ }
+ }).filter(Objects::nonNull).collect(CollectionSupport.nonnullCollector(Collectors.toSet())).get();
+ }
+ }
+
/** {@inheritDoc} */
@Override
public CredentialPolicyOutcome doEvaluate(
- @Nonnull final CredentialRecord credential, @Nonnull final ProfileRequestContext prc,
+ @Nonnull final EnhancedCredentialRecord credential, @Nonnull final ProfileRequestContext prc,
@Nonnull final WebAuthnAuthenticationContext webAuthnContext) {
- final Map<String,String> capabilities = credential.getAuthenticatorCapabilities();
- if (capabilities.containsKey(SecondFactorOnlyAuthenticatorInspector.CAPABILITY_NAME)) {
- final String value = capabilities.get(SecondFactorOnlyAuthenticatorInspector.CAPABILITY_NAME);
- final boolean isTrue = Boolean.parseBoolean(value);
-
- if (!webAuthnContext.isSecondFactor() && isTrue) {
- if (log.isTraceEnabled()) {
- log.trace("Rejected credential '{}', authentication is sole-factor and authenticator '{}' that "
- + "created the credential should only be used as a second factor",
- credential.getCredentialIdBase64Url(), toBase64OrUnknown(credential.getAaguid()));
- }
- return CredentialPolicyOutcome.REJECT;
+ final AAGUID aaguidToCheck = new AAGUID(new ByteArray(credential.getCredentialRecord().getAaguid()));
+ final boolean secondFactorOnly = secondFactorOnlyAuthenticators.contains(aaguidToCheck);
+
+ if (!webAuthnContext.isSecondFactor() && secondFactorOnly) {
+ if (log.isTraceEnabled()) {
+ log.trace("Rejected credential '{}', authentication is sole-factor and authenticator '{}' that "
+ + "created the credential should only be used as a second factor",
+ credential.getCredentialRecord().getCredentialIdBase64Url(),
+ toBase64OrUnknown(credential.getCredentialRecord().getAaguid()));
}
+ return CredentialPolicyOutcome.REJECT;
}
return CredentialPolicyOutcome.ACCEPT;
}
/**
- * Convert the value to Base64 URL encoding, or return "unknown" if there is an error.
+ * Convert the value to Base64 URL encoding or return "unknown" if there is an error.
*
* @param value the value to Base64 URL encode
- * @return the Bas64 URL encoded string.
+ * @return the Bas64 URL encoded string or "unknown".
*/
private String toBase64OrUnknown(@Nullable final byte[] value) {
if (value == null) {
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 ed54578..d1ac708 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
@@ -40,7 +40,6 @@
p:webAuthnClient="#{getObject('shibboleth.authn.WebAuthn.WebAuthnAuthenticationClientFactory') ?: getObject('shibboleth.authn.WebAuthn.DefaultWebAuthnAuthenticationClientFactory')}"
p:credentialRepository="#{getObject('shibboleth.authn.WebAuthn.CredentialRepository') ?: getObject('shibboleth.authn.WebAuthn.DefaultCredentialRepository')}"
p:fidoMetadataService="#{'false'.equals('%{idp.authn.webauthn.metadata.enabled:false}') ? null : getObject('shibboleth.authn.WebAuthn.DefaultWebAuthnFidoMetadataServiceFactory')}"/>
-
<!-- Flow beans -->
@@ -71,15 +70,14 @@
<bean id="LookupCredentialsForUser" parent="AbstractWebAuthnBaseAction" scope="prototype"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.LookupCredentialsForUser"
p:webAuthnContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnManagementContext" />
-
- <bean id="LabelCredentialRecords" parent="AbstractWebAuthnBaseAction" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.LabelAdminCredentialRecords"
- p:webAuthnContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnManagementContext"
- p:labeller="#{getObject('%{idp.authn.webauthn.registration.labeller:shibboleth.authn.WebAuthn.ChainingCredentialLabeller}')}"/>
-
- <bean id="shibboleth.authn.WebAuthn.ChainingCredentialLabeller" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.ChainingCredentialLabeller"
- c:labellers="#{getObject('shibboleth.authn.WebAuthn.CredentialLabellerList')}"/>
+
+ <bean id="LabelCredentialRecords" scope="prototype"
+ class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.LabelCredentialRecords"
+ p:labeller="#{getObject('%{idp.authn.webauthn.labeller:shibboleth.authn.WebAuthn.ChainingCredentialLabeller}')}">
+ <property name="credentialsLookupStrategy">
+ <bean id="SearchedUserCredentialLookupStrategy" class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.AdminSearchCredentialsLookupFunction"/>
+ </property>
+ </bean>
<bean id="ExtractKeyRemovalInformationFromFormRequest" scope="prototype"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.ExtractKeyInformationFromFormRequest"
@@ -142,5 +140,6 @@
</bean>
<import resource="conditional:%{idp.home}/conf/authn/webauthn-management-config.xml" />
+ <import resource="conditional:%{idp.home}/conf/authn/webauthn-config.xml" />
</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 ce929a9..5891926 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
@@ -36,19 +36,12 @@
<bean id="shibboleth.authn.WebAuthn.registration.AbstractAuthenticatorPolicyRule" scope="prototype" abstract="true"
p:fidoMetadataService="#{'false'.equals('%{idp.authn.webauthn.metadata.enabled:false}') ? null : getObject('shibboleth.authn.WebAuthn.DefaultWebAuthnFidoMetadataServiceFactory')}"/>
-
- <bean id="shibboleth.authn.WebAuthn.registration.AbstractAuthenticatorCapabilitiesInspector" scope="prototype" abstract="true"
- p:fidoMetadataService="#{'false'.equals('%{idp.authn.webauthn.metadata.enabled:false}') ? null : getObject('shibboleth.authn.WebAuthn.DefaultWebAuthnFidoMetadataServiceFactory')}"/>
<bean id="shibboleth.authn.WebAuthn.registration.FunctionalAuthenticatorPolicyRule" abstract="true"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.FunctionalAuthenticatorPolicyRule"
p:fidoMetadataService="#{'false'.equals('%{idp.authn.webauthn.metadata.enabled:false}') ? null : getObject('shibboleth.authn.WebAuthn.DefaultWebAuthnFidoMetadataServiceFactory')}"/>
- <bean id="shibboleth.authn.WebAuthn.registration.FunctionalAuthenticatorInspectorRule" abstract="true"
- class="net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.FunctionalAuthenticatorInspectorRule"
- p:fidoMetadataService="#{'false'.equals('%{idp.authn.webauthn.metadata.enabled:false}') ? null : getObject('shibboleth.authn.WebAuthn.DefaultWebAuthnFidoMetadataServiceFactory')}"/>
-
-
+
<!-- Flow beans -->
<bean id="PopulateInlineEnrolmentContext" scope="prototype"
@@ -132,15 +125,14 @@
<bean id="LookupRegisteredCredentials" parent="AbstractWebAuthnBaseAction" scope="prototype"
class="net.shibboleth.idp.plugin.authn.webauthn.impl.LookupRegisteredCredentials"
p:webAuthnContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnRegistrationContext" />
-
- <bean id="LabelCredentialRecords" parent="AbstractWebAuthnBaseAction" scope="prototype"
+
+ <bean id="LabelCredentialRecords" scope="prototype"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.LabelCredentialRecords"
- p:webAuthnContextLookupStrategy-ref="shibboleth.ChildLookup.WebAuthnRegistrationContext"
- p:labeller="#{getObject('%{idp.authn.webauthn.registration.labeller:shibboleth.authn.WebAuthn.ChainingCredentialLabeller}')}"/>
-
- <bean id="shibboleth.authn.WebAuthn.ChainingCredentialLabeller" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.ChainingCredentialLabeller"
- c:labellers="#{getObject('shibboleth.authn.WebAuthn.CredentialLabellerList')}"/>
+ p:labeller="#{getObject('%{idp.authn.webauthn.registration.labeller:shibboleth.authn.WebAuthn.ChainingCredentialLabeller}')}">
+ <property name="credentialsLookupStrategy">
+ <bean id="ExistingCredentialsLookup" class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.ExistingRegistrationCredentialsLookupStrategy"/>
+ </property>
+ </bean>
<bean id="GenerateServerChallenge" parent="AbstractWebAuthnBaseAction" scope="prototype"
class="net.shibboleth.idp.plugin.authn.webauthn.impl.GenerateServerChallenge"
@@ -194,24 +186,15 @@
p:writeAuditLogAction="#{%{idp.authn.webauthn.registration.audit.enabled:false} ? getObject('WriteAdminAuditLog') : null}"
p:auditContextCreationStrategy-ref="AdminAuditContextLookup" />
- <bean id="CheckAuthenticatorPolicy" parent="AbstractWebAuthnRegistrationAction" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.CheckAuthenticatorPolicy"
- p:authenticatorPolicy="#{getObject('%{idp.authn.webauthn.registration.authenticator.policy:shibboleth.authn.WebAuthn.registration.ChainedAuthenticatorPolicy}')}"
+ <bean id="CheckRegistrationPolicy" parent="AbstractWebAuthnRegistrationAction" scope="prototype"
+ class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.CheckRegistrationPolicy"
+ p:authenticatorPolicy="#{getObject('%{idp.authn.webauthn.registration.authenticator.policy:shibboleth.authn.WebAuthn.registration.ChainedRegistrationPolicy}')}"
p:activationCondition="%{idp.authn.webauthn.registration.authenticator.policy.enabled:false}"/>
- <bean id="shibboleth.authn.WebAuthn.registration.ChainedAuthenticatorPolicy" scope="prototype"
+ <bean id="shibboleth.authn.WebAuthn.registration.ChainedRegistrationPolicy" scope="prototype"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.ChainingAuthenticatorPolicyRule"
- p:authenticatorPolicyChain="#{getObject('%{idp.authn.webauthn.registration.authenticator.policy.chainedlist:shibboleth.authn.WebAuthn.registration.ChainedAuthenticatorPolicyList}')}"/>
-
- <bean id="AttachAuthenticatorCapabilities" parent="AbstractWebAuthnRegistrationAction" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.AttachAuthenticatorCapabilities"
- p:authenticatorInspector="#{getObject('%{idp.authn.webauthn.registration.authenticator.inspector:shibboleth.authn.WebAuthn.registration.ChainedAuthenticatorCapabilitiesInspector}')}"
- p:activationCondition="%{idp.authn.webauthn.registration.authenticator.inspector.enabled:false}"/>
-
- <bean id="shibboleth.authn.WebAuthn.registration.ChainedAuthenticatorCapabilitiesInspector" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.ChainingAuthenticatorCapabilitiesInspector"
- p:authenticatorInspectorChain="#{getObject('%{idp.authn.webauthn.registration.authenticator.inspector.chainedlist:shibboleth.authn.WebAuthn.registration.ChainedAuthenticatorInspectorList}')}"/>
-
+ p:authenticatorPolicyChain="#{getObject('%{idp.authn.webauthn.registration.authenticator.policy.chainedlist:shibboleth.authn.WebAuthn.registration.ChainedRegistrationPolicyList}')}"/>
+
<bean id="ValidateAuthenticatorAttestationResponse" parent="AbstractWebAuthnRegistrationAction"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.ValidateAuthenticatorAttestationResponse" />
@@ -270,5 +253,6 @@
</bean>
<import resource="conditional:%{idp.home}/conf/authn/webauthn-registration-config.xml" />
+ <import resource="conditional:%{idp.home}/conf/authn/webauthn-config.xml" />
</beans>
diff --git a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-flow.xml b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-flow.xml
index cc6c46a..f50e19c 100644
--- a/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-flow.xml
+++ b/webauthn-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/admin/webauthn-registration/webauthn-registration-flow.xml
@@ -135,8 +135,7 @@
<action-state id="AddKey">
<evaluate expression="ExtractPublicKeyCredentialAttestationFromFormRequest"/>
- <evaluate expression="CheckAuthenticatorPolicy"/>
- <evaluate expression="AttachAuthenticatorCapabilities"/>
+ <evaluate expression="CheckRegistrationPolicy"/>
<evaluate expression="ValidateAuthenticatorAttestationResponse"/>
<evaluate expression="StorePublicKeyCredential"/>
<evaluate expression="'proceed'" />
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 98b70ba..1f483e1 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
@@ -34,7 +34,14 @@
p:fidoMetadataService="#{'false'.equals('%{idp.authn.webauthn.metadata.enabled:false}') ? null : getObject('shibboleth.authn.WebAuthn.DefaultWebAuthnFidoMetadataServiceFactory')}"
p:aaguidService="#{'false'.equals('%{idp.authn.webauthn.metadata.aaguid.enabled:false}') ? null : getObject('shibboleth.authn.WebAuthn.DefaultPasskeyAaguidMetadataServiceFactory')}"/>
- <!-- Used in views to calculate CSP hashes and nonces, remove and adjust beans in flow when compatibility bumped past 5.0 -->
+
+ <!-- Beans shared across two or more flows -->
+
+ <bean id="shibboleth.authn.WebAuthn.ChainingCredentialLabeller" scope="prototype"
+ class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.ChainingCredentialLabeller"
+ c:labellers="#{getObject('shibboleth.authn.WebAuthn.CredentialLabellerList')}"/>
+
+ <!-- Used in views to calculate CSP hashes and nonces, remove and adjust beans in flow when compatibility bumped past 5.0 -->
<bean id="WebAuthnCSPDigester" class="net.shibboleth.shared.codec.StringDigester"
c:algorithm="SHA256" c:format="BASE64" />
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 668cba4..d9dd67e 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
@@ -142,7 +142,7 @@
class="net.shibboleth.idp.plugin.authn.webauthn.impl.LookupRegisteredCredentialsFromUserHandle"
p:credentialRepository="#{getObject('shibboleth.authn.WebAuthn.CredentialRepositoryy') ?: getObject('shibboleth.authn.WebAuthn.DefaultCredentialRepository')}"
p:triggerEventOnNoCredentials="%{idp.authn.webauthn.signalEventOnNoCredentialsRegisteredForUserHandle:false}"
- p:noCredentialsEventId="%{idp.authn.webauthn.userHandleNoRegisteredCredentialsEventId:NoCredentialsRegisteredForUserHandle}"/>
+ p:noCredentialsEventId="%{idp.authn.webauthn.userHandleNoRegisteredCredentialsEventId:NoCredentialsRegisteredForUserHandle}"/>
<bean id="CheckCredentialPolicy" parent="AbstractWebAuthnAuthenticationAction" scope="prototype"
class="net.shibboleth.idp.plugin.authn.webauthn.impl.CheckCredentialPolicy"
@@ -216,6 +216,7 @@
</property>
</bean>
+ <import resource="conditional:%{idp.home}/conf/authn/webauthn-authentication-config.xml" />
<import resource="conditional:%{idp.home}/conf/authn/webauthn-config.xml" />
</beans>
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-authentication-config.xml
similarity index 87%
copy from webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml
copy to webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-authentication-config.xml
index 48e1dc0..ad23afd 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-authentication-config.xml
@@ -16,8 +16,9 @@
-->
<util:list id="shibboleth.authn.WebAuthn.ChainedCredentialPolicyList">
<bean id="SecondFactorOnlyCredentialPolicyRule" parent="shibboleth.authn.WebAuthn.AbstractCredentialPolicyRule"
- class="net.shibboleth.idp.plugin.authn.webauthn.policy.impl.SecondFactorOnlyCredentialPolicyRule"/>
+ class="net.shibboleth.idp.plugin.authn.webauthn.policy.impl.SecondFactorOnlyCredentialPolicyRule"
+ p:secondFactorOnlyAuthenticators="%{idp.authn.webauthn.authenticator.policy.secondFactorOnlyAuthenticators:null}"/>
</util:list>
-
+
</beans>
\ No newline at end of file
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml
index 48e1dc0..4e6ca64 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-config.xml
@@ -8,15 +8,22 @@
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
default-init-method="initialize" default-destroy-method="destroy" default-lazy-init="true">
+
+ <!-- Configuration that is shared between two or more WebAuthn flows -->
+
<!--
- The default list to add credential policies used during authentication. The property
- idp.authn.webauthn.credential.policy.enabled must be set to true for these to take effect.
-
+ The default credential labeller list. Use during registration or management flows to label credential for display
+ purposes only.
-->
- <util:list id="shibboleth.authn.WebAuthn.ChainedCredentialPolicyList">
- <bean id="SecondFactorOnlyCredentialPolicyRule" parent="shibboleth.authn.WebAuthn.AbstractCredentialPolicyRule"
- class="net.shibboleth.idp.plugin.authn.webauthn.policy.impl.SecondFactorOnlyCredentialPolicyRule"/>
+ <util:list id="shibboleth.authn.WebAuthn.CredentialLabellerList">
+
+ <bean id="CapabilitiesCredentialLabeller" scope="prototype"
+ class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.SecondFactorOnlyCredentialLabeller"
+ p:secondFactorOnlyAuthenticators="%{idp.authn.webauthn.authenticator.policy.secondFactorOnlyAuthenticators:null}"
+ p:label="SecondFactorOnly"/>
+
+ <bean id="PasskeyCredentialLabeller" class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.PasskeyCredentialLabeller"/>
</util:list>
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-management-config.xml b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-management-config.xml
index 07605bd..92416d5 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-management-config.xml
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-management-config.xml
@@ -8,31 +8,6 @@
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
default-init-method="initialize" default-destroy-method="destroy" default-lazy-init="true">
-
- <!--
- The default credential labeller list
- -->
- <util:list id="shibboleth.authn.WebAuthn.CredentialLabellerList">
-
- <bean id="CapabilitiesCredentialLabeller" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.AuthenticatorCapabilitiesLabeller"
- c:_0-ref="shibboleth.authn.WebAuthn.DefaultCapabilitiesToLabelsMap"/>
-
- <bean id="PasskeyCredentialLabeller" class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.PasskeyCredentialLabeller"/>
- </util:list>
-
- <!--
- The default capabilities to labels map. The capabilities are first determined by the Inspectors.
- -->
- <util:map id="shibboleth.authn.WebAuthn.DefaultCapabilitiesToLabelsMap">
- <entry>
- <key>
- <bean parent="shibboleth.Pair"
- p:first="2faOnly" p:second="true"/>
- </key>
- <value>SecondFactorOnly</value>
- </entry>
- </util:map>
</beans>
\ No newline at end of file
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-registration-config.xml b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-registration-config.xml
index ce177f3..3b1a7c1 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-registration-config.xml
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-registration-config.xml
@@ -13,45 +13,11 @@
The default list to add authenticator policies used during registration. The property
idp.authn.webauthn.registration.authenticator.policy.enabled must be set to true for these to take effect.
-->
- <util:list id="shibboleth.authn.WebAuthn.registration.ChainedAuthenticatorPolicyList">
+ <util:list id="shibboleth.authn.WebAuthn.registration.ChainedRegistrationPolicyList">
<bean id="AllowlistAuthenticatorPolicy" parent="shibboleth.authn.WebAuthn.registration.AbstractAuthenticatorPolicyRule"
class="net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.AllowlistAuthenticatorPolicy"
p:allowedAuthenticators="%{idp.authn.webauthn.registration.authenticator.policy.allowedAuthenticators:null}"/>
</util:list>
- <!--
- The default list to add authenticator inspectors used during registration. The property
- idp.authn.webauthn.registration.authenticator.inspector.enabled must be set to true for these to take effect.
- -->
- <util:list id="shibboleth.authn.WebAuthn.registration.ChainedAuthenticatorInspectorList">
- <bean id="SecondFactorOnlyInspector" parent="shibboleth.authn.WebAuthn.registration.AbstractAuthenticatorCapabilitiesInspector"
- class="net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.SecondFactorOnlyAuthenticatorInspector"
- p:secondFactorOnlyAuthenticators="%{idp.authn.webauthn.registration.authenticator.inspector.secondFactorOnlyAuthenticators:null}"/>
- </util:list>
-
- <!--
- The default credential labeller list
- -->
- <util:list id="shibboleth.authn.WebAuthn.CredentialLabellerList">
-
- <bean id="CapabilitiesCredentialLabeller" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.AuthenticatorCapabilitiesLabeller"
- c:_0-ref="shibboleth.authn.WebAuthn.DefaultCapabilitiesToLabelsMap"/>
-
- <bean id="PasskeyCredentialLabeller" class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.PasskeyCredentialLabeller"/>
- </util:list>
-
- <!--
- The default capabilities to labels map. The capabilities are first determined by the Inspectors.
- -->
- <util:map id="shibboleth.authn.WebAuthn.DefaultCapabilitiesToLabelsMap">
- <entry>
- <key>
- <bean parent="shibboleth.Pair"
- p:first="2faOnly" p:second="true"/>
- </key>
- <value>SecondFactorOnly</value>
- </entry>
- </util:map>
</beans>
\ No newline at end of file
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-registration.properties b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-registration.properties
index 2cd2022..c8527e6 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-registration.properties
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-registration.properties
@@ -48,7 +48,7 @@
# State the preference of the IdP during registration to receive an authenticator attestation. One-of 'none', 'indirect', 'direct', or 'enterprise'.
#idp.authn.webauthn.registration.attestationConveyancePreference = none
-# Enable the authenticator policy engine?
+# Enable the registration policy engine?
#idp.authn.webauthn.registration.authenticator.policy.enabled = false
# Set the authenticator policy to use, defaults to a chained set of policies
#idp.authn.webauthn.registration.authenticator.policy = shibboleth.authn.WebAuthn.registration.ChainedAuthenticatorPolicy
@@ -57,15 +57,6 @@
# When using the default chained policy; list allowed authenticator's by their attestation GUIDs (AAGUID)
#idp.authn.webauthn.registration.authenticator.policy.allowedAuthenticators =
-# Enable the authenticator inspector engine?
-#idp.authn.webauthn.registration.authenticator.inspector.enabled = false
-# Set the authenticator inspectors to use, defaults to a chained set of inspectors
-#idp.authn.webauthn.registration.authenticator.inspector = shibboleth.authn.WebAuthn.registration.ChainedAuthenticatorCapabilitiesInspector
-# When using the default chained inspector; name the inspector list bean
-#idp.authn.webauthn.registration.authenticator.inspector.chainedlist = shibboleth.authn.WebAuthn.registration.ChainedAuthenticatorInspectors
-# When using the default chained set of inspectors; list authenticators (by attestation GUIDs (AAGUID)) to tag as only allowed for second factor authentication
-#idp.authn.webauthn.registration.authenticator.inspector.secondFactorOnlyAuthenticators =
-
# Allow inline self-enrolment
#idp.authnwebauthn.registration.allowInline = true
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 b9ed6db..5e70f5e 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
@@ -70,6 +70,8 @@ idp.authn.webauthn.supportedPrincipals = \
#idp.authn.webauthn.credential.policy = shibboleth.authn.WebAuthn.ChainedCredentialPolicy
# When using the default chained policy, which policy list should we use?
#idp.authn.webauthn.credential.policy.chainedlist = shibboleth.authn.WebAuthn.ChainedCredentialPolicyList
+# When using the default chained set of inspectors; list authenticators (by attestation GUIDs (AAGUID)) to tag as only allowed for second factor authentication
+#idp.authn.webauthn.authenticator.policy.secondFactorOnlyAuthenticators =
# Audit
#idp.authn.webauthn.audit.enabled = false
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/module.properties b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/module.properties
index 860430e..ebbf6da 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/module.properties
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/module.properties
@@ -30,35 +30,39 @@ idp.authn.WebAuthn.5.src = /net/shibboleth/idp/plugin/authn/webauthn/conf/authn/
idp.authn.WebAuthn.5.dest = conf/authn/webauthn-management-config.xml
idp.authn.WebAuthn.5.replace = false
-idp.authn.WebAuthn.6.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-authn.vm
-idp.authn.WebAuthn.6.dest = views/webauthn/webauthn-authn.vm
+idp.authn.WebAuthn.6.src = /net/shibboleth/idp/plugin/authn/webauthn/conf/authn/webauthn-authentication-config.xml
+idp.authn.WebAuthn.6.dest = conf/authn/webauthn-authentication-config.xml
+idp.authn.WebAuthn.6.replace = false
-idp.authn.WebAuthn.7.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm
-idp.authn.WebAuthn.7.dest = views/webauthn/webauthn-register.vm
+idp.authn.WebAuthn.7.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-authn.vm
+idp.authn.WebAuthn.7.dest = views/webauthn/webauthn-authn.vm
-idp.authn.WebAuthn.8.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register-username.vm
-idp.authn.WebAuthn.8.dest = views/webauthn/webauthn-register-username.vm
+idp.authn.WebAuthn.8.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm
+idp.authn.WebAuthn.8.dest = views/webauthn/webauthn-register.vm
-idp.authn.WebAuthn.9.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-authn-username.vm
-idp.authn.WebAuthn.9.dest = views/webauthn/webauthn-authn-username.vm
+idp.authn.WebAuthn.9.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register-username.vm
+idp.authn.WebAuthn.9.dest = views/webauthn/webauthn-register-username.vm
-idp.authn.WebAuthn.10.src = /net/shibboleth/idp/plugin/authn/webauthn/css/webauthn.css
-idp.authn.WebAuthn.10.dest = edit-webapp/css/webauthn.css
-idp.authn.WebAuthn.10.postenable = Customize edit-webapp/css/webauthn.css and rebuild war to deploy.
+idp.authn.WebAuthn.10.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-authn-username.vm
+idp.authn.WebAuthn.10.dest = views/webauthn/webauthn-authn-username.vm
-idp.authn.WebAuthn.11.src = /net/shibboleth/idp/plugin/authn/webauthn/js/webauthn-json.browser-ponyfill.min.js
-idp.authn.WebAuthn.11.dest = edit-webapp/js/webauthn/webauthn-json.browser-ponyfill.min.js
+idp.authn.WebAuthn.11.src = /net/shibboleth/idp/plugin/authn/webauthn/css/webauthn.css
+idp.authn.WebAuthn.11.dest = edit-webapp/css/webauthn.css
+idp.authn.WebAuthn.11.postenable = Customize edit-webapp/css/webauthn.css and rebuild war to deploy.
-idp.authn.WebAuthn.12.src = /net/shibboleth/idp/plugin/authn/webauthn/js/webauthn-support.js
-idp.authn.WebAuthn.12.dest = edit-webapp/js/webauthn/webauthn-support.js
+idp.authn.WebAuthn.12.src = /net/shibboleth/idp/plugin/authn/webauthn/js/webauthn-json.browser-ponyfill.min.js
+idp.authn.WebAuthn.12.dest = edit-webapp/js/webauthn/webauthn-json.browser-ponyfill.min.js
-idp.authn.WebAuthn.13.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-end.vm
-idp.authn.WebAuthn.13.dest = views/webauthn/webauthn-end.vm
+idp.authn.WebAuthn.13.src = /net/shibboleth/idp/plugin/authn/webauthn/js/webauthn-support.js
+idp.authn.WebAuthn.13.dest = edit-webapp/js/webauthn/webauthn-support.js
-idp.authn.WebAuthn.14.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-management.vm
-idp.authn.WebAuthn.14.dest = views/webauthn/webauthn-management.vm
+idp.authn.WebAuthn.14.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-end.vm
+idp.authn.WebAuthn.14.dest = views/webauthn/webauthn-end.vm
-idp.authn.WebAuthn.15.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-management-search.vm
-idp.authn.WebAuthn.15.dest = views/webauthn/webauthn-management-search.vm
+idp.authn.WebAuthn.15.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-management.vm
+idp.authn.WebAuthn.15.dest = views/webauthn/webauthn-management.vm
+
+idp.authn.WebAuthn.16.src = /net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-management-search.vm
+idp.authn.WebAuthn.16.dest = views/webauthn/webauthn-management-search.vm
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CheckAuthenticatorPolicyTest.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CheckAuthenticatorPolicyTest.java
index 63885bf..b5721ba 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CheckAuthenticatorPolicyTest.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/admin/impl/CheckAuthenticatorPolicyTest.java
@@ -34,11 +34,11 @@ import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnTest;
import net.shibboleth.shared.component.ComponentInitializationException;
/**
- * Test for {@link CheckAuthenticatorPolicy}.
+ * Test for {@link CheckRegistrationPolicy}.
*/
public class CheckAuthenticatorPolicyTest extends AbstractWebAuthnTest {
- private CheckAuthenticatorPolicy checkAction;
+ private CheckRegistrationPolicy checkAction;
private ByteArray aaguid;
@@ -46,7 +46,7 @@ public class CheckAuthenticatorPolicyTest extends AbstractWebAuthnTest {
@BeforeMethod
public void setup() throws Exception {
super.setup();
- checkAction = new CheckAuthenticatorPolicy();
+ checkAction = new CheckRegistrationPolicy();
final var context = addWebAuthnRegistrationContext();
context.setUsername(USERNAME);
checkAction.setWebAuthnClient(client);
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/AbstractWebAuthnFlowTest.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/AbstractWebAuthnFlowTest.java
index 6823f11..f9822f7 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/AbstractWebAuthnFlowTest.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/AbstractWebAuthnFlowTest.java
@@ -92,7 +92,6 @@ import net.shibboleth.idp.ui.context.RelyingPartyUIContext;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.codec.Base64Support;
import net.shibboleth.shared.codec.EncodingException;
-import net.shibboleth.shared.collection.CollectionSupport;
import net.shibboleth.shared.collection.Pair;
import net.shibboleth.shared.servlet.impl.HttpServletRequestResponseContext;
@@ -204,21 +203,6 @@ public abstract class AbstractWebAuthnFlowTest extends AbstractFlowTest {
return obj;
}
- /**
- * Create a credential registration with a new attestation response from the mock authenticator.
- *
- * @param username the username
- * @param display name the display name
- * @param userHandleB64 the user handle base64 encoded
- * @return the credential registration
- *
- * @throws Exception on error
- */
- protected CredentialRecord createCredentialRegistration(final String username, final String displayName,
- final String userHandleB64) throws Exception {
-
- return createCredentialRegistration(username, displayName, userHandleB64, null);
- }
/**
* Create a credential registration with a new attestation response from the mock authenticator.
@@ -226,13 +210,12 @@ public abstract class AbstractWebAuthnFlowTest extends AbstractFlowTest {
* @param username the username
* @param display name the display name
* @param userHandleB64 the user handle base64 encoded
- * @param capabilities the capabilities of the authenticator that created this credential
* @return the credential registration
*
* @throws Exception on error
*/
protected CredentialRecord createCredentialRegistration(final String username, final String displayName,
- final String userHandleB64, final Map<String,String> capabilities) throws Exception {
+ final String userHandleB64) throws Exception {
final var user = UserIdentity.builder()
.name(username)
@@ -264,8 +247,9 @@ public abstract class AbstractWebAuthnFlowTest extends AbstractFlowTest {
.withCredential(credential)
.withCredentialNickname("nickname")
.withDiscoverable(Optional.of(Boolean.TRUE))
- .withAuthenticatorCapabilities(capabilities != null ? capabilities : CollectionSupport.emptyMap())
.withUserVerified(true)
+ .withAaguid(attestation.getResponse().getParsedAuthenticatorData()
+ .getAttestedCredentialData().get().getAaguid().getBytes())
.build();
return reg;
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestAdminManagementFlow.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestAdminManagementFlow.java
index 9e9f190..c4a99ce 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestAdminManagementFlow.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestAdminManagementFlow.java
@@ -47,7 +47,8 @@ import net.shibboleth.shared.collection.Pair;
@ContextConfiguration(
locations = {
"classpath*:/META-INF/net.shibboleth.idp/postconfig.xml",
- "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-management.xml", },
+ "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-management.xml",
+ "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml"},
initializers = {
TestWebAuthnEnvironmentApplicationContextInitializer.class,
TestWebAuthnAdminManagementApplicationContextInitializer.class
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestPasswordlessFlow.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestPasswordlessFlow.java
index d4301a4..1de07d9 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestPasswordlessFlow.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestPasswordlessFlow.java
@@ -43,7 +43,8 @@ import net.shibboleth.shared.collection.Pair;
@ContextConfiguration(
locations = {
"classpath*:/META-INF/net.shibboleth.idp/postconfig.xml",
- "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml", },
+ "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml",
+ "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml"},
initializers = {
TestWebAuthnEnvironmentApplicationContextInitializer.class,
TestWebAuthnPasswordlessApplicationContextInitializer.class
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestRegistrationFlow.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestRegistrationFlow.java
index f629a0e..aef5fc4 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestRegistrationFlow.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestRegistrationFlow.java
@@ -52,7 +52,8 @@ import net.shibboleth.shared.collection.Pair;
@ContextConfiguration(
locations = {
"classpath*:/META-INF/net.shibboleth.idp/postconfig.xml",
- "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-registration.xml", },
+ "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-registration.xml",
+ "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml"},
initializers = {
TestWebAuthnEnvironmentApplicationContextInitializer.class,
TestWebAuthnRegistrationApplicationContextInitializer.class
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestSecondFactorFlow.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestSecondFactorFlow.java
index 4fe8b63..e230574 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestSecondFactorFlow.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestSecondFactorFlow.java
@@ -47,7 +47,8 @@ import net.shibboleth.shared.collection.Pair;
@ContextConfiguration(
locations = {
"classpath*:/META-INF/net.shibboleth.idp/postconfig.xml",
- "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml", },
+ "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml",
+ "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml"},
initializers = {
TestWebAuthnEnvironmentApplicationContextInitializer.class,
TestWebAuthn2FaApplicationContextInitializer.class
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestUsernameslessFlow.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestUsernameslessFlow.java
index ed7efea..36db8d0 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestUsernameslessFlow.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestUsernameslessFlow.java
@@ -44,7 +44,8 @@ import net.shibboleth.shared.collection.Pair;
@ContextConfiguration(
locations = {
"classpath*:/META-INF/net.shibboleth.idp/postconfig.xml",
- "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml", },
+ "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml",
+ "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml"},
initializers = {
TestWebAuthnEnvironmentApplicationContextInitializer.class,
TestWebAuthnUsernamelessApplicationContextInitializer.class
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestUsernameslessFlowWithPolicy.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestUsernameslessFlowWithPolicy.java
index 8e20440..511c082 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestUsernameslessFlowWithPolicy.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestUsernameslessFlowWithPolicy.java
@@ -29,7 +29,6 @@ import com.yubico.webauthn.data.ByteArray;
import com.yubico.webauthn.data.ClientAssertionExtensionOutputs;
import com.yubico.webauthn.data.PublicKeyCredential;
-import net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.SecondFactorOnlyAuthenticatorInspector;
import net.shibboleth.idp.plugin.authn.webauthn.context.WebAuthnAuthenticationContext;
import net.shibboleth.idp.plugin.authn.webauthn.impl.ExtractPublicKeyCredentialAssertionFromFormRequest;
import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRecord;
@@ -42,7 +41,8 @@ import net.shibboleth.shared.collection.Pair;
@ContextConfiguration(
locations = {
"classpath*:/META-INF/net.shibboleth.idp/postconfig.xml",
- "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml", },
+ "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml",
+ "classpath*:/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml"},
initializers = {
TestWebAuthnEnvironmentApplicationContextInitializer.class,
TestWebAuthnUsernamelessWithPolicyApplicationContextInitializer.class
@@ -60,13 +60,14 @@ public class TestUsernameslessFlowWithPolicy extends AbstractWebAuthnFlowTest{
super(FLOW_ID, "proceed");
}
+ /* Most the policy this is using is in the TestWebAuthnUsernamelessWithPolicyApplicationContextInitializer
+ * initializer.*/
@SuppressWarnings("null")
@Test
public void testUsernamelessFlow_CredentialRejected_2FAOnly() throws Exception {
//Register a credential for use that is only suitable for 2FA
final CredentialRecord registration =
- createCredentialRegistration(USERNAME, DISPLAY_NAME, USER_HANDLE_B64,
- Map.of(SecondFactorOnlyAuthenticatorInspector.CAPABILITY_NAME, "true"));
+ createCredentialRegistration(USERNAME, DISPLAY_NAME, USER_HANDLE_B64);
credentialRepo.addRegistrationByUsername(USERNAME, registration);
final var prc = buildProfileRequestContext(false, false, null);
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestWebAuthnUsernamelessWithPolicyApplicationContextInitializer.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestWebAuthnUsernamelessWithPolicyApplicationContextInitializer.java
index f7b9220..4a06889 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestWebAuthnUsernamelessWithPolicyApplicationContextInitializer.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/flow/TestWebAuthnUsernamelessWithPolicyApplicationContextInitializer.java
@@ -42,8 +42,8 @@ public class TestWebAuthnUsernamelessWithPolicyApplicationContextInitializer
@Override public void initialize(@Nonnull final ConfigurableApplicationContext applicationContext) {
final MockPropertySource mock = new MockPropertySource("usernameless-mock-properties");
mock.setProperty("idp.authn.webauthn.usernameless.enabled", "true");
- mock.setProperty("idp.authn.webauthn.credential.policy.enabled", "true");
- mock.setProperty("idp.authn.webauthn.registration.authenticator.policy.secondFactorOnly.enabled", "true");
+ mock.setProperty("idp.authn.webauthn.credential.policy.enabled", "true");
+ mock.setProperty("idp.authn.webauthn.authenticator.policy.secondFactorOnlyAuthenticators", "9231fe6c-def3-4856-cbc1-e940bb035246");
applicationContext.getEnvironment().getPropertySources().addFirst(mock);
log.info("Prepending usernameless properties '{}'", mock.getSource());
}
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnTest.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnTest.java
index 0542c7b..36380e6 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnTest.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/impl/AbstractWebAuthnTest.java
@@ -334,19 +334,6 @@ public abstract class AbstractWebAuthnTest {
* @throws Exception on error
*/
protected CredentialRecord createCredentialRegistration() throws Exception {
- return createCredentialRegistration(null);
- }
-
- /**
- * Create a credential registration with a new attestation response from the mock authenticator.
- *
- * @param capabilities the set of capabilities to add
- * @return the credential registration
- * @throws Exception on error
- */
- protected CredentialRecord createCredentialRegistration(final Map<String,String> capabilities)
- throws Exception {
-
final var user = UserIdentity.builder()
.name("jdoe")
.displayName("John Doe")
@@ -377,7 +364,6 @@ public abstract class AbstractWebAuthnTest {
.withCredential(credential)
.withCredentialNickname("nickname")
.withDiscoverable(Optional.of(Boolean.TRUE))
- .withAuthenticatorCapabilities(capabilities)
.withAaguid(attestation.getResponse().getParsedAuthenticatorData()
.getAttestedCredentialData().get().getAaguid().getBytes())
.withUserVerified(true)
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/ChainingCredentialPolicyRuleTest.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/ChainingCredentialPolicyRuleTest.java
index fe09d77..a522101 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/ChainingCredentialPolicyRuleTest.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/ChainingCredentialPolicyRuleTest.java
@@ -24,6 +24,7 @@ import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnTest;
import net.shibboleth.idp.plugin.authn.webauthn.policy.CredentialPolicy;
import net.shibboleth.idp.plugin.authn.webauthn.policy.CredentialPolicy.CredentialPolicyOutcome;
import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRecord;
+import net.shibboleth.idp.plugin.authn.webauthn.storage.EnhancedCredentialRecord;
import net.shibboleth.shared.collection.CollectionSupport;
import net.shibboleth.shared.component.ComponentInitializationException;
@@ -57,13 +58,14 @@ public class ChainingCredentialPolicyRuleTest extends AbstractWebAuthnTest {
}
@Override
- public CredentialPolicyOutcome evaluate(final CredentialRecord cred, final ProfileRequestContext prc) {
+ public CredentialPolicyOutcome evaluate(
+ final EnhancedCredentialRecord cred, final ProfileRequestContext prc) {
return CredentialPolicyOutcome.ACCEPT;
}
}));
policy.initialize();
- final CredentialPolicyOutcome accepted = policy.evaluate(registration, prc);
+ final CredentialPolicyOutcome accepted = policy.evaluate(new EnhancedCredentialRecord(registration), prc);
assertTrue(accepted == CredentialPolicyOutcome.ACCEPT);
}
@@ -78,13 +80,14 @@ public class ChainingCredentialPolicyRuleTest extends AbstractWebAuthnTest {
}
@Override
- public CredentialPolicyOutcome evaluate(final CredentialRecord cred, final ProfileRequestContext prc) {
+ public CredentialPolicyOutcome evaluate(
+ final EnhancedCredentialRecord cred, final ProfileRequestContext prc) {
return CredentialPolicyOutcome.REJECT;
}
}));
policy.initialize();
- final CredentialPolicyOutcome accepted = policy.evaluate(registration, prc);
+ final CredentialPolicyOutcome accepted = policy.evaluate(new EnhancedCredentialRecord(registration), prc);
assertTrue(accepted == CredentialPolicyOutcome.REJECT);
}
@@ -99,7 +102,8 @@ public class ChainingCredentialPolicyRuleTest extends AbstractWebAuthnTest {
}
@Override
- public CredentialPolicyOutcome evaluate(final CredentialRecord cred, final ProfileRequestContext prc) {
+ public CredentialPolicyOutcome evaluate(
+ final EnhancedCredentialRecord cred, final ProfileRequestContext prc) {
return CredentialPolicyOutcome.ACCEPT;
}
}, new CredentialPolicy() {
@@ -110,13 +114,14 @@ public class ChainingCredentialPolicyRuleTest extends AbstractWebAuthnTest {
}
@Override
- public CredentialPolicyOutcome evaluate(final CredentialRecord cred, final ProfileRequestContext prc) {
+ public CredentialPolicyOutcome evaluate(
+ final EnhancedCredentialRecord cred, final ProfileRequestContext prc) {
return CredentialPolicyOutcome.REJECT;
}
}));
policy.initialize();
- final CredentialPolicyOutcome accepted = policy.evaluate(registration, prc);
+ final CredentialPolicyOutcome accepted = policy.evaluate(new EnhancedCredentialRecord(registration), prc);
assertTrue(accepted == CredentialPolicyOutcome.REJECT);
}
@@ -131,13 +136,14 @@ public class ChainingCredentialPolicyRuleTest extends AbstractWebAuthnTest {
}
@Override
- public CredentialPolicyOutcome evaluate(final CredentialRecord cred, final ProfileRequestContext prc) {
+ public CredentialPolicyOutcome evaluate(
+ final EnhancedCredentialRecord cred, final ProfileRequestContext prc) {
return CredentialPolicyOutcome.IGNORE;
}
}));
policy.initialize();
- final CredentialPolicyOutcome accepted = policy.evaluate(registration, prc);
+ final CredentialPolicyOutcome accepted = policy.evaluate(new EnhancedCredentialRecord(registration), prc);
assertTrue(accepted == CredentialPolicyOutcome.ACCEPT);
}
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/SecondFactorOnlyCredentialPolicyRuleTest.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/SecondFactorOnlyCredentialPolicyRuleTest.java
deleted file mode 100644
index 07af7bc..0000000
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/policy/impl/SecondFactorOnlyCredentialPolicyRuleTest.java
+++ /dev/null
@@ -1,67 +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.policy.impl;
-
-import static org.testng.Assert.assertTrue;
-
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.SecondFactorOnlyAuthenticatorInspector;
-import net.shibboleth.idp.plugin.authn.webauthn.impl.AbstractWebAuthnTest;
-import net.shibboleth.idp.plugin.authn.webauthn.policy.CredentialPolicy.CredentialPolicyOutcome;
-import net.shibboleth.idp.plugin.authn.webauthn.storage.CredentialRecord;
-import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.component.ComponentInitializationException;
-
-/**
- * Tests for {@link SecondFactorOnlyCredentialPolicyRule}.
- */
-public class SecondFactorOnlyCredentialPolicyRuleTest extends AbstractWebAuthnTest {
-
- private SecondFactorOnlyCredentialPolicyRule policy;
-
- private CredentialRecord credential;
-
- @Override
- @BeforeMethod
- public void setup() throws Exception {
- super.setup();
- policy = new SecondFactorOnlyCredentialPolicyRule();
- policy.setId("SecondFactorOnlyPolicy");
- credential = createCredentialRegistration();
-
- }
-
- @Test
- public void testAllowed() throws ComponentInitializationException {
- policy.initialize();
-
- final CredentialPolicyOutcome accepted = policy.evaluate(credential, prc);
- assertTrue(accepted == CredentialPolicyOutcome.ACCEPT);
-
- }
-
- @Test
- public void testRejected_IsSoleFactor() throws Exception {
- credential = createCredentialRegistration(
- CollectionSupport.singletonMap(SecondFactorOnlyAuthenticatorInspector.CAPABILITY_NAME, "true"));
- policy.initialize();
-
- final CredentialPolicyOutcome accepted = policy.evaluate(credential, prc);
- assertTrue(accepted == CredentialPolicyOutcome.REJECT);
-
- }
-}
diff --git a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/storage/impl/MockAuthenticator.java b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/storage/impl/MockAuthenticator.java
index 3c38725..fd9f8eb 100644
--- a/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/storage/impl/MockAuthenticator.java
+++ b/webauthn-impl/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/storage/impl/MockAuthenticator.java
@@ -92,6 +92,9 @@ public class MockAuthenticator {
* -7 is ES256 in the IANA COSE Algorithms registry.
*/
private static final int KEY_ALGO = -7;
+
+ /** Give this authenticator its own made up AAGUID.*/
+ private static final String AUTHENTICATOR_ATTESTATION_GUID_HEX = "9231fe6cdef34856cbc1e940bb035246";
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(MockAuthenticator.class);
@@ -183,7 +186,7 @@ public class MockAuthenticator {
final CombinedKey createdKey = generateECDSA_256_P256_Key();
assert createdKey != null;
- final String aaguidHex = generateRandomIdentifierHex(32);
+ final String aaguidHex = AUTHENTICATOR_ATTESTATION_GUID_HEX; //generateRandomIdentifierHex(32);
final byte[] credentialId = generateRandomIdentifierBytes(32);
final byte[] attestationObject = createAttestationObject(createdKey, aaguidHex, credentialId);
diff --git a/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml b/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml
index b64f7ac..e8aef93 100644
--- a/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml
+++ b/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml
@@ -14,19 +14,19 @@
default-init-method="initialize"
default-destroy-method="destroy">
- <bean id="AbstractCredentialPolicyRule" scope="prototype" abstract="true"
+ <bean id="shibboleth.authn.WebAuthn.AbstractCredentialPolicyRule" scope="prototype" abstract="true"
p:fidoMetadataService="#{'false'.equals('%{idp.authn.webauthn.metadata.enabled:false}') ? null : getObject('shibboleth.authn.WebAuthn.DefaultWebAuthnFidoMetadataServiceFactory')}"/>
-
+
<!--
The default list to add credential policies used during authentication. The property
idp.authn.webauthn.credential.policy.enabled must be set to true for these to take effect.
-->
- <util:list id="shibboleth.authn.WebAuthn.ChainedCredentialPolicyList">
- <bean id="SecondFactorOnlyCredentialPolicyRule" parent="AbstractCredentialPolicyRule"
- class="net.shibboleth.idp.plugin.authn.webauthn.policy.impl.SecondFactorOnlyCredentialPolicyRule"/>
+ <util:list id="shibboleth.authn.WebAuthn.ChainedCredentialPolicyList">
+ <bean id="SecondFactorOnlyCredentialPolicyRule" parent="shibboleth.authn.WebAuthn.AbstractCredentialPolicyRule"
+ class="net.shibboleth.idp.plugin.authn.webauthn.policy.impl.SecondFactorOnlyCredentialPolicyRule"
+ p:secondFactorOnlyAuthenticators="%{idp.authn.webauthn.authenticator.policy.secondFactorOnlyAuthenticators:null}"/>
</util:list>
-
-
+
</beans>
diff --git a/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-registration.xml b/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-registration.xml
index 0dd2022..465639e 100644
--- a/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-registration.xml
+++ b/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-registration.xml
@@ -33,34 +33,4 @@
p:allowedAuthenticators="%{idp.authn.webauthn.registration.authenticator.policy.allowedAuthenticators:null}"/>
</util:list>
- <!--
- The default list to add authenticator inspectors used during registration. The property
- idp.authn.webauthn.registration.authenticator.inspector.enabled must be set to true for these to take effect.
- -->
- <util:list id="shibboleth.authn.WebAuthn.registration.ChainedAuthenticatorInspectorList">
- <bean id="shibboleth.authn.WebAuthn.registration.SecondFactorOnlyInspector" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.webauthn.admin.policy.impl.SecondFactorOnlyAuthenticatorInspector"
- p:secondFactorOnlyAuthenticators="%{idp.authn.webauthn.registration.authenticator.inspector.secondFactorOnlyAuthenticators:null}"/>
- </util:list>
-
- <util:list id="shibboleth.authn.WebAuthn.CredentialLabellerList">
-
- <bean id="CapabilitiesCredentialLabeller" scope="prototype"
- class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.AuthenticatorCapabilitiesLabeller"
- c:_0-ref="shibboleth.authn.WebAuthn.DefaultCapabilitiesToLabelsMap"/>
-
- <bean id="PasskeyCredentialLabeller" class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.PasskeyCredentialLabeller"/>
- </util:list>
-
- <util:map id="shibboleth.authn.WebAuthn.DefaultCapabilitiesToLabelsMap">
- <entry>
- <key>
- <bean parent="shibboleth.Pair"
- p:first="2faOnly" p:second="true"/>
- </key>
- <value>SecondFactorOnly</value>
- </entry>
- </util:map>
-
-
</beans>
diff --git a/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml b/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml
similarity index 60%
copy from webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml
copy to webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml
index b64f7ac..fabd555 100644
--- a/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans-authentication.xml
+++ b/webauthn-impl/src/test/resources/net/shibboleth/idp/plugin/authn/webauthn/test-beans.xml
@@ -13,20 +13,24 @@
default-init-method="initialize"
default-destroy-method="destroy">
-
- <bean id="AbstractCredentialPolicyRule" scope="prototype" abstract="true"
- p:fidoMetadataService="#{'false'.equals('%{idp.authn.webauthn.metadata.enabled:false}') ? null : getObject('shibboleth.authn.WebAuthn.DefaultWebAuthnFidoMetadataServiceFactory')}"/>
-
+
+
<!--
- The default list to add credential policies used during authentication. The property
- idp.authn.webauthn.credential.policy.enabled must be set to true for these to take effect.
-
+ The default credential labeller list. Use during registration or management flows to label credential for display
+ purposes only.
-->
- <util:list id="shibboleth.authn.WebAuthn.ChainedCredentialPolicyList">
- <bean id="SecondFactorOnlyCredentialPolicyRule" parent="AbstractCredentialPolicyRule"
- class="net.shibboleth.idp.plugin.authn.webauthn.policy.impl.SecondFactorOnlyCredentialPolicyRule"/>
+ <util:list id="shibboleth.authn.WebAuthn.CredentialLabellerList">
+
+ <bean id="CapabilitiesCredentialLabeller" scope="prototype"
+ class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.SecondFactorOnlyCredentialLabeller"
+ p:secondFactorOnlyAuthenticators="%{idp.authn.webauthn.authenticator.policy.secondFactorOnlyAuthenticators:null}"
+ p:label="SecondFactorOnly"/>
+
+ <bean id="PasskeyCredentialLabeller" class="net.shibboleth.idp.plugin.authn.webauthn.admin.impl.PasskeyCredentialLabeller"/>
</util:list>
+
+
</beans>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list