[java-idp-plugin-webauthn] branch main updated: Improve view

Phil Smart philip.smart at jisc.ac.uk
Tue Jan 30 10:31:50 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=276e7cd324395d197c13b62083297f2fa65fd0c0

The following commit(s) were added to refs/heads/main by this push:
     new 276e7cd  Improve view
276e7cd is described below

commit 276e7cd324395d197c13b62083297f2fa65fd0c0
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Tue Jan 30 11:31:44 2024 +0100

    Improve view
---
 .../webauthn/storage/CredentialRegistration.java   | 13 ---
 .../StorageServiceCredentialRepository.java        |  6 +-
 .../authn/webauthn/impl/CredentialEncoder.java     | 94 ++++++++++++++++++++++
 .../webauthn-registration-flow.xml                 |  6 +-
 .../authn/webauthn/views/webauthn-register.vm      |  7 +-
 .../views/webauthn-registration-outcomes.vm        |  7 +-
 6 files changed, 111 insertions(+), 22 deletions(-)

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 5338c77..126a0b9 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
@@ -22,7 +22,6 @@ import java.util.Collections;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.SortedSet;
-import java.util.stream.Collectors;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -103,12 +102,6 @@ public class CredentialRegistration {
         return discoverable;
     }
     
-    //TODO replace with view encoder?
-    @JsonIgnore
-    @Nonnull public String getDiscoverableAsString() {
-        return discoverable.isEmpty() ? "unknown" : Boolean.toString(discoverable.get());
-    }
-
     @JsonGetter("nickname")
     public String getNickname() {
         return credentialNickname;
@@ -144,12 +137,6 @@ public class CredentialRegistration {
     public String getCredentialIdBase64Url() {
         return credential.getCredentialId().getBase64Url();
     }
-
-    @JsonIgnore
-    public String getTransportsString() {
-        return transports.stream().map(AuthenticatorTransport::getId).collect(Collectors.joining(","));
-    }
-
     /**
      * Convert the credential registration into a {@link PublicKeyCredentialDescriptor}.
      * 
diff --git a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/StorageServiceCredentialRepository.java b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/StorageServiceCredentialRepository.java
index fe572f1..a1f6e88 100644
--- a/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/StorageServiceCredentialRepository.java
+++ b/webauthn-api/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/storage/StorageServiceCredentialRepository.java
@@ -22,6 +22,9 @@ import javax.annotation.Nonnull;
 import com.yubico.webauthn.CredentialRepository;
 import com.yubico.webauthn.data.ByteArray;
 
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+
 /**
  * An extension of the {@link CredentialRepository} interface to support additional read and add operations.
  * 
@@ -36,6 +39,7 @@ public interface StorageServiceCredentialRepository extends CredentialRepository
      * 
      * @return the set of registered credentials associated to the user
      */
+    @Nonnull @NonnullElements @NotLive 
     Set<CredentialRegistration> getRegistrationsByUsername(@Nonnull final String username);
 
     /**
@@ -56,7 +60,7 @@ public interface StorageServiceCredentialRepository extends CredentialRepository
      * 
      * @return the credential if found, otherwise an empty {@link Optional}.
      */
-    Optional<CredentialRegistration> getRegistrationByUsernameAndCredentialId(@Nonnull final String username,
+    @Nonnull Optional<CredentialRegistration> getRegistrationByUsernameAndCredentialId(@Nonnull final String username,
             @Nonnull final ByteArray id);
 
     /**
diff --git a/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/CredentialEncoder.java b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/CredentialEncoder.java
new file mode 100644
index 0000000..735ff9d
--- /dev/null
+++ b/webauthn-impl/src/main/java/net/shibboleth/idp/plugin/authn/webauthn/impl/CredentialEncoder.java
@@ -0,0 +1,94 @@
+/*
+ * 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.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.yubico.webauthn.RegisteredCredential;
+import com.yubico.webauthn.data.AuthenticatorTransport;
+
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+
+/**
+ * Encoder for {@link RegisteredCredential credentials} that converts raw values into a format suitable for views.
+ */
+public final class CredentialEncoder {
+   
+    /** Private constructor. */
+    private CredentialEncoder() {
+        
+    }
+    
+    /**
+     * Format the given instant into a display friendly string.
+     * 
+     * @param time the instant to encode
+     * 
+     * @return a formatted time string
+     */
+    @Nonnull @NotEmpty public static String formatInstant(@Nullable final Instant time) {
+        if (time == null) {
+            return "unknown";
+        }
+        try {
+            final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'hh:mm")
+                    .withZone(ZoneId.systemDefault());
+            final String isoFormat = formatter.format(time);
+            assert isoFormat != null;
+            return isoFormat; 
+        } catch (final Exception e) {
+            return "unknown";
+        }
+    }
+    
+    /**
+     * Format the isDiscoverable flag for display. Can be used to determine if the credential is a 'passkey'. That is, 
+     * either true, false, or unknown if empty.
+     * 
+     * @param discoverable the isDiscoverable flag
+     * 
+     * @return is the credential discoverable (a 'passkey' or resident key) true, false, or unknown
+     */
+    @Nonnull public static String formatDiscoverable(@Nullable final Optional<Boolean> discoverable) {
+        if (discoverable == null) {
+            return "not-set";
+        }
+        return discoverable.isEmpty() ? "unknown" : Boolean.toString(discoverable.get());
+    }
+    
+    /**
+     * Convert a set of {@link AuthenticatorTransport transports} into a CSV string.
+     * 
+     * @param transports the transports to convert
+     * 
+     * @return the CSV string
+     */
+    public static String formatTransports(@Nullable final Set<AuthenticatorTransport> transports) {
+        if (transports == null) {
+            return "";
+        }
+        
+        return transports.stream().map(AuthenticatorTransport::getId).collect(Collectors.joining(","));
+    }
+
+}
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 f2517d3..cd89004 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
@@ -45,7 +45,8 @@
             <evaluate expression="authenticationContext.getSubcontext(T(net.shibboleth.idp.ui.context.RelyingPartyUIContext))" result="viewScope.rpUIContext" />
             <evaluate expression="authenticationContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationErrorContext))" result="viewScope.authenticationErrorContext" />
             <evaluate expression="authenticationContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationWarningContext))" result="viewScope.authenticationWarningContext" />
-            <evaluate expression="T(net.shibboleth.utilities.java.support.codec.HTMLEncoder)" result="viewScope.encoder" />
+            <evaluate expression="T(net.shibboleth.shared.codec.HTMLEncoder)" result="viewScope.encoder" />
+            <evaluate expression="T(net.shibboleth.idp.plugin.authn.webauthn.impl.CredentialEncoder)" result="viewScope.credentialEncoder"/>
             <evaluate expression="flowRequestContext.getExternalContext().getNativeRequest()" result="viewScope.request" />
             <evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()" result="viewScope.response" />
         </on-render>
@@ -90,7 +91,8 @@
             <evaluate expression="authenticationContext.getSubcontext(T(net.shibboleth.idp.ui.context.RelyingPartyUIContext))" result="viewScope.rpUIContext" />
             <evaluate expression="authenticationContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationErrorContext))" result="viewScope.authenticationErrorContext" />
             <evaluate expression="authenticationContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationWarningContext))" result="viewScope.authenticationWarningContext" />
-            <evaluate expression="T(net.shibboleth.utilities.java.support.codec.HTMLEncoder)" result="viewScope.encoder" />
+            <evaluate expression="T(net.shibboleth.shared.codec.HTMLEncoder)" result="viewScope.encoder" />
+            <evaluate expression="T(net.shibboleth.idp.plugin.authn.webauthn.impl.CredentialEncoder)" result="viewScope.credentialEncoder"/>
             <evaluate expression="flowRequestContext.getExternalContext().getNativeRequest()" result="viewScope.request" />
             <evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()" result="viewScope.response" />
         </on-render>
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm
index 50b6bf2..716bd21 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-register.vm
@@ -12,6 +12,7 @@
 ## authenticationWarningContext - context with login warning state
 ## rpUIContext - the context with SP UI information from the metadata
 ## encoder - HTMLEncoder class
+## credentialEncoder - CredentialEncoder class
 ## request - HttpServletRequest
 ## response - HttpServletResponse
 ## environment - Spring Environment object for property resolution
@@ -116,9 +117,9 @@
                              #foreach($cred in $webauthnRegContext.existingCredentials)
                                 <tr>
                                     <td>$encoder.encodeForHTML($cred.nickname)</td>
-                                    <td>$encoder.encodeForHTML($cred.transports)</td>
-                                    <td>$encoder.encodeForHTML($cred.discoverableAsString)</td>
-                                    <td>$encoder.encodeForHTML($cred.registrationTime)</td>
+                                    <td>$encoder.encodeForHTML($credentialEncoder.formatTransports($cred.transports))</td>
+                                    <td>$encoder.encodeForHTML($credentialEncoder.formatDiscoverable($cred.isDiscoverable()))</td>
+                                    <td>$encoder.encodeForHTML($credentialEncoder.formatInstant($cred.registrationTime))</td>
                                     <td>
                                     <form id="deleteKeyForm" action="$flowExecutionUrl" method="post">
                                         #parse("csrf/csrf.vm")
diff --git a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-registration-outcomes.vm b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-registration-outcomes.vm
index 5167bfc..ae53893 100644
--- a/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-registration-outcomes.vm
+++ b/webauthn-impl/src/main/resources/net/shibboleth/idp/plugin/authn/webauthn/views/webauthn-registration-outcomes.vm
@@ -12,6 +12,7 @@
 ## authenticationWarningContext - context with login warning state
 ## rpUIContext - the context with SP UI information from the metadata
 ## encoder - HTMLEncoder class
+## credentialEncoder - CredentialEncoder class
 ## request - HttpServletRequest
 ## response - HttpServletResponse
 ## environment - Spring Environment object for property resolution
@@ -64,10 +65,10 @@
                         #foreach($cred in $webauthnRegContext.existingCredentials)
                         <tr>
                             <td>$encoder.encodeForHTML($cred.nickname)</td>
-                            <td>$encoder.encodeForHTML($cred.transportsString)</td>
-                            <td>$encoder.encodeForHTML($cred.discoverableAsString)</td>
+                            <td>$encoder.encodeForHTML($credentialEncoder.formatTransports($cred.transports))</td>
+                            <td>$encoder.encodeForHTML($credentialEncoder.formatDiscoverable($cred.isDiscoverable()))</td>
                             <td>$encoder.encodeForHTML($cred.userVerified)</td>
-                            <td>$encoder.encodeForHTML($cred.registrationTime)</td>
+                            <td>$encoder.encodeForHTML($credentialEncoder.formatInstant($cred.registrationTime))</td>
                         </tr>
                         #end
                     </table>

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


More information about the commits mailing list