<div dir="ltr">To correct myself, getValues returns a List .. but size() is still the method that provides "how many"..<div><br></div><div>Steve.</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, Sep 6, 2024 at 6:55 AM Steven Premeau <<a href="mailto:steven.premeau@maine.edu">steven.premeau@maine.edu</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">Two comments:<div><br></div><div>  In my experience, comment lines do not count when exceptions are reported in inlineScripts, so the number returned is likely earlier than the actual line that caused the exception.</div><div><br></div><div>  Your only reference to length is " if (attribute != null && attribute.getValues().length()" a few lines below ... since getValues() returns a Map of values, I believe you need to use size() rather than length() in that test... </div><div><br></div><div>Steve.</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, Sep 6, 2024 at 5:52 AM Francesco Malvezzi via users <<a href="mailto:users@shibboleth.net" target="_blank">users@shibboleth.net</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">hi everybody,<br>
<br>
I am facing a runtime exception in a MFA flow.<br>
<br>
My idea is to setup a MFA flow where, after the authn/Password, user is <br>
prompted the authn/TOTP according to the presence/absence of a user <br>
attribute (the TOTP seed in the directory, by the way).<br>
<br>
This is my $IDP_HOME/conf/authn/mfa-authn-config.xml<br>
<br>
<?xml version="1.0" encoding="UTF-8"?><br>
<beans xmlns="<a href="http://www.springframework.org/schema/beans" rel="noreferrer" target="_blank">http://www.springframework.org/schema/beans</a>"<br>
        xmlns:context="<a href="http://www.springframework.org/schema/context" rel="noreferrer" target="_blank">http://www.springframework.org/schema/context</a>"<br>
        xmlns:util="<a href="http://www.springframework.org/schema/util" rel="noreferrer" target="_blank">http://www.springframework.org/schema/util</a>"<br>
        xmlns:p="<a href="http://www.springframework.org/schema/p" rel="noreferrer" target="_blank">http://www.springframework.org/schema/p</a>"<br>
        xmlns:c="<a href="http://www.springframework.org/schema/c" rel="noreferrer" target="_blank">http://www.springframework.org/schema/c</a>"<br>
        xmlns:xsi="<a href="http://www.w3.org/2001/XMLSchema-instance" rel="noreferrer" target="_blank">http://www.w3.org/2001/XMLSchema-instance</a>"<br>
        xsi:schemaLocation="<a href="http://www.springframework.org/schema/beans" rel="noreferrer" target="_blank">http://www.springframework.org/schema/beans</a> <br>
<a href="http://www.springframework.org/schema/beans/spring-beans.xsd" rel="noreferrer" target="_blank">http://www.springframework.org/schema/beans/spring-beans.xsd</a><br>
<br>
<a href="http://www.springframework.org/schema/context" rel="noreferrer" target="_blank">http://www.springframework.org/schema/context</a> <br>
<a href="http://www.springframework.org/schema/context/spring-context.xsd" rel="noreferrer" target="_blank">http://www.springframework.org/schema/context/spring-context.xsd</a><br>
                            <a href="http://www.springframework.org/schema/util" rel="noreferrer" target="_blank">http://www.springframework.org/schema/util</a> <br>
<a href="http://www.springframework.org/schema/util/spring-util.xsd" rel="noreferrer" target="_blank">http://www.springframework.org/schema/util/spring-util.xsd</a>"<br>
<br>
        default-init-method="initialize"<br>
        default-destroy-method="destroy"><br>
<br>
     <util:map id="shibboleth.authn.MFA.TransitionMap"><br>
         <!-- Run authn/Flow1 first. --><br>
         <entry key=""><br>
             <bean parent="shibboleth.authn.MFA.Transition" <br>
p:nextFlow="authn/Password" /><br>
         </entry><br>
<br>
         <!--<br>
         Second rule runs a function if authn/Flow1 succeeds, to <br>
determine whether an additional<br>
         factor is required.<br>
         --><br>
         <entry key="authn/Password"><br>
             <bean parent="shibboleth.authn.MFA.Transition" <br>
p:nextFlowStrategy-ref="checkSecondFactor" /><br>
         </entry><br>
<br>
         <!-- An implicit final rule will return whatever the second <br>
flow returns. --><br>
     </util:map><br>
<br>
     <!-- Example script to see if second factor is required. --><br>
     <bean id="checkSecondFactor" <br>
parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript"<br>
           p:customObject-ref="shibboleth.AttributeResolverService"><br>
         <constructor-arg><br>
             <value><br>
                 <![CDATA[<br>
             nextFlow = "authn/TOTP";<br>
<br>
             // Go straight to second factor if we have to, or set up <br>
for an attribute lookup first.<br>
             authCtx = <br>
input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");<br>
             mfaCtx = <br>
authCtx.getSubcontext("net.shibboleth.idp.authn.context.MultiFactorAuthenticationContext");<br>
             if (mfaCtx.isAcceptable()) {<br>
                 // Attribute check is required to decide if first <br>
factor alone is enough.<br>
                 resCtx = input.getSubcontext(<br>
<br>
"net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext", <br>
true);<br>
<br>
                                // Look up the username<br>
                 usernameLookupStrategyClass = <br>
Java.type("net.shibboleth.idp.session.context.navigate.CanonicalUsernameLookupStrategy");<br>
                 usernameLookupStrategy = new usernameLookupStrategyClass();<br>
                 resCtx.setPrincipal(usernameLookupStrategy.apply(input));<br>
<br>
<br>
                                // resolve the attribute to determine if a first factor is sufficient<br>
<br>
resCtx.getRequestedIdPAttributeNames().add("unimoreTotpSeed");<br>
                 resCtx.resolveAttributes(custom);<br>
<br>
                 // Check for an attribute value that authorizes use of <br>
first factor.<br>
                 attribute = <br>
resCtx.getResolvedIdPAttributes().get("unimoreTotpSeed");<br>
                 //valueType = <br>
Java.type("net.shibboleth.idp.attribute.StringAttributeValue");<br>
                 if (attribute != null && attribute.getValues().length() <br>
 > 0) {<br>
                         nextFlow = "authn/TOTP";<br>
                     }<br>
                     else<br>
                     {<br>
                         nextFlow = null;<br>
                     }<br>
<br>
                 input.removeSubcontext(resCtx);   // cleanup<br>
             }<br>
<br>
             nextFlow;   // pass control to second factor or end with <br>
the first<br>
         ]]><br>
             </value><br>
         </constructor-arg><br>
     </bean><br>
</beans><br>
<br>
Note that it has been lifted from:<br>
<a href="https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199505534/MultiFactorAuthnConfiguration" rel="noreferrer" target="_blank">https://shibboleth.atlassian.net/wiki/spaces/IDP5/pages/3199505534/MultiFactorAuthnConfiguration</a><br>
only 4 lines have been changed (flow names and the attribute name).<br>
Please also note I didn't even change the comments from the example. <br>
They are now somewhat misleading.<br>
<br>
In the logs I read (idp-process.log):<br>
2024-09-06 11:21:21,158 - 155.185.3.38 - DEBUG <br>
[net.shibboleth.idp.authn.AbstractSubjectCanonicalizationAction:225] - <br>
Profile Action SimpleSubjectCanonicalization: trimming whitespace of <br>
input string 'this-user'<br>
2024-09-06 11:21:21,159 - 155.185.3.38 - DEBUG <br>
[net.shibboleth.idp.authn.impl.TransitionMultiFactorAuthentication:195] <br>
- Profile Action TransitionMultiFactorAuthentication: Preserving <br>
authentication result from 'authn/Password' flow<br>
2024-09-06 11:21:21,160 - 155.185.3.38 - DEBUG <br>
[net.shibboleth.idp.authn.impl.TransitionMultiFactorAuthentication:212] <br>
- Profile Action TransitionMultiFactorAuthentication: Applying MFA <br>
transition rule to exit state 'authn/Password'<br>
2024-09-06 11:21:21,309 - 155.185.3.38 - ERROR <br>
[net.shibboleth.idp.authn:35] - Uncaught runtime exception<br>
java.lang.NullPointerException: Cannot invoke <br>
"java.lang.CharSequence.length()" because "this.text" is null<br>
         at <br>
java.base/java.util.regex.Matcher.getTextLength(Matcher.java:1769)<br>
2024-09-06 11:21:21,321 - 155.185.3.38 - WARN <br>
[org.opensaml.profile.action.impl.LogEvent:94] - A non-proceed event <br>
occurred while processing the request: RuntimeException<br>
<br>
the idp-warn.log confirms:<br>
<br>
2024-09-06 10:42:00,753 - 155.185.3.38 - ERROR <br>
[net.shibboleth.idp.authn:35] - Uncaught runtime exception<br>
java.lang.NullPointerException: Cannot invoke <br>
"java.lang.CharSequence.length()" because "this.text" is null<br>
         at <br>
java.base/java.util.regex.Matcher.getTextLength(Matcher.java:1769)<br>
         at java.base/java.util.regex.Matcher.reset(Matcher.java:415)<br>
         at java.base/java.util.regex.Matcher.<init>(Matcher.java:252)<br>
         at java.base/java.util.regex.Pattern.matcher(Pattern.java:1134)<br>
         at com.google.common.base.JdkPattern.matcher(JdkPattern.java:36)<br>
         at <br>
com.google.common.base.Predicates$ContainsPatternPredicate.apply(Predicates.java:662)<br>
         at <br>
com.google.common.base.Predicates$ContainsPatternPredicate.apply(Predicates.java:651)<br>
         at com.google.common.base.Predicate.test(Predicate.java:79)<br>
         at <br>
net.shibboleth.shared.logic.StrategyIndirectedPredicate.test(StrategyIndirectedPredicate.java:74)<br>
         at <br>
java.base/java.util.function.Predicate.lambda$negate$1(Predicate.java:80)<br>
         at <br>
net.shibboleth.idp.attribute.resolver.AbstractResolverPlugin.resolve(AbstractResolverPlugin.java:174)<br>
         at <br>
net.shibboleth.idp.attribute.resolver.impl.AttributeResolverImpl.resolveDataConnector(AttributeResolverImpl.java:400)<br>
         at <br>
net.shibboleth.idp.attribute.resolver.impl.AttributeResolverImpl.resolveDependencies(AttributeResolverImpl.java:453)<br>
         at <br>
net.shibboleth.idp.attribute.resolver.impl.AttributeResolverImpl.resolveAttributeDefinition(AttributeResolverImpl.java:328)<br>
         at <br>
net.shibboleth.idp.attribute.resolver.impl.AttributeResolverImpl.resolveAttributes(AttributeResolverImpl.java:244)<br>
         at <br>
net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext.resolveAttributes(AttributeResolutionContext.java:334)<br>
         at <br>
org.openjdk.nashorn.internal.scripts.Script$\^eval\_.:program(<eval>:19)<br>
         at <br>
org.openjdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:646)<br>
[...]<br>
<br>
line n# 19 is:<br>
resCtx.resolveAttributes(custom);<br>
<br>
I already checked the obvious:<br>
1) attribute-resolver loads without errors;<br>
2) aacli.sh works and resolves the TOTP seed attribute.<br>
<br>
$ ./bin/version.sh<br>
5.1.3<br>
$ sudo ./bin/plugin.sh -l<br>
INFO  - Including auto-located properties in ./bin/../conf/ldap.properties<br>
INFO  - Including auto-located properties in <br>
./bin/../conf/saml-nameid.properties<br>
INFO  - Including auto-located properties in <br>
./bin/../conf/c14n/subject-c14n.properties<br>
INFO  - Including auto-located properties in <br>
./bin/../conf/services.properties<br>
INFO  - Including auto-located properties in <br>
./bin/../conf/authn/SpidExternal.properties<br>
INFO  - Including auto-located properties in <br>
./bin/../conf/authn/X509External.properties<br>
INFO  - Including auto-located properties in <br>
./bin/../conf/authn/authn.properties<br>
INFO  - Including auto-located properties in <br>
./bin/../conf/admin/admin.properties<br>
Plugin: net.shibboleth.idp.plugin.nashorn       Current Version: 2.0.0<br>
Plugin: net.shibboleth.idp.plugin.authn.totp    Current Version: 2.1.0<br>
<br>
Of course I am not looking in the right place. What did i forget?<br>
<br>
(and sorry for the very lenghty post),<br>
<br>
Francesco<br>
-- <br>
For Consortium Member technical support, see <a href="https://shibboleth.atlassian.net/wiki/x/ZYEpPw" rel="noreferrer" target="_blank">https://shibboleth.atlassian.net/wiki/x/ZYEpPw</a><br>
To unsubscribe from this list send an email to <a href="mailto:users-unsubscribe@shibboleth.net" target="_blank">users-unsubscribe@shibboleth.net</a><br>
</blockquote></div><br clear="all"><div><br></div><span class="gmail_signature_prefix">-- </span><br><div dir="ltr" class="gmail_signature"><div dir="ltr"><div style="color:rgb(136,136,136);font-size:12.8px">Steven Premeau, <span style="font-size:12.8px">Director of Enterprise Systems Architecture & Administration</span></div><div style="color:rgb(136,136,136);font-size:12.8px"><span style="font-size:12.8px">University of Maine System:</span> Information Technology</div><div style="color:rgb(136,136,136);font-size:12.8px"><br></div></div></div>
</blockquote></div><br clear="all"><div><br></div><span class="gmail_signature_prefix">-- </span><br><div dir="ltr" class="gmail_signature"><div dir="ltr"><div style="color:rgb(136,136,136);font-size:12.8px">Steven Premeau, <span style="font-size:12.8px">Director of Enterprise Systems Architecture & Administration</span></div><div style="color:rgb(136,136,136);font-size:12.8px"><span style="font-size:12.8px">University of Maine System:</span> Information Technology</div><div style="color:rgb(136,136,136);font-size:12.8px"><br></div></div></div>