I’m currently working on a project that is using the now deprecated security subsystem in Wildfly. I wanted to migrate towards elytron security and a JDBC bcrypt realm. I followed some instructions on how to do the migration. After performing these instructions the login keeps failing. I tried to debug my problem by setting the log levels of all involved sytems to ALL.
/subsystem=logging/logger=org.jboss.security:add(level=ALL)
/subsystem=logging/logger=org.jboss.as.security:add(level=ALL)
/subsystem=logging/logger=org.picketbox:add(level=ALL)
/subsystem=logging/logger=org.jboss.as.domain.management.security:add(level=ALL)
/subsystem=logging/logger=org.wildfly.security:add(level=ALL)
/subsystem=logging/logger=org.wildfly.elytron:add(level=ALL)
/subsystem=logging/console-handler=CONSOLE:write-attribute(name=level, value=ALL)
Due to my investigation this logs should also log the principal query that is fired against the defined datasource like select password from USERS where username = <userName>. In my case this log messaage does not appear. Instead an exception is thrown saying
024-07-17 13:18:31,408 DEBUG [org.jboss.security] (default task-1) PBOX00206: Login failure: javax.security.auth.login.FailedLoginException: PBOX00070: Password invalid/Password required
at org.picketbox//org.jboss.security.auth.spi.UsernamePasswordLoginModule.login(UsernamePasswordLoginModule.java:286)
at [email protected]//org.jboss.as.security.RealmDirectLoginModule.login(RealmDirectLoginModule.java:157)
at java.base/javax.security.auth.login.LoginContext.invoke(LoginContext.java:726)
at java.base/javax.security.auth.login.LoginContext$4.run(LoginContext.java:665)
at java.base/javax.security.auth.login.LoginContext$4.run(LoginContext.java:663)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:663)
at java.base/javax.security.auth.login.LoginContext.login(LoginContext.java:574)
at org.picketbox//org.jboss.security.authentication.JBossCachedAuthenticationManager.defaultLogin(JBossCachedAuthenticationManager.java:406)
at org.picketbox//org.jboss.security.authentication.JBossCachedAuthenticationManager.proceedWithJaasLogin(JBossCachedAuthenticationManager.java:345)
at org.picketbox//org.jboss.security.authentication.JBossCachedAuthenticationManager.authenticate(JBossCachedAuthenticationManager.java:323)
at org.picketbox//org.jboss.security.authentication.JBossCachedAuthenticationManager.isValid(JBossCachedAuthenticationManager.java:146)
at [email protected]//org.wildfly.extension.undertow.security.JAASIdentityManagerImpl.verifyCredential(JAASIdentityManagerImpl.java:123)
at [email protected]//org.wildfly.extension.undertow.security.JAASIdentityManagerImpl.verify(JAASIdentityManagerImpl.java:94)
at [email protected]//io.undertow.security.impl.SecurityContextImpl.login(SecurityContextImpl.java:198)
at [email protected]//io.undertow.servlet.spec.HttpServletRequestImpl.login(HttpServletRequestImpl.java:482)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.$$YJP$$invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at [email protected]//org.jboss.resteasy.core.ContextParameterInjector$GenericDelegatingProxy.invoke(ContextParameterInjector.java:79)
at [email protected]//com.sun.proxy.$Proxy187.login(Unknown Source)
at deployment.deployment.war//de.akad.oc.rest.resources.authentication.AuthenticationResource.login(AuthenticationResource.java:108)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.$$YJP$$invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at [email protected]//org.jboss.as.ee.component.ManagedReferenceMethodInterceptor.processInvocation(ManagedReferenceMethodInterceptor.java:52)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.invocation.InterceptorContext$Invocation.proceed(InterceptorContext.java:509)
at [email protected]//org.jboss.as.weld.interceptors.Jsr299BindingsInterceptor.delegateInterception(Jsr299BindingsInterceptor.java:79)
at [email protected]//org.jboss.as.weld.interceptors.Jsr299BindingsInterceptor.doMethodInterception(Jsr299BindingsInterceptor.java:89)
at [email protected]//org.jboss.as.weld.interceptors.Jsr299BindingsInterceptor.processInvocation(Jsr299BindingsInterceptor.java:102)
at [email protected]//org.jboss.as.ee.component.interceptors.UserInterceptorFactory$1.processInvocation(UserInterceptorFactory.java:63)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.component.invocationmetrics.ExecutionTimeInterceptor.processInvocation(ExecutionTimeInterceptor.java:43)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.jpa.interceptor.SBInvocationInterceptor.processInvocation(SBInvocationInterceptor.java:47)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ee.concurrent.ConcurrentContextInterceptor.processInvocation(ConcurrentContextInterceptor.java:45)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.invocation.InitialInterceptor.processInvocation(InitialInterceptor.java:40)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:53)
at [email protected]//org.jboss.as.ee.component.interceptors.ComponentDispatcherInterceptor.processInvocation(ComponentDispatcherInterceptor.java:52)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.component.pool.PooledInstanceInterceptor.processInvocation(PooledInstanceInterceptor.java:51)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.component.interceptors.AdditionalSetupInterceptor.processInvocation(AdditionalSetupInterceptor.java:54)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInOurTx(CMTTxInterceptor.java:237)
at [email protected]//org.jboss.as.ejb3.tx.CMTTxInterceptor.required(CMTTxInterceptor.java:362)
at [email protected]//org.jboss.as.ejb3.tx.CMTTxInterceptor.processInvocation(CMTTxInterceptor.java:144)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.invocation.InterceptorContext$Invocation.proceed(InterceptorContext.java:509)
at [email protected]//org.jboss.weld.module.ejb.AbstractEJBRequestScopeActivationInterceptor.aroundInvoke(AbstractEJBRequestScopeActivationInterceptor.java:72)
at [email protected]//org.jboss.as.weld.ejb.EjbRequestScopeActivationInterceptor.processInvocation(EjbRequestScopeActivationInterceptor.java:89)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.component.interceptors.CurrentInvocationContextInterceptor.processInvocation(CurrentInvocationContextInterceptor.java:41)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.component.invocationmetrics.WaitTimeInterceptor.processInvocation(WaitTimeInterceptor.java:47)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.security.SecurityContextInterceptor.processInvocation(SecurityContextInterceptor.java:100)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.deployment.processors.StartupAwaitInterceptor.processInvocation(StartupAwaitInterceptor.java:22)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.component.interceptors.ShutDownInterceptorFactory$1.processInvocation(ShutDownInterceptorFactory.java:64)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.component.interceptors.LoggingInterceptor.processInvocation(LoggingInterceptor.java:67)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ee.component.NamespaceContextInterceptor.processInvocation(NamespaceContextInterceptor.java:50)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.invocation.ContextClassLoaderInterceptor.processInvocation(ContextClassLoaderInterceptor.java:60)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.invocation.InterceptorContext.run(InterceptorContext.java:438)
at [email protected]//org.wildfly.security.manager.WildFlySecurityManager.doChecked(WildFlySecurityManager.java:618)
at [email protected]//org.jboss.invocation.AccessCheckingInterceptor.processInvocation(AccessCheckingInterceptor.java:57)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:53)
at [email protected]//org.jboss.as.ee.component.ViewService$View.invoke(ViewService.java:198)
at [email protected]//org.jboss.as.ee.component.ViewDescription$1.processInvocation(ViewDescription.java:185)
at [email protected]//org.jboss.as.ee.component.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:81)
at deployment.deployment.war//de.akad.oc.rest.resources.authentication.AuthenticationResource$$$view217.login(Unknown Source)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.$$YJP$$invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at [email protected]//org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:138)
at [email protected]//org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:517)
at [email protected]//org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:406)
at [email protected]//org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$0(ResourceMethodInvoker.java:370)
at [email protected]//org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:355)
at [email protected]//org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:372)
at [email protected]//org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:344)
at [email protected]//org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:317)
at [email protected]//org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:440)
at [email protected]//org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:229)
at [email protected]//org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:135)
at [email protected]//org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:355)
at [email protected]//org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:138)
at [email protected]//org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:215)
at [email protected]//org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:227)
at [email protected]//org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
at [email protected]//org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51)
at [email protected]//javax.servlet.http.HttpServlet.service(HttpServlet.java:791)
at [email protected]//io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
at [email protected]//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
at [email protected]//io.undertow.websockets.jsr.JsrWebSocketFilter.doFilter(JsrWebSocketFilter.java:173)
at [email protected]//io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at [email protected]//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at deployment.deployment.war//de.akad.oc.rest.filters.XSRFFilter.doFilter(XSRFFilter.java:66)
at [email protected]//io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at [email protected]//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at deployment.deployment.war//de.akad.oc.rest.filters.MDCSessionFilter.doFilter(MDCSessionFilter.java:41)
at [email protected]//io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at [email protected]//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at io.opentracing.contrib.opentracing-jaxrs2//io.opentracing.contrib.jaxrs2.server.SpanFinishingFilter.doFilter(SpanFinishingFilter.java:55)
at [email protected]//io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at [email protected]//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at [email protected]//io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
at [email protected]//io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at [email protected]//io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
at [email protected]//io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at [email protected]//org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
at [email protected]//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at [email protected]//io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:132)
at [email protected]//io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at [email protected]//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at [email protected]//io.undertow.security.handlers.AuthenticationConstraintHandler.handleRequest(AuthenticationConstraintHandler.java:53)
at [email protected]//io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at [email protected]//io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at [email protected]//io.undertow.servlet.handlers.security.ServletSecurityConstraintHandler.handleRequest(ServletSecurityConstraintHandler.java:59)
at [email protected]//io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
at [email protected]//io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
at [email protected]//io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)
at [email protected]//io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at [email protected]//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at [email protected]//org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at [email protected]//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at [email protected]//org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68)
at [email protected]//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at [email protected]//io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292)
at [email protected]//io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
at [email protected]//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
at [email protected]//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
at [email protected]//io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at [email protected]//io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at [email protected]//org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105)
at [email protected]//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at [email protected]//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at [email protected]//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at [email protected]//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at [email protected]//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at [email protected]//io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
at [email protected]//io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
at [email protected]//io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
at [email protected]//io.undertow.server.Connectors.executeRootHandler(Connectors.java:364)
at [email protected]//io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830)
at [email protected]//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at [email protected]//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1982)
at [email protected]//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
at [email protected]//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
at java.base/java.lang.Thread.run(Thread.java:834)
I was wondering why the database query does not appear. I started a debug session within IntelliJ and an exception breakpoint catching the thrown FailedLoginException. I could see that the expected password was always null just like the excpetion has indicated (Password invalid/Password required).
I investigated further by logging all database queries that are performed in our MariaDB database by enabling the general query log. As I had expected no query at all was performed for fetching the user credentials. For verification i reverted the elytron migration and i performed a login and as expected the query was logged in the general query log.
So as it seems the newly defined elytron realm does not even perform the principal query against our database and I do not know why because the application gets deployed without errors and the defined datasource works as expected (connection tests suceeds and also from within the application I can perform queries).
My guess is that I have missed something during the configuration. I performed the following steps:
Define bCrypt elytron realm
/subsystem=elytron/jdbc-realm=bcryptRealm:add(principal-query=[{sql="SELECT password FROM USERS WHERE USERNAME = ?", data-source="DomainDS", clear-password-mapper={password-index=1}},{sql="SELECT g.groupname, 'Roles' from GROUPS g where g.username = ?", data-source="DomainDS", attribute-mapping=[{index=1, to=roles}]}])
/subsystem=elytron/simple-role-decoder="from-roles-attribute":add(attribute="Roles")
/subsystem=elytron/security-domain=bcryptDomain:add(default-realm=bcryptRealm, permission-mapper=default-permission-mapper,realms=[{realm=bcryptRealm, role-decoder=from-roles-attribute}])
/subsystem=elytron/http-authentication-factory=http-factory:add(http-server-mechanism-factory=global,security-domain=bcryptDomain,mechanism-configurations=[{mechanism-name=FORM,mechanism-realm-configurations=[{realm-name="bcryptRealm"}]}])
/subsystem=undertow/application-security-domain=bcryptDomain:add(http-authentication-factory=http-factory)
(I use the clear password mapper just for testing purposes)
Adapt web.xml
<login-config>
<auth-method>FORM</auth-method>
<realm-name>bcrypRealm</realm-name>
</login-config>
As already mentioned the application deploys without any errors but I dont know why the query is not performed against the datasource for fetching the actual credentials. Are there any ideas on how to fix that or is there something else that I miss in my configuration?
The version of the used wildfly is 15.0.1.Final. For testing I also updated the wildfly version to 16.0.0 Final and 17.0.1 Final with the same results.
Kind regards Michael
Michael Sommer is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.