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/04/11 10:52:29 UTC
[isis] 02/03: ISIS-2550: extends UserService API with the concept
of impersonation
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch ISIS-2550
in repository https://gitbox.apache.org/repos/asf/isis.git
commit 2c54de0838c1029cdb6906af8c2927cc652738be
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Sun Apr 11 11:51:28 2021 +0100
ISIS-2550: extends UserService API with the concept of impersonation
... and modifies UserServiceDefault to support impersonation, delegating the details to a new service, ImpersonatedUserHolder (that uses HttpSession)
---
.../isis/applib/services/user/UserService.java | 78 ++++++++++++++++++++++
.../IsisModuleCoreRuntimeServices.java | 2 +
...iceDefault.java => ImpersonatedUserHolder.java} | 45 +++++++++----
.../runtimeservices/user/UserServiceDefault.java | 46 +++++++++++--
4 files changed, 152 insertions(+), 19 deletions(-)
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/user/UserService.java b/api/applib/src/main/java/org/apache/isis/applib/services/user/UserService.java
index af32a15..3adf804 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/user/UserService.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/user/UserService.java
@@ -18,6 +18,7 @@
*/
package org.apache.isis.applib.services.user;
+import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
@@ -84,4 +85,81 @@ public interface UserService {
.orElse("Nobody");
}
+ /**
+ * Allows implementations to override the current user with another user.
+ *
+ * <p>
+ * This is intended for non-production environments only, where it can
+ * be invaluable (from a support perspective) to be able to quickly
+ * use the application "as if" logged in as another user.
+ * </p>
+ *
+ * @see #supportsImpersonation()
+ * @see #getImpersonatedUser()
+ * @see #isImpersonating()
+ * @see #stopImpersonating()
+ *
+ * @param userName
+ * @param roles
+ */
+ default void impersonateUser(final String userName, final List<String> roles) {
+ throw new RuntimeException("Not implemented");
+ }
+
+ /**
+ * For implementations that support impersonation, this is to
+ * programmatically stop impersonating a user
+ *
+ * <p>
+ * Intended to be called at some point after
+ * {@link #impersonateUser(String, List)} would have been called.
+ * </p>
+ *
+ * @see #supportsImpersonation()
+ * @see #impersonateUser(String, List)
+ * @see #getImpersonatedUser()
+ * @see #isImpersonating()
+ */
+ default void stopImpersonating() {
+ throw new RuntimeException("Not implemented");
+ }
+
+ /**
+ * Whether this implementation supports impersonation.
+ *
+ * @see #impersonateUser(String, List)
+ * @see #getImpersonatedUser()
+ * @see #isImpersonating()
+ * @see #stopImpersonating()
+ */
+ default boolean supportsImpersonation() {
+ return false;
+ }
+
+ /**
+ * The impersonated user, if it has previously been set.
+ *
+ * @see #supportsImpersonation()
+ * @see #impersonateUser(String, List)
+ * @see #isImpersonating()
+ * @see #stopImpersonating()
+ */
+ default Optional<UserMemento> getImpersonatedUser() {
+ return Optional.empty();
+ }
+
+ /**
+ * Whether or not the user currently reported (in {@link #currentUser()}
+ * and similar) is actually an impersonated user.
+ *
+ * @see #currentUser()
+ * @see #supportsImpersonation()
+ * @see #impersonateUser(String, List)
+ * @see #getImpersonatedUser()
+ * @see #stopImpersonating()
+ */
+ default boolean isImpersonating() {
+ return getImpersonatedUser().isPresent();
+ }
+
}
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/IsisModuleCoreRuntimeServices.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/IsisModuleCoreRuntimeServices.java
index 307f7c3..2692536 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/IsisModuleCoreRuntimeServices.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/IsisModuleCoreRuntimeServices.java
@@ -59,6 +59,7 @@ import org.apache.isis.core.runtimeservices.session.InteractionFactoryDefault;
import org.apache.isis.core.runtimeservices.sudo.SudoServiceDefault;
import org.apache.isis.core.runtimeservices.transaction.TransactionServiceSpring;
import org.apache.isis.core.runtimeservices.urlencoding.UrlEncodingServiceWithCompression;
+import org.apache.isis.core.runtimeservices.user.ImpersonatedUserHolder;
import org.apache.isis.core.runtimeservices.user.UserServiceDefault;
import org.apache.isis.core.runtimeservices.userprof.UserProfileServiceDefault;
import org.apache.isis.core.runtimeservices.userreg.EmailNotificationServiceDefault;
@@ -88,6 +89,7 @@ import org.apache.isis.core.runtimeservices.xmlsnapshot.XmlSnapshotServiceDefaul
EventBusServiceSpring.class,
FactoryServiceDefault.class,
HomePageResolverServiceDefault.class,
+ ImpersonatedUserHolder.class,
InteractionDtoFactoryDefault.class,
InteractionFactoryDefault.class,
JaxbServiceDefault.class,
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/user/UserServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/user/ImpersonatedUserHolder.java
similarity index 53%
copy from core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/user/UserServiceDefault.java
copy to core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/user/ImpersonatedUserHolder.java
index 5bce6c3..9e26dab 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/user/UserServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/user/ImpersonatedUserHolder.java
@@ -22,32 +22,51 @@ import java.util.Optional;
import javax.inject.Inject;
import javax.inject.Named;
+import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.apache.isis.applib.annotation.OrderPrecedence;
-import org.apache.isis.applib.services.iactn.ExecutionContext;
import org.apache.isis.applib.services.user.UserMemento;
-import org.apache.isis.applib.services.user.UserService;
-import org.apache.isis.core.interaction.session.InteractionTracker;
+/**
+ * Used by {@link UserServiceDefault} to allow the current user to be
+ * temporarily impersonated.
+ *
+ * <p>
+ * Intended for non-production environments only.
+ * </p>
+ *
+ * @since 2.0 {@index}
+ */
@Service
-@Named("isis.runtimeservices.UserServiceDefault")
+@Named("isis.runtimeservices.ImpersonatedUserHolder")
@Order(OrderPrecedence.MIDPOINT)
@Primary
@Qualifier("Default")
-public class UserServiceDefault implements UserService {
-
- @Inject private InteractionTracker isisInteractionTracker;
-
- @Override
- public Optional<UserMemento> currentUser() {
- return isisInteractionTracker.currentExecutionContext()
- .map(ExecutionContext::getUser);
+public class ImpersonatedUserHolder {
+
+ @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
+ @Inject private HttpSession httpSession;
+
+ private static final String HTTP_SESSION_KEY_IMPERSONATED_USER = ImpersonatedUserHolder.class.getName() + "#userMemento";
+
+ public void setUserMemento(final UserMemento userMemento) {
+ this.httpSession.setAttribute(HTTP_SESSION_KEY_IMPERSONATED_USER, userMemento);
+ }
+
+ public Optional<UserMemento> getUserMemento() {
+ final Object attribute = this.httpSession.getAttribute(HTTP_SESSION_KEY_IMPERSONATED_USER);
+ return attribute instanceof UserMemento
+ ? Optional.of((UserMemento)attribute)
+ : Optional.empty();
}
-
+ public void clearUserMemento() {
+ this.httpSession.removeAttribute(HTTP_SESSION_KEY_IMPERSONATED_USER);
+ }
}
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/user/UserServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/user/UserServiceDefault.java
index 5bce6c3..69d570a 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/user/UserServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/user/UserServiceDefault.java
@@ -18,6 +18,8 @@
*/
package org.apache.isis.core.runtimeservices.user;
+import java.util.Arrays;
+import java.util.List;
import java.util.Optional;
import javax.inject.Inject;
@@ -34,20 +36,52 @@ import org.apache.isis.applib.services.user.UserMemento;
import org.apache.isis.applib.services.user.UserService;
import org.apache.isis.core.interaction.session.InteractionTracker;
+import lombok.RequiredArgsConstructor;
+
@Service
@Named("isis.runtimeservices.UserServiceDefault")
@Order(OrderPrecedence.MIDPOINT)
@Primary
@Qualifier("Default")
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
public class UserServiceDefault implements UserService {
-
- @Inject private InteractionTracker isisInteractionTracker;
-
+
+ private final InteractionTracker isisInteractionTracker;
+ private final ImpersonatedUserHolder impersonatedUserHolder;
+
+ /**
+ * Either the current user or the one being impersonated.
+ */
@Override
public Optional<UserMemento> currentUser() {
- return isisInteractionTracker.currentExecutionContext()
- .map(ExecutionContext::getUser);
+ return or(getImpersonatedUser(),
+ isisInteractionTracker.currentExecutionContext()
+ .map(ExecutionContext::getUser)
+ );
+ }
+
+ private static <T> Optional<T> or(Optional<T> optional, Optional<T> fallback) {
+ return optional.isPresent() ? optional : fallback;
+ }
+
+ @Override
+ public void impersonateUser(final String userName, final List<String> roles) {
+ impersonatedUserHolder.setUserMemento(UserMemento.ofNameAndRoleNames(userName, roles));
+ }
+
+ @Override
+ public void stopImpersonating() {
+ impersonatedUserHolder.clearUserMemento();
+ }
+
+ @Override
+ public boolean supportsImpersonation() {
+ return true;
+ }
+
+ @Override
+ public Optional<UserMemento> getImpersonatedUser() {
+ return impersonatedUserHolder.getUserMemento();
}
-
}