You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2021/06/29 09:23:35 UTC

[isis] branch ISIS-2728 updated (70ec189 -> bbe7511)

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

danhaywood pushed a change to branch ISIS-2728
in repository https://gitbox.apache.org/repos/asf/isis.git.


    from 70ec189  ISIS-2728: adds UserMemento#multiTenancyToken unit tests
     new 6059534  ISIS-2728: introduces UserMementoRefiner as a new SPI
     new bbe7511  ISIS-2728: adds secman implementation of UserMementoRefiner

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../services/iactnlayer/InteractionContext.java    | 35 ++++++++--
 .../iactnlayer/InteractionContextUtil.java         | 27 ++++++++
 .../services/iactnlayer/InteractionService.java    |  2 +
 .../manager/AuthenticationManager.java             | 36 ++++++++--
 .../authentication/manager/UserMementoRefiner.java | 78 ++++++++++++++++++++++
 .../UserMementoRefinerFromApplicationUser.java}    | 34 +++++-----
 .../authconverters/AuthenticationConverter.java    |  2 +-
 7 files changed, 183 insertions(+), 31 deletions(-)
 create mode 100644 api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContextUtil.java
 create mode 100644 core/security/src/main/java/org/apache/isis/core/security/authentication/manager/UserMementoRefiner.java
 copy extensions/security/secman/{applib/src/main/java/org/apache/isis/extensions/secman/applib/user/app/mixins/ApplicationUserManager_allUsers.java => integration/src/main/java/org/apache/isis/extensions/secman/integration/usermementorefiner/UserMementoRefinerFromApplicationUser.java} (55%)

[isis] 02/02: ISIS-2728: adds secman implementation of UserMementoRefiner

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch ISIS-2728
in repository https://gitbox.apache.org/repos/asf/isis.git

commit bbe75110ad56f915c3d7d0dae8e75227a249419c
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Tue Jun 29 10:23:09 2021 +0100

    ISIS-2728: adds secman implementation of UserMementoRefiner
    
    ... to copy the ApplicationUser#atPath onto the UserMemento#multiTenancyToken
---
 .../UserMementoRefinerFromApplicationUser.java     | 45 ++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/usermementorefiner/UserMementoRefinerFromApplicationUser.java b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/usermementorefiner/UserMementoRefinerFromApplicationUser.java
new file mode 100644
index 0000000..2b29aa2
--- /dev/null
+++ b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/usermementorefiner/UserMementoRefinerFromApplicationUser.java
@@ -0,0 +1,45 @@
+/*
+ *  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.extensions.secman.integration.usermementorefiner;
+
+import javax.inject.Inject;
+
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.services.user.UserMemento;
+import org.apache.isis.core.security.authentication.manager.UserMementoRefiner;
+import org.apache.isis.extensions.secman.applib.user.dom.ApplicationUserRepository;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+
+@Service
+@Log4j2
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
+public class UserMementoRefinerFromApplicationUser implements UserMementoRefiner {
+
+    private final ApplicationUserRepository applicationUserRepository;
+
+    @Override
+    public UserMemento refine(UserMemento userMemento) {
+        return applicationUserRepository.findByUsername(userMemento.getName())
+                .map(applicationUser -> userMemento.withMultiTenancyToken(applicationUser.getAtPath()))
+                .orElse(userMemento);
+    }
+}

[isis] 01/02: ISIS-2728: introduces UserMementoRefiner as a new SPI

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch ISIS-2728
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 60595342bcb6decb517734ebc8d3f9d7d072df2b
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Tue Jun 29 10:17:43 2021 +0100

    ISIS-2728: introduces UserMementoRefiner as a new SPI
    
    to allow secman to be able to add in the multiTenancyToken from the ApplicationUser
---
 .../services/iactnlayer/InteractionContext.java    | 35 ++++++++--
 .../iactnlayer/InteractionContextUtil.java         | 27 ++++++++
 .../services/iactnlayer/InteractionService.java    |  2 +
 .../manager/AuthenticationManager.java             | 36 ++++++++--
 .../authentication/manager/UserMementoRefiner.java | 78 ++++++++++++++++++++++
 .../authconverters/AuthenticationConverter.java    |  2 +-
 6 files changed, 167 insertions(+), 13 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContext.java b/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContext.java
index 1d75cb7..c7d30f9 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContext.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContext.java
@@ -28,10 +28,14 @@ import org.apache.isis.applib.clock.VirtualClock;
 import org.apache.isis.applib.services.iactn.Interaction;
 import org.apache.isis.applib.services.user.UserMemento;
 
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
 import lombok.Builder;
+import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
+import lombok.ToString;
 import lombok.With;
 
 /**
@@ -39,7 +43,12 @@ import lombok.With;
  *
  * @since 2.0 {@index}
  */
-@lombok.Value @Builder
+@Getter
+@lombok.experimental.FieldDefaults(makeFinal=false, level= AccessLevel.PRIVATE)
+@AllArgsConstructor
+@ToString
+@EqualsAndHashCode
+@Builder
 @RequiredArgsConstructor
 public class InteractionContext implements Serializable {
 
@@ -66,11 +75,12 @@ public class InteractionContext implements Serializable {
     /**
      * The (programmatically) simulated (or actual) user.
      *
-     * @apiNote immutable, allows an {@link Interaction} to (logically) run with its
+     * @apiNote practically immutable, allows an {@link Interaction} to (logically) run with its
      * own simulated (or actual) user
+     *
      */
     @With @Getter @Builder.Default
-    @NonNull UserMemento user = UserMemento.system();
+    @NonNull /*final*/ UserMemento user = UserMemento.system();
 
     /**
      * The (programmatically) simulated (or actual) clock.
@@ -79,13 +89,13 @@ public class InteractionContext implements Serializable {
      * own simulated (or actual) clock
      */
     @With @Getter @Builder.Default
-    @NonNull VirtualClock clock = VirtualClock.system();
+    @NonNull final VirtualClock clock = VirtualClock.system();
 
     @With @Getter @Builder.Default
-    @NonNull Locale locale = Locale.getDefault();
+    @NonNull final Locale locale = Locale.getDefault();
 
     @With @Getter @Builder.Default
-    @NonNull TimeZone timeZone = TimeZone.getDefault();
+    @NonNull final TimeZone timeZone = TimeZone.getDefault();
 
 
     /**
@@ -146,5 +156,16 @@ public class InteractionContext implements Serializable {
         return mappers.reduce(t -> t, (a,b) -> a.andThen(b)::apply);
     }
 
-
+    /**
+     * For internal usage, not API.
+     *
+     * <p>
+     *     Instead, use {@link #withUser(UserMemento)}, which honours the value semantics of this class.
+     * </p>
+     *
+     * @param user
+     */
+    void replaceUser(UserMemento user) {
+        this.user = user;
+    }
 }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContextUtil.java b/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContextUtil.java
new file mode 100644
index 0000000..125e899
--- /dev/null
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContextUtil.java
@@ -0,0 +1,27 @@
+package org.apache.isis.applib.services.iactnlayer;
+
+import java.util.concurrent.Callable;
+
+import org.apache.isis.applib.services.user.UserMemento;
+
+import lombok.NonNull;
+import lombok.experimental.UtilityClass;
+
+
+@UtilityClass
+public interface InteractionContextUtil extends InteractionLayerTracker {
+
+    /**
+     * For internal usage, not formal API.
+     *
+     * <p>
+     *     Instead, use {@link InteractionContext#withUser(UserMemento)}, which honours the value semantics of this class.
+     * </p>
+     *
+     * @param user
+     */
+    public static void replaceUserIn(InteractionContext interactionContext, UserMemento userMemento) {
+        interactionContext.replaceUser(userMemento);
+    }
+
+}
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionService.java b/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionService.java
index dea7b33..be60b0d 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionService.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionService.java
@@ -2,6 +2,8 @@ package org.apache.isis.applib.services.iactnlayer;
 
 import java.util.concurrent.Callable;
 
+import org.apache.isis.applib.services.user.UserMemento;
+
 import lombok.NonNull;
 
 /**
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 6336693..d197862 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
@@ -35,7 +35,9 @@ import org.springframework.transaction.annotation.Transactional;
 import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.exceptions.unrecoverable.NoAuthenticatorException;
 import org.apache.isis.applib.services.iactnlayer.InteractionContext;
+import org.apache.isis.applib.services.iactnlayer.InteractionContextUtil;
 import org.apache.isis.applib.services.iactnlayer.InteractionService;
+import org.apache.isis.applib.services.user.UserMemento;
 import org.apache.isis.applib.util.ToString;
 import org.apache.isis.commons.collections.Can;
 import org.apache.isis.commons.internal.base._Timing;
@@ -61,16 +63,19 @@ public class AuthenticationManager {
     private final @NonNull InteractionService interactionService;
     private final @NonNull RandomCodeGenerator randomCodeGenerator;
     private final @NonNull Can<Registrar> registrars;
+    private final @NonNull List<UserMementoRefiner> userMementoRefiners;
 
     @Inject
     public AuthenticationManager(
             final List<Authenticator> authenticators,
             // needs @Lazy due to circular provisioning dependency
             final @Lazy InteractionService anonymousInteractionFactory,
-            final RandomCodeGenerator randomCodeGenerator) {
+            final RandomCodeGenerator randomCodeGenerator,
+            final List<UserMementoRefiner> userMementoRefiners) {
         this.interactionService = anonymousInteractionFactory;
         this.randomCodeGenerator = randomCodeGenerator;
         this.authenticators = Can.ofCollection(authenticators);
+        this.userMementoRefiners = userMementoRefiners;
         if (this.authenticators.isEmpty()) {
             throw new NoAuthenticatorException("No authenticators specified");
         }
@@ -102,13 +107,13 @@ public class AuthenticationManager {
         return interactionService.callAnonymous(()->{
 
             for (val authenticator : compatibleAuthenticators) {
-                val authentication = authenticator.authenticate(request, getUnusedRandomCode());
-                if (authentication != null) {
-                    val userMemento = authentication.getUser();
+                val interactionContext = authenticator.authenticate(request, getUnusedRandomCode());
+                if (interactionContext != null) {
+                    val userMemento = refineUserWithin(interactionContext);
                     userByValidationCode.put(
                             userMemento.getAuthenticationCode(),
                             userMemento.getName());
-                    return authentication;
+                    return interactionContext;
                 }
             }
 
@@ -118,6 +123,27 @@ public class AuthenticationManager {
 
     }
 
+    /**
+     * Iterates over all available {@link UserMementoRefiner}s; if at the end the {@link UserMemento} has been changed,
+     * then replaces (using a private API) the {@link UserMemento} held within {@link InteractionContext}.
+     *
+     * @param interactionContext
+     * @return
+     */
+    @NonNull
+    private UserMemento refineUserWithin(InteractionContext interactionContext) {
+        val userMementoOrig = interactionContext.getUser();
+        UserMemento userMemento = userMementoOrig;
+        for (UserMementoRefiner refiner : userMementoRefiners) {
+            final UserMemento refined = refiner.refine(userMemento);
+            userMemento = refined != null ? refined : userMemento;
+        }
+        if(userMemento != userMementoOrig) {
+            InteractionContextUtil.replaceUserIn(interactionContext, userMemento);
+        }
+        return interactionContext.getUser();
+    }
+
     private String getUnusedRandomCode() {
 
         val stopWatch = _Timing.now();
diff --git a/core/security/src/main/java/org/apache/isis/core/security/authentication/manager/UserMementoRefiner.java b/core/security/src/main/java/org/apache/isis/core/security/authentication/manager/UserMementoRefiner.java
new file mode 100644
index 0000000..cdd895f
--- /dev/null
+++ b/core/security/src/main/java/org/apache/isis/core/security/authentication/manager/UserMementoRefiner.java
@@ -0,0 +1,78 @@
+/*
+ *  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.List;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+import javax.annotation.Priority;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import org.apache.isis.applib.annotation.PriorityPrecedence;
+import org.apache.isis.applib.exceptions.unrecoverable.NoAuthenticatorException;
+import org.apache.isis.applib.services.iactnlayer.InteractionContext;
+import org.apache.isis.applib.services.iactnlayer.InteractionService;
+import org.apache.isis.applib.services.user.UserMemento;
+import org.apache.isis.applib.util.ToString;
+import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.base._Timing;
+import org.apache.isis.commons.internal.collections._Maps;
+import org.apache.isis.core.security.authentication.AuthenticationRequest;
+import org.apache.isis.core.security.authentication.Authenticator;
+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;
+
+/**
+ * SPI provided by the internal {@link AuthenticationManager}, allowing the {@link UserMemento} representing an
+ * authenticated (logged in) user to be refined.
+ *
+ * <p>
+ *     As {@link UserMemento} is immutable, the implementation must return a new instance.  This new instance replaces
+ *     the {@link InteractionContext#getUser() user reference held} by {@link InteractionContext}.
+ * </p>
+ *
+ * <p>
+ *     Originally introduced to allow SecMan to update the {@link UserMemento#getMultiTenancyToken() multi-tenancy}
+ *     token of {@link UserMemento}.
+ * </p>
+ */
+public interface UserMementoRefiner {
+
+    /**
+     * Return either the same (unchanged) or an updated {@link UserMemento} to use instead of the original within the
+     * owning {@link InteractionContext}.
+     *
+     * @param userMemento - to be refined
+     * @return the userMemento that is refined, or else unchanged.
+     */
+    UserMemento refine(final UserMemento userMemento);
+
+}
diff --git a/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverter.java b/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverter.java
index 39efb07..535a3c2 100644
--- a/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverter.java
+++ b/security/spring/src/main/java/org/apache/isis/security/spring/authconverters/AuthenticationConverter.java
@@ -24,7 +24,7 @@ import org.apache.isis.applib.services.user.UserMemento;
  *     All known converters are checked one by one, but checking stops once one
  *     converter has successively converted the {@link Authentication} into a
  *     {@link UserMemento} (in other words, chain-of-responsibility pattern).
- *     Use the {@link org.springframework.core.Ordered} to influence the order
+ *     Use the {@link javax.annotation.Priority} annotation to influence the order
  *     in which converter implementations are checked.
  * </p>
  *