[java-idp-plugin-totp] branch main updated: JTOTP-8/9 - Full 5.0 port and CSP additions.

Scott Cantor cantor.2 at osu.edu
Thu Oct 26 16:51:18 UTC 2023


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch main
in repository java-idp-plugin-totp.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-totp.git;a=commit;h=062d3aa334ca9a4146d3806fe1a7df46a2ec225f

The following commit(s) were added to refs/heads/main by this push:
     new 062d3aa  JTOTP-8/9 - Full 5.0 port and CSP additions.
062d3aa is described below

commit 062d3aa334ca9a4146d3806fe1a7df46a2ec225f
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Thu Oct 26 12:51:15 2023 -0400

    JTOTP-8/9 - Full 5.0 port and CSP additions.
    
    https://shibboleth.atlassian.net/browse/JTOTP-8
    https://shibboleth.atlassian.net/browse/JTOTP-9
    
    Null cleanup as well.
---
 .../idp/plugin/authn/totp/TOTPPlugin.java          |  6 ++--
 .../totp/impl/AbstractTOTPExtractionAction.java    | 13 ++++---
 .../totp/impl/AttributeResolverSeedSource.java     |  5 +--
 .../totp/impl/ExtractTOTPFromFormRequest.java      |  6 ----
 .../authn/totp/impl/ExtractTOTPFromHeader.java     |  6 ----
 .../authn/totp/impl/GoogleTOTPAuthenticator.java   | 12 ++-----
 .../totp/impl/TOTPAuthenticatorArguments.java      |  3 +-
 .../authn/totp/impl/TOTPAuthenticatorCLI.java      | 17 +++++----
 .../authn/totp/impl/TOTPCredentialValidator.java   | 11 ++++--
 .../idp/flows/authn/TOTP/TOTP-authenticator.xml    |  3 --
 .../shibboleth/idp/flows/authn/TOTP/TOTP-beans.xml | 40 +++++++++++++++-------
 .../shibboleth/idp/flows/authn/TOTP/TOTP-flow.xml  |  4 ++-
 .../shibboleth/idp/plugin/authn/totp/views/totp.vm |  7 ++--
 .../totp/impl/AttributeResolverSeedSourceTest.java |  8 ++---
 .../totp/impl/ExtractTOTPFromFormRequestTest.java  | 16 ++++++---
 .../authn/totp/impl/ExtractTOTPFromHeaderTest.java | 16 ++++++---
 .../totp/impl/GoogleTOTPAuthenticatorTest.java     | 28 ++++++++-------
 17 files changed, 116 insertions(+), 85 deletions(-)

diff --git a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/TOTPPlugin.java b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/TOTPPlugin.java
index 2627f91..0309a4b 100644
--- a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/TOTPPlugin.java
+++ b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/TOTPPlugin.java
@@ -15,12 +15,12 @@
 package net.shibboleth.idp.plugin.authn.totp;
 
 import java.io.IOException;
-import java.util.Collections;
 
 import net.shibboleth.idp.module.IdPModule;
 import net.shibboleth.idp.plugin.impl.FirstPartyIdPPlugin;
 import net.shibboleth.profile.module.ModuleException;
 import net.shibboleth.profile.plugin.PluginException;
+import net.shibboleth.shared.collection.CollectionSupport;
 
 /**
  * Details about the TOTP login plugin.
@@ -37,8 +37,8 @@ public class TOTPPlugin extends FirstPartyIdPPlugin {
         super(TOTPPlugin.class);
         try {
             final IdPModule module = new TOTPModule();
-            setEnableOnInstall(Collections.singleton(module));
-            setDisableOnRemoval(Collections.singleton(module));
+            setEnableOnInstall(CollectionSupport.singleton(module));
+            setDisableOnRemoval(CollectionSupport.singleton(module));
         } catch (final IOException e) {
             throw e;
         } catch (final ModuleException e) {
diff --git a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/AbstractTOTPExtractionAction.java b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/AbstractTOTPExtractionAction.java
index 762eace..af2d23d 100644
--- a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/AbstractTOTPExtractionAction.java
+++ b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/AbstractTOTPExtractionAction.java
@@ -30,6 +30,7 @@ import net.shibboleth.shared.primitive.LoggerFactory;
 
 import org.opensaml.messaging.context.navigate.ChildContextLookup;
 import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 
@@ -58,9 +59,6 @@ public abstract class AbstractTOTPExtractionAction extends AbstractAuthenticatio
     /** Creation strategy for TOTP context. */
     @Nonnull private Function<AuthenticationContext,TOTPContext> totpContextCreationStrategy;
     
-    /** TOTP context being operated on. */
-    @Nullable private TOTPContext totpContext;
-    
     /** Constructor. */
     public AbstractTOTPExtractionAction() {
         usernameLookupStrategy = new CanonicalUsernameLookupStrategy();
@@ -97,7 +95,14 @@ public abstract class AbstractTOTPExtractionAction extends AbstractAuthenticatio
         // Clear error state.
         authenticationContext.removeSubcontext(AuthenticationErrorContext.class);
         
-        totpContext = totpContextCreationStrategy.apply(authenticationContext);
+        final TOTPContext totpContext = totpContextCreationStrategy.apply(authenticationContext);
+        if (totpContext == null) {
+            log.warn("{} Unable to create TOTP context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+            return;
+        }
+        
+        
         totpContext.setTokenCode(null);
         
         // Fill in username if not set.
diff --git a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/AttributeResolverSeedSource.java b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/AttributeResolverSeedSource.java
index 70627b4..3e6ed0a 100644
--- a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/AttributeResolverSeedSource.java
+++ b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/AttributeResolverSeedSource.java
@@ -15,7 +15,6 @@
 package net.shibboleth.idp.plugin.authn.totp.impl;
 
 import java.util.Collection;
-import java.util.Collections;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -34,6 +33,7 @@ import net.shibboleth.shared.annotation.constraint.ThreadSafeAfterInit;
 import net.shibboleth.shared.codec.Base32Support;
 import net.shibboleth.shared.codec.Base64Support;
 import net.shibboleth.shared.codec.DecodingException;
+import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
@@ -102,7 +102,7 @@ public class AttributeResolverSeedSource extends AbstractSeedSource {
 
             resCtx.setResolutionLabel("TOTP");
             resCtx.setPrincipal(totp.getUsername());
-            resCtx.setRequestedIdPAttributeNames(Collections.singletonList(attributeId));
+            resCtx.setRequestedIdPAttributeNames(CollectionSupport.singletonList(attributeId));
 
             log.debug("Resolving attribute {} for '{}'", attributeId, totp.getUsername());
 
@@ -120,6 +120,7 @@ public class AttributeResolverSeedSource extends AbstractSeedSource {
                         .map(StringAttributeValue.class::cast)
                         .map(StringAttributeValue::getValue)
                         .forEachOrdered(v -> {
+                            assert v != null;
                             try {
                                 switch (getEncoding()) {
                                     case BASE32:
diff --git a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/ExtractTOTPFromFormRequest.java b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/ExtractTOTPFromFormRequest.java
index f108def..358a011 100644
--- a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/ExtractTOTPFromFormRequest.java
+++ b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/ExtractTOTPFromFormRequest.java
@@ -17,13 +17,10 @@ package net.shibboleth.idp.plugin.authn.totp.impl;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import org.slf4j.Logger;
-
 import jakarta.servlet.http.HttpServletRequest;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
 import net.shibboleth.shared.primitive.StringSupport;
 
 /**
@@ -34,9 +31,6 @@ public class ExtractTOTPFromFormRequest extends AbstractTOTPExtractionAction {
     /** Default token code field name. */
     @Nonnull @NotEmpty public static final String DEFAULT_FIELD_NAME = "tokencode";
     
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(ExtractTOTPFromFormRequest.class);
-    
     /** Name of header. */
     @NonnullAfterInit @NotEmpty private String fieldName;
     
diff --git a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/ExtractTOTPFromHeader.java b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/ExtractTOTPFromHeader.java
index 0d1b6b1..15cae81 100644
--- a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/ExtractTOTPFromHeader.java
+++ b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/ExtractTOTPFromHeader.java
@@ -17,13 +17,10 @@ package net.shibboleth.idp.plugin.authn.totp.impl;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import org.slf4j.Logger;
-
 import jakarta.servlet.http.HttpServletRequest;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
 import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
 import net.shibboleth.shared.primitive.StringSupport;
 
 /**
@@ -34,9 +31,6 @@ public class ExtractTOTPFromHeader extends AbstractTOTPExtractionAction {
     /** Default token code header. */
     @Nonnull @NotEmpty public static final String DEFAULT_HEADER_NAME = "X-Shibboleth-TOTP";
     
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(ExtractTOTPFromHeader.class);
-    
     /** Name of header. */
     @NonnullAfterInit @NotEmpty private String headerName;
     
diff --git a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/GoogleTOTPAuthenticator.java b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/GoogleTOTPAuthenticator.java
index 53ca279..3a44734 100644
--- a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/GoogleTOTPAuthenticator.java
+++ b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/GoogleTOTPAuthenticator.java
@@ -20,8 +20,6 @@ import java.util.Collection;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import org.slf4j.Logger;
-
 import com.google.common.net.UrlEscapers;
 import com.warrenstrange.googleauth.GoogleAuthenticator;
 import com.warrenstrange.googleauth.GoogleAuthenticatorConfig;
@@ -37,7 +35,6 @@ import net.shibboleth.shared.codec.EncodingException;
 import net.shibboleth.shared.component.AbstractInitializableComponent;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
 import net.shibboleth.shared.primitive.StringSupport;
 
 /**
@@ -46,9 +43,6 @@ import net.shibboleth.shared.primitive.StringSupport;
 @ThreadSafeAfterInit
 public class GoogleTOTPAuthenticator extends AbstractInitializableComponent implements TOTPAuthenticator {
     
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(GoogleAuthenticator.class);
-    
     /** Google Authenticator config. **/
     @NonnullAfterInit private GoogleAuthenticatorConfig authconfig;
 
@@ -105,11 +99,11 @@ public class GoogleTOTPAuthenticator extends AbstractInitializableComponent impl
             }
             
             return new TOTPCredential() {
-                public byte[] getKey() {
+                @Nonnull public byte[] getKey() {
                     return secret;
                 }
                 
-                public String getTOTPURL() {
+                @Nonnull public String getTOTPURL() {
                     final String label;
                     if (trimmedName != null) {
                         if (trimmedIssuer != null) {
@@ -133,7 +127,7 @@ public class GoogleTOTPAuthenticator extends AbstractInitializableComponent impl
                     return url.toString();
                 }
     
-                public Collection<Integer> getScratchCodes() {
+                @Nonnull public Collection<Integer> getScratchCodes() {
                     return cred.getScratchCodes();
                 }
             };
diff --git a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/TOTPAuthenticatorArguments.java b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/TOTPAuthenticatorArguments.java
index 9c302f7..c6b0635 100644
--- a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/TOTPAuthenticatorArguments.java
+++ b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/TOTPAuthenticatorArguments.java
@@ -63,6 +63,7 @@ public class TOTPAuthenticatorArguments extends AbstractIdPHomeAwareCommandLineA
         if (log == null) {
             log = LoggerFactory.getLogger(TOTPAuthenticatorArguments.class);
         }
+        assert log != null;
         return log;
     }
     
@@ -129,7 +130,7 @@ public class TOTPAuthenticatorArguments extends AbstractIdPHomeAwareCommandLineA
     }
 
     /** {@inheritDoc} */
-    public void printHelp(final PrintStream out) {
+    public void printHelp(@Nonnull final PrintStream out) {
         out.println("TOTPAuthenticatorCLI");
         out.println("Provides a command line interface for TOTPAuthenticator operations.");
         out.println();
diff --git a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/TOTPAuthenticatorCLI.java b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/TOTPAuthenticatorCLI.java
index 7f9dd74..17c79ec 100644
--- a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/TOTPAuthenticatorCLI.java
+++ b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/TOTPAuthenticatorCLI.java
@@ -21,7 +21,6 @@ import org.slf4j.Logger;
 
 import com.google.common.net.UrlEscapers;
 
-import net.shibboleth.idp.Version;
 import net.shibboleth.idp.cli.AbstractIdPHomeAwareCommandLine;
 import net.shibboleth.idp.plugin.authn.totp.impl.TOTPAuthenticator.TOTPCredential;
 import net.shibboleth.shared.annotation.constraint.NotEmpty;
@@ -42,6 +41,7 @@ public class TOTPAuthenticatorCLI extends AbstractIdPHomeAwareCommandLine<TOTPAu
         if (log == null) {
             log = LoggerFactory.getLogger(TOTPAuthenticatorCLI.class);
         }
+        assert log != null;
         return log;
     }
     
@@ -54,7 +54,7 @@ public class TOTPAuthenticatorCLI extends AbstractIdPHomeAwareCommandLine<TOTPAu
     /** {@inheritDoc} */
     @Override
     @Nonnull @NotEmpty protected String getVersion() {
-        return Version.getVersion();
+        return getClass().getPackage().getImplementationVersion();
     }
     
     /** {@inheritDoc} */
@@ -67,15 +67,18 @@ public class TOTPAuthenticatorCLI extends AbstractIdPHomeAwareCommandLine<TOTPAu
         
         try {
             final TOTPAuthenticator authenticator;
-            if (args.getAuthenticatorName() != null) {
-                authenticator = getApplicationContext().getBean(args.getAuthenticatorName(), TOTPAuthenticator.class);
+            final String authenticatorName = args.getAuthenticatorName();
+            if (authenticatorName != null) {
+                authenticator = getApplicationContext().getBean(authenticatorName, TOTPAuthenticator.class);
             } else {
                 authenticator = getApplicationContext().getBean(TOTPAuthenticator.class);
             }
             
-            if (args.getSeed() != null && args.getTokenCode() != null) {
-                
-                if (authenticator.validate(args.getSeed(), args.getTokenCode())) {
+            final byte[] seed = args.getSeed();
+            final Integer tokenCode = args.getTokenCode();
+            if (seed != null && tokenCode != null) {
+
+                if (authenticator.validate(seed, tokenCode)) {
                     System.out.println("OK");
                     return RC_OK;
                 }
diff --git a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/TOTPCredentialValidator.java b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/TOTPCredentialValidator.java
index b82aadf..ca0353b 100644
--- a/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/TOTPCredentialValidator.java
+++ b/totp-impl/src/main/java/net/shibboleth/idp/plugin/authn/totp/impl/TOTPCredentialValidator.java
@@ -177,8 +177,11 @@ public class TOTPCredentialValidator extends AbstractCredentialValidator {
         log.debug("{} Attempting to authenticate token code for '{}' ", getLogPrefix(), totpContext.getUsername());
         
         try {
+            final Integer tokenCode = totpContext.getTokenCode();
+            // Checked above.
+            assert tokenCode != null;
             if (totpContext.getTokenSeeds().stream().anyMatch(
-                    seed -> authenticator.validate(seed, totpContext.getTokenCode()))) {
+                    seed -> authenticator.validate(seed, tokenCode))) {
                 log.info("{} Login by '{}' succeeded", getLogPrefix(), totpContext.getUsername());
                 return populateSubject(new Subject(), profileRequestContext, totpContext);
             }
@@ -208,7 +211,11 @@ public class TOTPCredentialValidator extends AbstractCredentialValidator {
     @Nonnull protected Subject populateSubject(@Nonnull final Subject subject,
             @Nonnull final ProfileRequestContext profileRequestContext,
             @Nonnull final TOTPContext totpContext) {
-        subject.getPrincipals().add(new TOTPPrincipal(totpContext.getUsername()));
+        
+        final String username = totpContext.getUsername();
+        // Checked earlier.
+        assert username != null;
+        subject.getPrincipals().add(new TOTPPrincipal(username));
         
         // Bypass c14n. We already operate on a canonical name, so just re-confirm it.
         profileRequestContext.ensureSubcontext(SubjectCanonicalizationContext.class).setPrincipalName(
diff --git a/totp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/TOTP/TOTP-authenticator.xml b/totp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/TOTP/TOTP-authenticator.xml
index e8ee79c..b3cbe1a 100644
--- a/totp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/TOTP/TOTP-authenticator.xml
+++ b/totp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/TOTP/TOTP-authenticator.xml
@@ -12,9 +12,6 @@
        default-init-method="initialize"
        default-destroy-method="destroy">
 
-    <bean class="net.shibboleth.ext.spring.config.IdentifiableBeanPostProcessor" />
-    <bean class="net.shibboleth.idp.profile.impl.ProfileActionBeanPostProcessor" />
-
     <bean id="DefaultAuthenticator" class="net.shibboleth.idp.plugin.authn.totp.impl.GoogleTOTPAuthenticator" lazy-init="true" />
 
 </beans>
diff --git a/totp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/TOTP/TOTP-beans.xml b/totp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/TOTP/TOTP-beans.xml
index 548082b..640ff7c 100644
--- a/totp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/TOTP/TOTP-beans.xml
+++ b/totp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/TOTP/TOTP-beans.xml
@@ -19,21 +19,19 @@
         p:basenames="classpath:/net/shibboleth/idp/plugin/authn/totp/messages"
         p:defaultEncoding="UTF-8" />
 
-    <bean id="InternalHttpServletRequest" class="net.shibboleth.utilities.java.support.net.ThreadLocalHttpServletRequestProxy" />
-
     <!-- Default username comes from previous c14n or session. -->        
     <bean id="DefaultUsernameLookupStrategy"
         class="net.shibboleth.idp.session.context.navigate.CanonicalUsernameLookupStrategy" />
 
     <bean id="ExtractTOTPFromHeader"
         class="net.shibboleth.idp.plugin.authn.totp.impl.ExtractTOTPFromHeader" scope="prototype"
-        p:httpServletRequest-ref="InternalHttpServletRequest"
+        p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
         p:usernameLookupStrategy="#{getObject('shibboleth.authn.TOTP.UsernameLookupStrategy') ?: getObject('DefaultUsernameLookupStrategy')}"
         p:headerName="#{'%{idp.authn.TOTP.headerName:X-Shibboleth-TOTP}'.trim()}" />
 
     <bean id="ExtractTOTPFromFormRequest"
         class="net.shibboleth.idp.plugin.authn.totp.impl.ExtractTOTPFromFormRequest" scope="prototype"
-        p:httpServletRequest-ref="InternalHttpServletRequest"
+        p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
         p:usernameLookupStrategy="#{getObject('shibboleth.authn.TOTP.UsernameLookupStrategy') ?: getObject('DefaultUsernameLookupStrategy')}"
         p:fieldName="#{'%{idp.authn.TOTP.fieldName:tokencode}'.trim()}" />
             
@@ -42,17 +40,9 @@
         p:validators="#{getObject('shibboleth.authn.TOTP.Validator') ?: getObject('DefaultTOTPValidator')}"
         p:resultCachingPredicate="#{getObject('shibboleth.authn.TOTP.resultCachingPredicate')}"
         p:addDefaultPrincipals="%{idp.authn.TOTP.addDefaultPrincipals:true}"
-        p:classifiedMessages="#{getObject('shibboleth.authn.TOTP.ClassifiedMessageMap') ?: getObject('DefaultTOTPClassifiedMessageMap')}"
+        p:classifiedMessages="#{getObject('shibboleth.authn.TOTP.ClassifiedMessageMap')}"
         p:lockoutManager="#{getObject('shibboleth.authn.TOTP.AccountLockoutManager')}" />
     
-    <util:map id="DefaultTOTPClassifiedMessageMap">
-        <entry key="InvalidCredentials">
-            <list>
-                <value>InvalidCredentials</value>
-            </list>
-        </entry>
-    </util:map>
-    
     <!-- These are singletons acting as default "back-ends". -->
     
     <bean id="DefaultTOTPValidator" class="net.shibboleth.idp.plugin.authn.totp.impl.TOTPCredentialValidator" lazy-init="true"
@@ -63,4 +53,28 @@
         p:attributeResolver-ref="shibboleth.AttributeResolverService"
         p:sourceAttribute="#{'%{idp.authn.TOTP.tokenSeedAttribute:tokenSeeds}'.trim()}" />
 
+    <!-- Used in views to calculate CSP hashes and nonces, remove and adjust beans in flow when compatibility bumped past 5.0 -->
+    
+    <bean id="TOTPCSPDigester" class="net.shibboleth.shared.codec.StringDigester"
+        c:algorithm="SHA256" c:format="BASE64" />
+
+    <bean id="TOTPCSPNonce" destroy-method=""
+            class="net.shibboleth.shared.security.IdentifierGenerationStrategy" factory-method="getInstance">
+        <constructor-arg>
+            <util:constant
+                static-field="net.shibboleth.shared.security.IdentifierGenerationStrategy.ProviderType.SECURE" />
+        </constructor-arg>
+        <constructor-arg>
+            <bean class="net.shibboleth.shared.security.RandomIdentifierParameterSpec"
+                    c:identifierSize="16">
+                <constructor-arg name="source">
+                    <null/>
+                </constructor-arg>
+                <constructor-arg name="identifierEncoder">
+                    <null/>
+                </constructor-arg>
+            </bean>
+        </constructor-arg>
+    </bean>
+
 </beans>
diff --git a/totp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/TOTP/TOTP-flow.xml b/totp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/TOTP/TOTP-flow.xml
index f912c4c..18c4884 100644
--- a/totp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/TOTP/TOTP-flow.xml
+++ b/totp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/TOTP/TOTP-flow.xml
@@ -27,7 +27,9 @@
             <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="flowRequestContext.getActiveFlow().getApplicationContext().getBean('TOTPCSPDigester')" result="viewScope.cspDigester" />
+            <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('TOTPCSPNonce')" result="viewScope.cspNonce" />
+            <evaluate expression="T(net.shibboleth.shared.codec.HTMLEncoder)" result="viewScope.encoder" />
 			<evaluate expression="flowRequestContext.getExternalContext().getNativeRequest()" result="viewScope.request" />
 			<evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()" result="viewScope.response" />
 			<evaluate
diff --git a/totp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/totp/views/totp.vm b/totp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/totp/views/totp.vm
index 68bbab9..ec1a68e 100644
--- a/totp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/totp/views/totp.vm
+++ b/totp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/totp/views/totp.vm
@@ -11,6 +11,8 @@
 ## authenticationWarningContext - context with login warning state
 ## rpUIContext - the context with SP UI information from the metadata
 ## encoder - HTMLEncoder class
+## cspDigester - Calculates base64-encoded SHA-2 hashes (call apply)
+## cspNonce - Calculates secure nonces (call generateIdentifier)
 ## request - HttpServletRequest
 ## response - HttpServletResponse
 ## environment - Spring Environment object for property resolution
@@ -18,6 +20,8 @@
 ##
 #set ($rpContext = $profileRequestContext.getSubcontext('net.shibboleth.idp.profile.context.RelyingPartyContext'))
 ##
+#set ($onClick = "this.childNodes[0].nodeValue='#springMessageText('idp.login.pleasewait', 'Logging in, please wait...')'")
+$response.addHeader("Content-Security-Policy", "script-src-attr 'unsafe-hashes' 'sha256-$cspDigester.apply($onClick)'")
 <!DOCTYPE html>
 <html>
     <head>
@@ -70,8 +74,7 @@
                     <div class="grid">
                         <div class="grid-item">
                             <button type="submit" name="_eventId_proceed"
-                                onClick="this.childNodes[0].nodeValue='#springMessageText("idp.login.pleasewait", "Logging in, please wait...")'"
-                                >#springMessageText("idp.login.login", "Login")</button>
+                                onClick="$onClick">#springMessageText("idp.login.login", "Login")</button>
                         </div>
                     </div>
                 </form>
diff --git a/totp-impl/src/test/java/net/shibboleth/idp/plugin/authn/totp/impl/AttributeResolverSeedSourceTest.java b/totp-impl/src/test/java/net/shibboleth/idp/plugin/authn/totp/impl/AttributeResolverSeedSourceTest.java
index 305bbc4..f25bfd2 100644
--- a/totp-impl/src/test/java/net/shibboleth/idp/plugin/authn/totp/impl/AttributeResolverSeedSourceTest.java
+++ b/totp-impl/src/test/java/net/shibboleth/idp/plugin/authn/totp/impl/AttributeResolverSeedSourceTest.java
@@ -17,7 +17,6 @@ package net.shibboleth.idp.plugin.authn.totp.impl;
 import static org.testng.Assert.*;
 
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
@@ -38,6 +37,7 @@ import net.shibboleth.idp.plugin.authn.totp.context.TOTPContext;
 import net.shibboleth.idp.profile.testing.RequestContextBuilder;
 import net.shibboleth.shared.codec.Base32Support;
 import net.shibboleth.shared.codec.EncodingException;
+import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.component.ComponentInitializationException;
 import net.shibboleth.shared.testing.MockReloadableService;
 
@@ -54,7 +54,7 @@ public class AttributeResolverSeedSourceTest {
         final String two = Base32Support.encode("two".getBytes(), false);
         
         final IdPAttribute single = new IdPAttribute("single");
-        single.setValues(Collections.singletonList(StringAttributeValue.valueOf(one)));
+        single.setValues(CollectionSupport.singletonList(StringAttributeValue.valueOf(one)));
         final AttributeDefinition singledef = new MockAttributeDefinition("single", single);
         singledef.initialize();
 
@@ -142,8 +142,8 @@ public class AttributeResolverSeedSourceTest {
         final AttributeResolverImpl result = new AttributeResolverImpl();
         result.setId("test");
         
-        result.setAttributeDefinitions(definitions == null ? Collections.emptyList() : definitions);
-        result.setDataConnectors(connectors == null ? Collections.emptyList() : connectors);
+        result.setAttributeDefinitions(definitions == null ? CollectionSupport.emptyList() : definitions);
+        result.setDataConnectors(connectors == null ? CollectionSupport.emptyList() : connectors);
         result.initialize();
         
         return result;
diff --git a/totp-impl/src/test/java/net/shibboleth/idp/plugin/authn/totp/impl/ExtractTOTPFromFormRequestTest.java b/totp-impl/src/test/java/net/shibboleth/idp/plugin/authn/totp/impl/ExtractTOTPFromFormRequestTest.java
index 6c5f262..abba6f8 100644
--- a/totp-impl/src/test/java/net/shibboleth/idp/plugin/authn/totp/impl/ExtractTOTPFromFormRequestTest.java
+++ b/totp-impl/src/test/java/net/shibboleth/idp/plugin/authn/totp/impl/ExtractTOTPFromFormRequestTest.java
@@ -70,27 +70,33 @@ public class ExtractTOTPFromFormRequestTest extends BaseAuthenticationContextTes
     }
 
     @Test public void testWrongField() throws Exception {
-        ((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("Bar", "123456");
+        if (action.getHttpServletRequest() instanceof MockHttpServletRequest mock) {
+            mock.addParameter("Bar", "123456");
+        }
         
         final Event event = action.execute(src);
         ActionTestingSupport.assertEvent(event, AuthnEventIds.NO_CREDENTIALS);
     }
 
     @Test public void testInvalidFormat() throws Exception {
-        ((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("Foo", "A123456");
+        if (action.getHttpServletRequest() instanceof MockHttpServletRequest mock) {
+            mock.addParameter("Foo", "A123456");
+        }
         
         final Event event = action.execute(src);
         ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_CREDENTIALS);
     }
 
     @Test public void testValid() throws Exception {
-        ((MockHttpServletRequest) action.getHttpServletRequest()).addParameter("Foo", "123456");
+        if (action.getHttpServletRequest() instanceof MockHttpServletRequest mock) {
+            mock.addParameter("Foo", "123456");
+        }
         
         final Event event = action.execute(src);
         ActionTestingSupport.assertProceedEvent(event);
-        final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
+        final AuthenticationContext authCtx = prc.ensureSubcontext(AuthenticationContext.class);
         final TOTPContext totpCtx = authCtx.getSubcontext(TOTPContext.class);
-        Assert.assertNotNull(totpCtx);
+        assert totpCtx != null;
         Assert.assertEquals(totpCtx.getUsername(), "jdoe");
         Assert.assertEquals(totpCtx.getTokenCode(), Integer.valueOf(123456));
     }
diff --git a/totp-impl/src/test/java/net/shibboleth/idp/plugin/authn/totp/impl/ExtractTOTPFromHeaderTest.java b/totp-impl/src/test/java/net/shibboleth/idp/plugin/authn/totp/impl/ExtractTOTPFromHeaderTest.java
index 753220a..f483a6d 100644
--- a/totp-impl/src/test/java/net/shibboleth/idp/plugin/authn/totp/impl/ExtractTOTPFromHeaderTest.java
+++ b/totp-impl/src/test/java/net/shibboleth/idp/plugin/authn/totp/impl/ExtractTOTPFromHeaderTest.java
@@ -70,27 +70,33 @@ public class ExtractTOTPFromHeaderTest extends BaseAuthenticationContextTest {
     }
 
     @Test public void testWrongHeader() {
-        ((MockHttpServletRequest) action.getHttpServletRequest()).addHeader("Foo", "123456");
+        if (action.getHttpServletRequest() instanceof MockHttpServletRequest mock) {
+            mock.addHeader("Foo", "123456");
+        }
         
         final Event event = action.execute(src);
         ActionTestingSupport.assertEvent(event, AuthnEventIds.NO_CREDENTIALS);
     }
 
     @Test public void testInvalidFormat() {
-        ((MockHttpServletRequest) action.getHttpServletRequest()).addHeader("X-Foo", "A123456");
+        if (action.getHttpServletRequest() instanceof MockHttpServletRequest mock) {
+            mock.addHeader("X-Foo", "A123456");
+        }
         
         final Event event = action.execute(src);
         ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_CREDENTIALS);
     }
 
     @Test public void testValid() {
-        ((MockHttpServletRequest) action.getHttpServletRequest()).addHeader("X-Foo", "123456");
+        if (action.getHttpServletRequest() instanceof MockHttpServletRequest mock) {
+            mock.addHeader("X-Foo", "123456");
+        }
         
         final Event event = action.execute(src);
         ActionTestingSupport.assertProceedEvent(event);
-        final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
+        final AuthenticationContext authCtx = prc.ensureSubcontext(AuthenticationContext.class);
         final TOTPContext totpCtx = authCtx.getSubcontext(TOTPContext.class);
-        Assert.assertNotNull(totpCtx);
+        assert totpCtx != null;
         Assert.assertEquals(totpCtx.getUsername(), "jdoe");
         Assert.assertEquals(totpCtx.getTokenCode(), Integer.valueOf(123456));
     }
diff --git a/totp-impl/src/test/java/net/shibboleth/idp/plugin/authn/totp/impl/GoogleTOTPAuthenticatorTest.java b/totp-impl/src/test/java/net/shibboleth/idp/plugin/authn/totp/impl/GoogleTOTPAuthenticatorTest.java
index 18993f9..ed4ec8f 100644
--- a/totp-impl/src/test/java/net/shibboleth/idp/plugin/authn/totp/impl/GoogleTOTPAuthenticatorTest.java
+++ b/totp-impl/src/test/java/net/shibboleth/idp/plugin/authn/totp/impl/GoogleTOTPAuthenticatorTest.java
@@ -22,6 +22,7 @@ import java.util.regex.Pattern;
 
 import javax.security.auth.login.LoginException;
 
+import net.shibboleth.idp.authn.AuthenticationResult;
 import net.shibboleth.idp.authn.AuthnEventIds;
 import net.shibboleth.idp.authn.context.AuthenticationContext;
 import net.shibboleth.idp.authn.context.AuthenticationErrorContext;
@@ -76,7 +77,7 @@ public class GoogleTOTPAuthenticatorTest extends BaseAuthenticationContextTest {
     }
 
     @Test public void testMissingContext() throws ComponentInitializationException {
-        prc.getSubcontext(AuthenticationContext.class).setAttemptedFlow(authenticationFlows.get(0));
+        prc.ensureSubcontext(AuthenticationContext.class).setAttemptedFlow(authenticationFlows.get(0));
         
         validator.initialize();
         action.initialize();
@@ -86,7 +87,7 @@ public class GoogleTOTPAuthenticatorTest extends BaseAuthenticationContextTest {
     }
 
     @Test public void testMissingUser() throws ComponentInitializationException {
-        final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
+        final AuthenticationContext ac = prc.ensureSubcontext(AuthenticationContext.class);
         ac.setAttemptedFlow(authenticationFlows.get(0));
         ac.ensureSubcontext(TOTPContext.class);
         
@@ -99,7 +100,7 @@ public class GoogleTOTPAuthenticatorTest extends BaseAuthenticationContextTest {
 
 
     @Test public void testMissingCode() throws ComponentInitializationException {
-        final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
+        final AuthenticationContext ac = prc.ensureSubcontext(AuthenticationContext.class);
         ac.setAttemptedFlow(authenticationFlows.get(0));
         ac.ensureSubcontext(TOTPContext.class).setUsername("foo").getTokenSeeds().add("foo".getBytes());
         
@@ -111,7 +112,7 @@ public class GoogleTOTPAuthenticatorTest extends BaseAuthenticationContextTest {
     }
 
     @Test public void testMissingSeeds() throws ComponentInitializationException {
-        final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
+        final AuthenticationContext ac = prc.ensureSubcontext(AuthenticationContext.class);
         ac.setAttemptedFlow(authenticationFlows.get(0));
         ac.ensureSubcontext(TOTPContext.class).setUsername("foo").setTokenCode(123456);
         
@@ -123,7 +124,7 @@ public class GoogleTOTPAuthenticatorTest extends BaseAuthenticationContextTest {
     }
 
     @Test public void testUnmatchedUser() throws Exception {
-        final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
+        final AuthenticationContext ac = prc.ensureSubcontext(AuthenticationContext.class);
         ac.setAttemptedFlow(authenticationFlows.get(0));
         ac.ensureSubcontext(TOTPContext.class).setUsername("bar").setTokenCode(123456).getTokenSeeds().add("foo".getBytes());
         
@@ -138,7 +139,7 @@ public class GoogleTOTPAuthenticatorTest extends BaseAuthenticationContextTest {
     }
 
     @Test public void testInvalidSeed() throws ComponentInitializationException {
-        final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
+        final AuthenticationContext ac = prc.ensureSubcontext(AuthenticationContext.class);
         ac.setAttemptedFlow(authenticationFlows.get(0));
         ac.ensureSubcontext(TOTPContext.class).setUsername("foo").setTokenCode(123456).getTokenSeeds().add("foo".getBytes());
         
@@ -147,12 +148,13 @@ public class GoogleTOTPAuthenticatorTest extends BaseAuthenticationContextTest {
 
         final Event event = action.execute(src);
         ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_CREDENTIALS);
-        AuthenticationErrorContext errorCtx = ac.getSubcontext(AuthenticationErrorContext.class);
+        final AuthenticationErrorContext errorCtx = ac.getSubcontext(AuthenticationErrorContext.class);
+        assert errorCtx != null;
         Assert.assertTrue(errorCtx.getExceptions().get(0) instanceof LoginException);
     }
 
     @Test public void testInvalidCode() throws ComponentInitializationException, DecodingException {
-        final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
+        final AuthenticationContext ac = prc.ensureSubcontext(AuthenticationContext.class);
         ac.setAttemptedFlow(authenticationFlows.get(0));
         ac.ensureSubcontext(TOTPContext.class).setUsername("foo").setTokenCode(123456).getTokenSeeds().add(
                 Base32Support.decode("G24YUKCHHXRDWCPR"));
@@ -162,12 +164,13 @@ public class GoogleTOTPAuthenticatorTest extends BaseAuthenticationContextTest {
 
         final Event event = action.execute(src);
         ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_CREDENTIALS);
-        AuthenticationErrorContext errorCtx = ac.getSubcontext(AuthenticationErrorContext.class);
+        final AuthenticationErrorContext errorCtx = ac.getSubcontext(AuthenticationErrorContext.class);
+        assert errorCtx != null;
         Assert.assertTrue(errorCtx.getExceptions().get(0) instanceof LoginException);
     }
 
     @Test public void testSuccess() throws ComponentInitializationException, DecodingException {
-        final AuthenticationContext ac = prc.getSubcontext(AuthenticationContext.class);
+        final AuthenticationContext ac = prc.ensureSubcontext(AuthenticationContext.class);
         ac.setAttemptedFlow(authenticationFlows.get(0));
 
         final GoogleAuthenticator auth = new GoogleAuthenticator();
@@ -184,8 +187,9 @@ public class GoogleTOTPAuthenticatorTest extends BaseAuthenticationContextTest {
         final Event event = action.execute(src);
         ActionTestingSupport.assertProceedEvent(event);
         
-        Assert.assertNotNull(ac.getAuthenticationResult());
-        Assert.assertEquals(ac.getAuthenticationResult().getSubject().getPrincipals(TOTPPrincipal.class).iterator()
+        final AuthenticationResult result = ac.getAuthenticationResult();
+        assert result != null;
+        Assert.assertEquals(result.getSubject().getPrincipals(TOTPPrincipal.class).iterator()
                 .next().getName(), "foo");
     }
 

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


More information about the commits mailing list