[java-idp-plugin-webauthn] branch main updated: Improve credential registration builder

Phil Smart philip.smart at jisc.ac.uk
Wed Aug 7 16:52:01 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=056e50df02df014885f7366f854870bcea0f8314

The following commit(s) were added to refs/heads/main by this push:
     new 056e50d  Improve credential registration builder
056e50d is described below

commit 056e50df02df014885f7366f854870bcea0f8314
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Wed Aug 7 17:51:59 2024 +0100

    Improve credential registration builder
---
 .../authn/webauthn/admin/RegistrationResult.java   |  2 +-
 .../webauthn/storage/CredentialRegistration.java   | 85 ++++++++++++++++++----
 .../storage/CredentialRegistrationTest.java        | 58 +++++++++++++++
 3 files changed, 131 insertions(+), 14 deletions(-)

diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/RegistrationResult.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/RegistrationResult.java
index bb5d822..b4f5d88 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/RegistrationResult.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/admin/RegistrationResult.java
@@ -266,7 +266,7 @@ public class RegistrationResult {
         public IBuildStage withCredential(
                 @Nonnull final PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> 
                 cred) {
-            this.credential = Constraint.isNotNull(cred, "Credential can not be null");
+            this.credential = Constraint.isNotNull(cred, "Credential cannot be null");
             return this;
         }
 
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/CredentialRegistration.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/CredentialRegistration.java
index e65b8d7..546ee0b 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/CredentialRegistration.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/CredentialRegistration.java
@@ -43,6 +43,7 @@ import com.yubico.webauthn.data.UserIdentity;
 import net.shibboleth.shared.annotation.constraint.NonnullElements;
 import net.shibboleth.shared.annotation.constraint.Unmodifiable;
 import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.logic.Constraint;
 
 /**
  * Registration record used to hold registered credentials.
@@ -87,7 +88,6 @@ public class CredentialRegistration {
      *
      * @param builder the builder
      */
-    //TODO look at nullable here.
     private CredentialRegistration(final Builder builder) {
         this.userIdentity = builder.userIdentity;
         this.transports = builder.transports;
@@ -147,7 +147,9 @@ public class CredentialRegistration {
      */
     @JsonIgnore
     @Nonnull public String getUsername() {
-        return userIdentity.getName();
+        final String name = userIdentity.getName();
+        assert name != null;
+        return name;
     }
 
     /**
@@ -211,7 +213,7 @@ public class CredentialRegistration {
      */
     @JsonIgnore
     @Nullable public String getAuthenticatorDescription() {
-        if (attestationMetadata != null && !attestationMetadata.isEmpty()) {
+        if (!attestationMetadata.isEmpty()) {
             final Optional<Optional<String>> descriptionFound = attestationMetadata.stream()
                 .filter(mtd -> mtd.getMetadataStatement().isPresent())
                 .map(mtd -> mtd.getMetadataStatement().get().getDescription()).findFirst();
@@ -234,7 +236,7 @@ public class CredentialRegistration {
      */
     @JsonIgnore
     @Nullable public String getIcon() {
-        if (attestationMetadata != null && !attestationMetadata.isEmpty()) {
+        if (!attestationMetadata.isEmpty()) {
             final Optional<Optional<String>> iconFound = attestationMetadata.stream()
                 .filter(mtd -> mtd.getMetadataStatement().isPresent())
                 .map(mtd -> mtd.getMetadataStatement().get().getIcon()).findFirst();
@@ -313,35 +315,86 @@ public class CredentialRegistration {
 
     /** Builder stage.*/
     public interface IUserIdentityStage {
+        /**
+         * The users identity. 
+         * @param userIdentity the users identity
+         * @return the next builder stage
+         */
         @Nonnull public ITransportsStage withUserIdentity(@Nonnull final UserIdentity userIdentity);
     }
 
     /** Builder stage.*/
     public interface ITransportsStage {
+        /**
+         * Set the {@link AuthenticatorTransport transports} the authenticator can
+         * use to communicate with the client.
+         * 
+         * @param transports the transports
+         * @return the next builder stage
+         */
         @Nonnull public IRegistrationTimeStage withTransports(@Nonnull SortedSet<AuthenticatorTransport> transports);
     }
 
     /** Builder stage.*/
     public interface IRegistrationTimeStage {
+        /**
+         * Set the time the registration took place
+         * 
+         * @param registrationTime the registration time
+         * @return the next builder stage
+         */
         @Nonnull public ICredentialStage withRegistrationTime(@Nonnull final Instant registrationTime);
     }
 
     /** Builder stage.*/
     public interface ICredentialStage {
+        /**
+         * Set the credential to register.
+         * 
+         * @param credential the credential
+         * @return the next builder stage
+         */
         @Nonnull public IBuildStage withCredential(@Nonnull final RegisteredCredential credential);
     }
 
     /** Builder stage.*/
     public interface IBuildStage {
+        /**
+         * Set an optional nickname of the credential.
+         * 
+         * @param credentialNickname the nickname
+         * @return the next builder stage
+         */
         @Nonnull public IBuildStage withCredentialNickname(@Nullable final String credentialNickname);
 
+        /**
+         * Is the credential a discoverable type (passkey). Empty if not known.
+         * 
+         * @param discoverable is the credential a discoverable type (passkey). Empty if not known
+         * @return the next builder stage
+         */
         @Nonnull public IBuildStage withDiscoverable(@Nonnull final Optional<Boolean> discoverable);
 
+        /**
+         * Set the optional attestation metadata about the authenticator.
+         * 
+         * @param attestationMetadata the metadata
+         * @return the next builder stage
+         */
         @Nonnull public IBuildStage withAttestationMetadata(
                 @Nonnull final Set<MetadataBLOBPayloadEntry> attestationMetadata);
 
+        /**
+         * Was the user verified during registration?
+         * 
+         * @param userVerified was the user verified during registration
+         * @return the next builder stage
+         */
         @Nonnull public IBuildStage withUserVerified(boolean userVerified);
 
+        /**
+         * Build this credential registration
+         */
         @Nonnull public CredentialRegistration build();
     }
 
@@ -349,16 +402,17 @@ public class CredentialRegistration {
     @JsonPOJOBuilder(buildMethodName = "build", withPrefix = "with")
     public static final class Builder
             implements IUserIdentityStage, ITransportsStage, IRegistrationTimeStage, ICredentialStage, IBuildStage {
-        private UserIdentity userIdentity;
-        private SortedSet<AuthenticatorTransport> transports;
-        private Instant registrationTime;
-        private RegisteredCredential credential;
+        @Nonnull private UserIdentity userIdentity;
+        @Nonnull private SortedSet<AuthenticatorTransport> transports;
+        @Nonnull private Instant registrationTime;
+        @Nonnull private RegisteredCredential credential;
         @Nullable private String credentialNickname;
         @Nonnull private Optional<Boolean> discoverable;
         @Nonnull private Set<MetadataBLOBPayloadEntry> attestationMetadata;
         private boolean userVerified;
 
         /** Constructor.*/
+        @SuppressWarnings("null")
         private Builder() {
             // Create empty, corresponds to 'unknown'
             discoverable = Optional.empty();            
@@ -370,7 +424,7 @@ public class CredentialRegistration {
         @Override
         @JsonProperty("userIdentity")
         @Nonnull public ITransportsStage withUserIdentity(@Nonnull final UserIdentity user) {
-            userIdentity = user;
+            userIdentity = Constraint.isNotNull(user, "UserIdentity can not be null");
             return this;
         }
 
@@ -378,21 +432,25 @@ public class CredentialRegistration {
         @JsonProperty("transports")
         @Nonnull public IRegistrationTimeStage withTransports(@Nonnull
                 final SortedSet<AuthenticatorTransport> authenticatorTransports) {
-            transports = authenticatorTransports;
+            if (authenticatorTransports == null) {
+                transports = Collections.emptySortedSet();
+            } else {
+                transports = authenticatorTransports;
+            }
             return this;
         }
 
         @Override
         @JsonProperty("registrationTime")
         @Nonnull public ICredentialStage withRegistrationTime(@Nonnull final Instant time) {
-            registrationTime = time;
+            registrationTime = Constraint.isNotNull(time, "Registration time can not be null");
             return this;
         }
 
         @Override
         @JsonProperty("credential")
         @Nonnull public IBuildStage withCredential(@Nonnull final RegisteredCredential cred) {
-            credential = cred;
+            credential = Constraint.isNotNull(cred, "Credential can not be null");
             return this;
         }
 
@@ -412,7 +470,8 @@ public class CredentialRegistration {
 
         @Override
         @JsonProperty("attestationMetadata")
-        @Nonnull public IBuildStage withAttestationMetadata(@Nonnull final Set<MetadataBLOBPayloadEntry> attestationMtd) {
+        @Nonnull public IBuildStage withAttestationMetadata(
+                @Nonnull final Set<MetadataBLOBPayloadEntry> attestationMtd) {
             attestationMetadata = attestationMtd;
             return this;
         }
diff --git a/webauthn-api/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/storage/CredentialRegistrationTest.java b/webauthn-api/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/storage/CredentialRegistrationTest.java
new file mode 100644
index 0000000..8b87609
--- /dev/null
+++ b/webauthn-api/src/test/java/net/shibboleth/idp/plugin/authn/webauthn/storage/CredentialRegistrationTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.storage;
+
+import java.time.Instant;
+import java.util.TreeSet;
+
+import org.testng.annotations.Test;
+
+import com.yubico.webauthn.data.AuthenticatorTransport;
+import com.yubico.webauthn.data.ByteArray;
+import com.yubico.webauthn.data.UserIdentity;
+
+import net.shibboleth.shared.logic.ConstraintViolationException;
+
+/**
+ * Tests for {@link CredentialRegistration}
+ */
+public class CredentialRegistrationTest {
+    
+    private CredentialRegistration credReg;
+    
+    @SuppressWarnings("null")
+    @Test(expectedExceptions = ConstraintViolationException.class)
+    public void testCredentialRegistrationBuilder_NullIdentity() {
+        CredentialRegistration.builder()
+        .withUserIdentity(null)
+        .withTransports(new TreeSet<AuthenticatorTransport>())
+        .withRegistrationTime(Instant.now())
+        .withCredential(null).build();
+    }
+    
+    @SuppressWarnings("null")
+    @Test(expectedExceptions = ConstraintViolationException.class)
+    public void testCredentialRegistrationBuilder_NullCredential() {
+        CredentialRegistration.builder()
+        .withUserIdentity(UserIdentity.builder().name("test")
+                .displayName("person")
+                .id(ByteArray.fromBase64("OjdT61K6rono3rZCXCRnSQ=="))
+                .build())
+        .withTransports(new TreeSet<AuthenticatorTransport>())
+        .withRegistrationTime(Instant.now())
+        .withCredential(null).build();
+    }
+
+}

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


More information about the commits mailing list