You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2021/06/02 19:31:40 UTC

[isis] branch master updated: ISIS-2557: let AuthenticationManager.authenticate(...) manage its own transaction and interaction

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new a90ca82  ISIS-2557: let AuthenticationManager.authenticate(...) manage its own transaction and interaction
a90ca82 is described below

commit a90ca82f36f482cc8300650e81e03b964c713b94
Author: andi-huber <ah...@apache.org>
AuthorDate: Wed Jun 2 21:29:31 2021 +0200

    ISIS-2557: let AuthenticationManager.authenticate(...) manage its own
    transaction and interaction
    
    - let Spring handle the transactional context for this method (readonly)
    - when passing the auth-request to the authenticators wrap the call in
    an anonymous interaction
    - Spring's transaction aware proxies cannot include public methods with
    final modifier (found by experiment not by documentation)
---
 .../interaction/session/InteractionFactory.java    |  7 ++-
 .../manager/AnonymousInteractionFactory.java       | 69 ++++++++++++++++++++++
 .../manager/AuthenticationManager.java             | 41 +++++++++----
 .../AuthenticationManager_authenticators_Test.java | 17 +++---
 ...rdAuthenticationManager_AuthenticationTest.java | 14 +++--
 .../authenticator/AuthenticatorSecman.java         | 17 +-----
 .../AuthenticatorSecmanAutoConfiguration.java      |  3 +-
 ...uthenticatedWebSessionForIsis_Authenticate.java | 32 +++++-----
 .../AuthenticatedWebSessionForIsis_SignIn.java     | 26 ++++----
 9 files changed, 156 insertions(+), 70 deletions(-)

diff --git a/core/interaction/src/main/java/org/apache/isis/core/interaction/session/InteractionFactory.java b/core/interaction/src/main/java/org/apache/isis/core/interaction/session/InteractionFactory.java
index dab91be..0254ceb 100644
--- a/core/interaction/src/main/java/org/apache/isis/core/interaction/session/InteractionFactory.java
+++ b/core/interaction/src/main/java/org/apache/isis/core/interaction/session/InteractionFactory.java
@@ -23,6 +23,7 @@ import java.util.concurrent.Callable;
 
 import org.apache.isis.commons.functional.ThrowingRunnable;
 import org.apache.isis.core.security.authentication.Authentication;
+import org.apache.isis.core.security.authentication.manager.AnonymousInteractionFactory;
 
 import lombok.NonNull;
 
@@ -36,12 +37,13 @@ import lombok.NonNull;
  * </p>
  *
  * <p>
- * @apiNote : this service is not currently formal API.
+ * @apiNote This is a framework internal class and so does not constitute a formal API.
  * </p>
  *
  * @since 2.0 {@index}
  */
-public interface InteractionFactory {
+public interface InteractionFactory
+extends AnonymousInteractionFactory {
 
     /**
      * If present, reuses the current top level {@link AuthenticationLayer}, otherwise creates a new
@@ -111,6 +113,7 @@ public interface InteractionFactory {
      * Variant of {@link #callAnonymous(Callable)} that takes a runnable.
      * @param runnable (non-null)
      */
+    @Override
     void runAnonymous(@NonNull ThrowingRunnable runnable);
 
     /**
diff --git a/core/security/src/main/java/org/apache/isis/core/security/authentication/manager/AnonymousInteractionFactory.java b/core/security/src/main/java/org/apache/isis/core/security/authentication/manager/AnonymousInteractionFactory.java
new file mode 100644
index 0000000..e84cc88
--- /dev/null
+++ b/core/security/src/main/java/org/apache/isis/core/security/authentication/manager/AnonymousInteractionFactory.java
@@ -0,0 +1,69 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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 org.apache.isis.core.security.authentication.manager;
+
+import java.util.concurrent.Callable;
+
+import org.apache.isis.commons.functional.ThrowingRunnable;
+
+import lombok.NonNull;
+import lombok.SneakyThrows;
+
+/**
+ * Inversion of dependency pattern.
+ * <p>
+ * Introduced to allow the AuthenticationManager (module: security) access to the
+ * InteractionFactory service (module: interaction),
+ * which otherwise was not possible, due to Maven module dependencies (preventing circles).
+ *
+ * @apiNote This is a framework internal class and so does not constitute a formal API.
+ *
+ * @since 2.0 {@index}
+ */
+public interface AnonymousInteractionFactory {
+
+    // for java-doc see InteractionFactory
+    void runAnonymous(@NonNull ThrowingRunnable runnable);
+
+    // for java-doc see InteractionFactory
+    <R> R callAnonymous(@NonNull Callable<R> callable);
+
+
+    // -- JUNIT SUPPORT
+
+    /**
+     * Returns a pass-through implementation, free of side-effects,
+     * in support of simple JUnit tests.
+     */
+    static AnonymousInteractionFactory forTesting() {
+        return new AnonymousInteractionFactory() {
+
+            @Override @SneakyThrows
+            public void runAnonymous(@NonNull ThrowingRunnable runnable) {
+                runnable.run();
+            }
+
+            @Override @SneakyThrows
+            public <R> R callAnonymous(@NonNull Callable<R> callable) {
+                return callable.call();
+            }
+
+        };
+    }
+}
diff --git a/core/security/src/main/java/org/apache/isis/core/security/authentication/manager/AuthenticationManager.java b/core/security/src/main/java/org/apache/isis/core/security/authentication/manager/AuthenticationManager.java
index 779da22..27bca27 100644
--- a/core/security/src/main/java/org/apache/isis/core/security/authentication/manager/AuthenticationManager.java
+++ b/core/security/src/main/java/org/apache/isis/core/security/authentication/manager/AuthenticationManager.java
@@ -27,9 +27,11 @@ import javax.inject.Inject;
 import javax.inject.Named;
 
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.context.annotation.Primary;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.exceptions.unrecoverable.NoAuthenticatorException;
@@ -44,6 +46,7 @@ import org.apache.isis.core.security.authentication.standard.RandomCodeGenerator
 import org.apache.isis.core.security.authentication.standard.Registrar;
 
 import lombok.Getter;
+import lombok.NonNull;
 import lombok.val;
 
 @Service
@@ -53,16 +56,20 @@ import lombok.val;
 @Qualifier("Default")
 public class AuthenticationManager {
 
-    @Getter private final Can<Authenticator> authenticators;
+    @Getter private final @NonNull Can<Authenticator> authenticators;
 
     private final Map<String, String> userByValidationCode = _Maps.newConcurrentHashMap();
-    private final RandomCodeGenerator randomCodeGenerator;
-    private final Can<Registrar> registrars;
+    private final @NonNull AnonymousInteractionFactory anonymousInteractionFactory;
+    private final @NonNull RandomCodeGenerator randomCodeGenerator;
+    private final @NonNull Can<Registrar> registrars;
 
     @Inject
     public AuthenticationManager(
             final List<Authenticator> authenticators,
+            // needs @Lazy due to circular provisioning dependency
+            final @Lazy AnonymousInteractionFactory anonymousInteractionFactory,
             final RandomCodeGenerator randomCodeGenerator) {
+        this.anonymousInteractionFactory = anonymousInteractionFactory;
         this.randomCodeGenerator = randomCodeGenerator;
         this.authenticators = Can.ofCollection(authenticators);
         if (this.authenticators.isEmpty()) {
@@ -75,7 +82,9 @@ public class AuthenticationManager {
 
     // -- SESSION MANAGEMENT (including authenticate)
 
-    public final Authentication authenticate(AuthenticationRequest request) {
+    @Transactional(readOnly = true) // let Spring handle the transactional context for this method
+    // cannot use final here, as Spring provides a transaction aware proxy for this type
+    public /*final*/ Authentication authenticate(AuthenticationRequest request) {
 
         if (request == null) {
             return null;
@@ -89,15 +98,22 @@ public class AuthenticationManager {
                     "No authenticator available for processing " + request.getClass().getName());
         }
 
-        for (val authenticator : compatibleAuthenticators) {
-            val authentication = authenticator.authenticate(request, getUnusedRandomCode());
-            if (authentication != null) {
-                userByValidationCode.put(authentication.getValidationCode(), authentication.getUserName());
-                return authentication;
+        // open a new anonymous interaction for this loop to run in
+        // we simply participate with the current transaction
+        return anonymousInteractionFactory.callAnonymous(()->{
+
+            for (val authenticator : compatibleAuthenticators) {
+                val authentication = authenticator.authenticate(request, getUnusedRandomCode());
+                if (authentication != null) {
+                    userByValidationCode.put(authentication.getValidationCode(), authentication.getUserName());
+                    return authentication;
+                }
             }
-        }
 
-        return null;
+            return null;
+
+        });
+
     }
 
     private String getUnusedRandomCode() {
@@ -120,7 +136,8 @@ public class AuthenticationManager {
     }
 
 
-    public final boolean isSessionValid(final @Nullable Authentication authentication) {
+    // cannot use final here, as Spring provides a transaction aware proxy for this type
+    public /*final*/ boolean isSessionValid(final @Nullable Authentication authentication) {
         if(authentication==null) {
             return false;
         }
diff --git a/core/security/src/test/java/org/apache/isis/security/authentication/standard/AuthenticationManager_authenticators_Test.java b/core/security/src/test/java/org/apache/isis/security/authentication/standard/AuthenticationManager_authenticators_Test.java
index bbdedac..5cc8756 100644
--- a/core/security/src/test/java/org/apache/isis/security/authentication/standard/AuthenticationManager_authenticators_Test.java
+++ b/core/security/src/test/java/org/apache/isis/security/authentication/standard/AuthenticationManager_authenticators_Test.java
@@ -23,16 +23,17 @@ import java.util.Collections;
 
 import org.junit.Test;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.sameInstance;
-import static org.hamcrest.MatcherAssert.assertThat;
-
 import org.apache.isis.applib.exceptions.unrecoverable.NoAuthenticatorException;
 import org.apache.isis.core.security.authentication.AuthenticationRequestPassword;
+import org.apache.isis.core.security.authentication.manager.AnonymousInteractionFactory;
 import org.apache.isis.core.security.authentication.manager.AuthenticationManager;
 import org.apache.isis.core.security.authentication.standard.RandomCodeGeneratorDefault;
 import org.apache.isis.security.AuthenticatorsForTesting;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.hamcrest.MatcherAssert.assertThat;
+
 import lombok.val;
 
 public class AuthenticationManager_authenticators_Test {
@@ -43,7 +44,8 @@ public class AuthenticationManager_authenticators_Test {
     public void shouldNotBeAbleToAuthenticateWithNoAuthenticators() throws Exception {
 
         authenticationManager = new AuthenticationManager(
-                Collections.emptyList(), 
+                Collections.emptyList(),
+                AnonymousInteractionFactory.forTesting(),
                 new RandomCodeGeneratorDefault());
         authenticationManager.authenticate(new AuthenticationRequestPassword("foo", "bar"));
     }
@@ -52,9 +54,10 @@ public class AuthenticationManager_authenticators_Test {
     public void shouldBeAbleToUseAuthenticators() throws Exception {
 
         val auth = AuthenticatorsForTesting.authenticatorAllwaysValid();
-        
+
         authenticationManager = new AuthenticationManager(
-                Collections.singletonList(auth), 
+                Collections.singletonList(auth),
+                AnonymousInteractionFactory.forTesting(),
                 new RandomCodeGeneratorDefault());
         assertThat(authenticationManager.getAuthenticators().size(), is(1));
         assertThat(authenticationManager.getAuthenticators().getElseFail(0), is(sameInstance(auth)));
diff --git a/core/security/src/test/java/org/apache/isis/security/authentication/standard/StandardAuthenticationManager_AuthenticationTest.java b/core/security/src/test/java/org/apache/isis/security/authentication/standard/StandardAuthenticationManager_AuthenticationTest.java
index f62c136..a5f683f 100644
--- a/core/security/src/test/java/org/apache/isis/security/authentication/standard/StandardAuthenticationManager_AuthenticationTest.java
+++ b/core/security/src/test/java/org/apache/isis/security/authentication/standard/StandardAuthenticationManager_AuthenticationTest.java
@@ -24,24 +24,26 @@ import java.util.Collections;
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
 import org.apache.isis.core.security.authentication.Authentication;
 import org.apache.isis.core.security.authentication.AuthenticationRequestPassword;
+import org.apache.isis.core.security.authentication.manager.AnonymousInteractionFactory;
 import org.apache.isis.core.security.authentication.manager.AuthenticationManager;
 import org.apache.isis.core.security.authentication.standard.RandomCodeGeneratorDefault;
 import org.apache.isis.security.AuthenticatorsForTesting;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
 public class StandardAuthenticationManager_AuthenticationTest {
 
     private AuthenticationManager authenticationManager;
 
     @Before
     public void setUp() throws Exception {
-        
+
         authenticationManager = new AuthenticationManager(
-                Collections.singletonList(AuthenticatorsForTesting.authenticatorValidForFoo()), 
+                Collections.singletonList(AuthenticatorsForTesting.authenticatorValidForFoo()),
+                AnonymousInteractionFactory.forTesting(),
                 new RandomCodeGeneratorDefault());
     }
 
@@ -52,7 +54,7 @@ public class StandardAuthenticationManager_AuthenticationTest {
 
         assertThat(authenticationManager.isSessionValid(authentication), is(true));
     }
-    
+
     @Test
     public void newlyCreatedAuthentication_whenUnauthorizedUser_shouldBeRejected() throws Exception {
         final AuthenticationRequestPassword request = new AuthenticationRequestPassword("me", "pass");
diff --git a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authenticator/AuthenticatorSecman.java b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authenticator/AuthenticatorSecman.java
index 90a1306..5bbd2d9 100644
--- a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authenticator/AuthenticatorSecman.java
+++ b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authenticator/AuthenticatorSecman.java
@@ -18,43 +18,30 @@
  */
 package org.apache.isis.extensions.secman.integration.authenticator;
 
-import java.util.Optional;
-import java.util.Set;
 import java.util.stream.Stream;
 
 import javax.inject.Inject;
-import javax.inject.Named;
 
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Service;
-
-import org.apache.isis.applib.Identifier;
-import org.apache.isis.applib.annotation.OrderPrecedence;
-import org.apache.isis.applib.services.appfeat.ApplicationFeatureId;
 import org.apache.isis.applib.services.user.UserMemento;
-import org.apache.isis.core.config.IsisConfiguration;
 import org.apache.isis.core.security.authentication.Authentication;
 import org.apache.isis.core.security.authentication.AuthenticationRequest;
 import org.apache.isis.core.security.authentication.AuthenticationRequestPassword;
 import org.apache.isis.core.security.authentication.Authenticator;
 import org.apache.isis.core.security.authentication.standard.SimpleAuthentication;
-import org.apache.isis.core.security.authorization.Authorizor;
-import org.apache.isis.extensions.secman.applib.permission.dom.ApplicationPermissionMode;
 import org.apache.isis.extensions.secman.applib.role.dom.ApplicationRole;
 import org.apache.isis.extensions.secman.applib.user.dom.ApplicationUser;
 import org.apache.isis.extensions.secman.applib.user.dom.ApplicationUserRepository;
 import org.apache.isis.extensions.secman.applib.user.spi.PasswordEncryptionService;
 
 import lombok.RequiredArgsConstructor;
-import lombok.extern.log4j.Log4j2;
 import lombok.val;
+import lombok.extern.log4j.Log4j2;
 
 /**
  * {@link Authenticator} implementation that authenticates the
  * {@link ApplicationUser}, first that the user exists and secondly that the
  * provided password matches the
- * {@link ApplicationUser#getEncryptedPassword() encypted password} of the user.
+ * {@link ApplicationUser#getEncryptedPassword() encrypted password} of the user.
  *
  * <p>
  *     This Authenticator is a fallback and is only used if there is no other
diff --git a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authenticator/AuthenticatorSecmanAutoConfiguration.java b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authenticator/AuthenticatorSecmanAutoConfiguration.java
index a881f4b..baf8689 100644
--- a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authenticator/AuthenticatorSecmanAutoConfiguration.java
+++ b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/authenticator/AuthenticatorSecmanAutoConfiguration.java
@@ -23,9 +23,9 @@ import org.springframework.boot.autoconfigure.AutoConfigureOrder;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.transaction.annotation.Transactional;
 
 import org.apache.isis.applib.annotation.OrderPrecedence;
-import org.apache.isis.core.config.IsisConfiguration;
 import org.apache.isis.core.security.authentication.Authenticator;
 import org.apache.isis.extensions.secman.applib.user.dom.ApplicationUserRepository;
 import org.apache.isis.extensions.secman.applib.user.spi.PasswordEncryptionService;
@@ -39,6 +39,7 @@ public class AuthenticatorSecmanAutoConfiguration  {
 
     @Bean("isis.ext.secman.AuthenticatorSecman")
     @ConditionalOnMissingBean(Authenticator.class)
+    @Transactional
     @Qualifier("Secman")
     public Authenticator authenticatorSecman(
             final ApplicationUserRepository applicationUserRepository,
diff --git a/viewers/wicket/viewer/src/test/java/org/apache/isis/viewer/wicket/viewer/integration/AuthenticatedWebSessionForIsis_Authenticate.java b/viewers/wicket/viewer/src/test/java/org/apache/isis/viewer/wicket/viewer/integration/AuthenticatedWebSessionForIsis_Authenticate.java
index 4a02d66..6f40630 100644
--- a/viewers/wicket/viewer/src/test/java/org/apache/isis/viewer/wicket/viewer/integration/AuthenticatedWebSessionForIsis_Authenticate.java
+++ b/viewers/wicket/viewer/src/test/java/org/apache/isis/viewer/wicket/viewer/integration/AuthenticatedWebSessionForIsis_Authenticate.java
@@ -29,11 +29,6 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.nullValue;
-
 import org.apache.isis.applib.services.registry.ServiceRegistry;
 import org.apache.isis.applib.services.session.SessionLoggingService;
 import org.apache.isis.commons.collections.Can;
@@ -44,11 +39,17 @@ import org.apache.isis.core.internaltestsupport.jmocking.JUnitRuleMockery2;
 import org.apache.isis.core.runtime.context.IsisAppCommonContext;
 import org.apache.isis.core.security.authentication.AuthenticationRequest;
 import org.apache.isis.core.security.authentication.AuthenticationRequestPassword;
+import org.apache.isis.core.security.authentication.Authenticator;
+import org.apache.isis.core.security.authentication.manager.AnonymousInteractionFactory;
 import org.apache.isis.core.security.authentication.manager.AuthenticationManager;
 import org.apache.isis.core.security.authentication.singleuser.SingleUserAuthentication;
-import org.apache.isis.core.security.authentication.Authenticator;
 import org.apache.isis.core.security.authentication.standard.RandomCodeGeneratorDefault;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+
 public class AuthenticatedWebSessionForIsis_Authenticate {
 
 
@@ -61,15 +62,18 @@ public class AuthenticatedWebSessionForIsis_Authenticate {
     private AuthenticationManager authMgr;
     @Mock protected Authenticator mockAuthenticator;
     @Mock protected IsisAppCommonContext mockCommonContext;
-    @Mock protected InteractionFactory mockIsisInteractionFactory;
-    @Mock protected InteractionTracker mockIsisInteractionTracker;
+    @Mock protected InteractionFactory mockInteractionFactory;
+    @Mock protected InteractionTracker mockInteractionTracker;
     @Mock protected ServiceRegistry mockServiceRegistry;
 
     protected AuthenticatedWebSessionForIsis webSession;
 
     @Before
     public void setUp() throws Exception {
-        authMgr = new AuthenticationManager(Collections.singletonList(mockAuthenticator), new RandomCodeGeneratorDefault());
+        authMgr = new AuthenticationManager(
+                Collections.singletonList(mockAuthenticator),
+                AnonymousInteractionFactory.forTesting(),
+                new RandomCodeGeneratorDefault());
 
         context.checking(new Expectations() {
             {
@@ -80,18 +84,18 @@ public class AuthenticatedWebSessionForIsis_Authenticate {
                 will(returnValue(Can.empty()));
 
                 allowing(mockCommonContext).lookupServiceElseFail(InteractionFactory.class);
-                will(returnValue(mockIsisInteractionFactory));
+                will(returnValue(mockInteractionFactory));
 
                 allowing(mockCommonContext).getInteractionTracker();
-                will(returnValue(mockIsisInteractionTracker));
+                will(returnValue(mockInteractionTracker));
 
-                allowing(mockIsisInteractionTracker).currentAuthentication();
+                allowing(mockInteractionTracker).currentAuthentication();
                 will(returnValue(Optional.of(new SingleUserAuthentication())));
 
-                allowing(mockIsisInteractionFactory)
+                allowing(mockInteractionFactory)
                 .runAuthenticated(with(new SingleUserAuthentication()), with(any(ThrowingRunnable.class)));
 
-                allowing(mockIsisInteractionFactory)
+                allowing(mockInteractionFactory)
                 .runAnonymous(with(any(ThrowingRunnable.class)));
 
                 // ignore
diff --git a/viewers/wicket/viewer/src/test/java/org/apache/isis/viewer/wicket/viewer/integration/AuthenticatedWebSessionForIsis_SignIn.java b/viewers/wicket/viewer/src/test/java/org/apache/isis/viewer/wicket/viewer/integration/AuthenticatedWebSessionForIsis_SignIn.java
index 73e96dc..5c2ee0f 100644
--- a/viewers/wicket/viewer/src/test/java/org/apache/isis/viewer/wicket/viewer/integration/AuthenticatedWebSessionForIsis_SignIn.java
+++ b/viewers/wicket/viewer/src/test/java/org/apache/isis/viewer/wicket/viewer/integration/AuthenticatedWebSessionForIsis_SignIn.java
@@ -29,9 +29,6 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-
 import org.apache.isis.applib.services.registry.ServiceRegistry;
 import org.apache.isis.applib.services.session.SessionLoggingService;
 import org.apache.isis.commons.collections.Can;
@@ -41,11 +38,15 @@ import org.apache.isis.core.internaltestsupport.jmocking.JUnitRuleMockery2;
 import org.apache.isis.core.runtime.context.IsisAppCommonContext;
 import org.apache.isis.core.security.authentication.AuthenticationRequest;
 import org.apache.isis.core.security.authentication.AuthenticationRequestPassword;
+import org.apache.isis.core.security.authentication.Authenticator;
+import org.apache.isis.core.security.authentication.manager.AnonymousInteractionFactory;
 import org.apache.isis.core.security.authentication.manager.AuthenticationManager;
 import org.apache.isis.core.security.authentication.singleuser.SingleUserAuthentication;
-import org.apache.isis.core.security.authentication.Authenticator;
 import org.apache.isis.core.security.authentication.standard.RandomCodeGeneratorDefault;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
 public class AuthenticatedWebSessionForIsis_SignIn {
 
     @Rule
@@ -57,14 +58,17 @@ public class AuthenticatedWebSessionForIsis_SignIn {
     private AuthenticationManager authMgr;
     @Mock protected Authenticator mockAuthenticator;
     @Mock protected IsisAppCommonContext mockCommonContext;
-    @Mock protected InteractionFactory mockIsisInteractionFactory;
+    @Mock protected InteractionFactory mockInteractionFactory;
     @Mock protected ServiceRegistry mockServiceRegistry;
 
     protected AuthenticatedWebSessionForIsis webSession;
 
     @Before
     public void setUp() throws Exception {
-        authMgr = new AuthenticationManager(Collections.singletonList(mockAuthenticator), new RandomCodeGeneratorDefault());
+        authMgr = new AuthenticationManager(
+                Collections.singletonList(mockAuthenticator),
+                AnonymousInteractionFactory.forTesting(),
+                new RandomCodeGeneratorDefault());
     }
 
     @Test
@@ -78,12 +82,12 @@ public class AuthenticatedWebSessionForIsis_SignIn {
                 will(returnValue(Can.empty()));
 
                 allowing(mockCommonContext).lookupServiceElseFail(InteractionFactory.class);
-                will(returnValue(mockIsisInteractionFactory));
+                will(returnValue(mockInteractionFactory));
 
-                allowing(mockIsisInteractionFactory)
+                allowing(mockInteractionFactory)
                 .runAuthenticated(with(new SingleUserAuthentication()), with(any(ThrowingRunnable.class)));
 
-                allowing(mockIsisInteractionFactory)
+                allowing(mockInteractionFactory)
                 .runAnonymous(with(any(ThrowingRunnable.class)));
 
                 // ignore
@@ -94,11 +98,7 @@ public class AuthenticatedWebSessionForIsis_SignIn {
 
                 // stub everything else out
                 ignoring(mockRequest);
-            }
-        });
 
-        context.checking(new Expectations() {
-            {
                 oneOf(mockAuthenticator).canAuthenticate(AuthenticationRequestPassword.class);
                 will(returnValue(true));
                 oneOf(mockAuthenticator).authenticate(with(any(AuthenticationRequest.class)), with(any(String.class)));