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/11 08:40:25 UTC
[isis] branch ISIS-2726 updated: ISIS-2726: improves docs for
UserService
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch ISIS-2726
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/ISIS-2726 by this push:
new cbddfec ISIS-2726: improves docs for UserService
cbddfec is described below
commit cbddfec0e4b2a751ae6546129300e5b97f0405a1
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Fri Jun 11 09:40:00 2021 +0100
ISIS-2726: improves docs for UserService
removes UserServiceDefault
---
.../org/apache/isis/applib/IsisModuleApplib.java | 3 +
.../org/apache/isis/applib/clock/VirtualClock.java | 2 -
.../isis/applib/services/user/UserService.java | 197 ++++++++++++++-------
.../IsisModuleCoreRuntimeServices.java | 2 -
.../runtimeservices/user/UserServiceDefault.java | 101 -----------
.../shiro/ShiroSecmanLdap_restfulStressTest.java | 2 +-
.../isis/testdomain/jpa/JpaInventoryResource.java | 2 +-
7 files changed, 141 insertions(+), 168 deletions(-)
diff --git a/api/applib/src/main/java/org/apache/isis/applib/IsisModuleApplib.java b/api/applib/src/main/java/org/apache/isis/applib/IsisModuleApplib.java
index a307456..fd03241 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/IsisModuleApplib.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/IsisModuleApplib.java
@@ -56,6 +56,7 @@ import org.apache.isis.applib.services.sudo.SudoService;
import org.apache.isis.applib.services.user.ImpersonateMenu;
import org.apache.isis.applib.services.user.RoleMemento;
import org.apache.isis.applib.services.user.UserMemento;
+import org.apache.isis.applib.services.user.UserService;
import org.apache.isis.schema.IsisModuleSchema;
/**
@@ -99,8 +100,10 @@ import org.apache.isis.schema.IsisModuleSchema;
LayoutServiceMenu.class,
ImpersonateMenu.class,
MetaModelServiceMenu.class,
+ UserService.class,
ApplicationFeatureMenu.class,
+
// @Service(s)
CommandDtoProcessorServiceIdentity.class,
CommandLogger.class,
diff --git a/api/applib/src/main/java/org/apache/isis/applib/clock/VirtualClock.java b/api/applib/src/main/java/org/apache/isis/applib/clock/VirtualClock.java
index 0bbdc4c..28e18e7 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/clock/VirtualClock.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/clock/VirtualClock.java
@@ -60,8 +60,6 @@ public interface VirtualClock extends Serializable {
*
* @apiNote This is a universal time difference, that does not depend on
* where you are (eg. your current timezone), just on when you are.
- *
- * @see {@link Instant}
*/
Instant now();
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 100126c..9e1c3c8 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
@@ -22,144 +22,219 @@ import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
+import javax.inject.Inject;
+import javax.inject.Named;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
import org.apache.isis.applib.services.iactnlayer.InteractionContext;
+import org.apache.isis.applib.services.iactnlayer.InteractionLayerTracker;
import org.apache.isis.applib.services.sudo.SudoService;
import org.apache.isis.commons.internal.exceptions._Exceptions;
+import lombok.RequiredArgsConstructor;
+import lombok.val;
+
/**
* Allows the domain object to obtain the identity of the user interacting with
* said object.
*
* <p>
* If {@link SudoService} has been used to temporarily override the user and/or
- * roles, then this service will report the overridden values instead.
+ * roles, then this service will report the overridden values instead. This is within the context of a thread.
+ * </p>
+ *
+ * <p>
+ * In addition, if impersonation has been invoked through the {@link ImpersonateMenu}, then this service
+ * will report the impersonated user, with the companion {@link ImpersonatedUserHolder} taking responsibilty for
+ * remembering the impersonated user over multiple (http) requests, eg using an http session. It's important to note
+ * that under these circumstances the user reported by this service (the "effective" user) will <i>not</i> be
+ * the same as the user held in the {@link InteractionContext}, as obtained by
+ * {@link InteractionLayerTracker#currentInteractionContext() InteractionLayerTracker} (the "real" user).
* </p>
*
+ * @see org.apache.isis.applib.services.iactnlayer.InteractionLayerTracker
+ * @see org.apache.isis.applib.services.iactnlayer.InteractionContext
+ * @see SudoService
+ * @see ImpersonateMenu
+ * @see ImpersonatedUserHolder
+ *
* @since 1.x revised in 2.0 {@index}
*/
-public interface UserService {
+@Service
+@Named("isis.applib.UserService")
+@Order(OrderPrecedence.MIDPOINT)
+@Primary
+@Qualifier("Default")
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
+public class UserService {
+
+ /**
+ * Default returned from {@link #currentUserNameElseNobody()}.
+ */
+ public static final String NOBODY = "__isis_nobody";
+
+ private final InteractionLayerTracker iInteractionLayerTracker;
+ private final List<ImpersonatedUserHolder> impersonatedUserHolders;
- // -- INTERFACE
/**
- * Optionally gets the details about the current user,
- * based on whether an {@link InteractionContext} can be found with the current thread's context.
+ * Returns the details about the current user, either the "effective" user (if being
+ * {@link #impersonateUser(String, List) impersonated}) otherwise the "real" user (as obtained from
+ * the {@link InteractionContext} of the current thread).
*/
- Optional<UserMemento> currentUser();
+ public Optional<UserMemento> currentUser() {
+ val impersonatedUserIfAny = impersonatedUserIfAny();
+ return impersonatedUserIfAny.isPresent()
+ ? impersonatedUserIfAny
+ : iInteractionLayerTracker.currentInteractionContext().map(InteractionContext::getUser);
+ }
/**
- * Gets the details about the current user.
- * @apiNote for backward compatibility
+ * Gets the details about the {@link #currentUser()} current user, if any (and returning <code>null</code> if
+ * there is none).
*/
@Nullable
- default UserMemento getUser() {
+ public UserMemento getUser() {
return currentUser().orElse(null);
}
-
/**
- * Gets the details about the current user.
+ * Gets the details about the {@link #currentUser()} current user, throwing an exception if there is none.
+ *
* @throws IllegalStateException if no {@link InteractionContext} can be found with the current thread's context.
*/
- default UserMemento currentUserElseFail() {
+ public UserMemento currentUserElseFail() {
return currentUser()
.orElseThrow(()->_Exceptions.illegalState("Current thread has no InteractionContext."));
}
/**
- * Optionally gets the the current user's name,
- * based on whether an {@link InteractionContext} can be found with the current thread's context.
+ * Optionally gets the {@link #currentUser() current user}'s name, obtained from {@link UserMemento}.
*/
- default Optional<String> currentUserName() {
+ public Optional<String> currentUserName() {
return currentUser()
.map(UserMemento::getName);
}
/**
- * Returns either the current user's name or else {@literal Nobody}.
+ * Returns either the current user's name or else {@link #NOBODY}.
*/
- default String currentUserNameElseNobody() {
+ public String currentUserNameElseNobody() {
return currentUserName()
- .orElse("Nobody");
+ .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>
+ * Whether or not the user currently reported (in {@link #currentUser()}
+ * and similar) is actually an impersonated user.
*
+ * @see #currentUser()
* @see #supportsImpersonation()
- * @see #getImpersonatedUser()
- * @see #isImpersonating()
+ * @see #impersonateUser(String, List)
+ * @see #impersonatedUserIfAny()
* @see #stopImpersonating()
- *
- * @param userName
- * @param roles
*/
- default void impersonateUser(final String userName, final List<String> roles) {
- throw new RuntimeException("Not implemented");
+ public boolean isImpersonating() {
+ return impersonatedUserIfAny().isPresent();
}
+
+
/**
- * For implementations that support impersonation, this is to
- * programmatically stop impersonating a user
+ * Whether impersonation is available for this request.
*
* <p>
- * Intended to be called at some point after
- * {@link #impersonateUser(String, List)} would have been called.
+ * The typical implementation uses an HTTP session, which is not guaranteed to be available
+ * for all viewers. Specifically, the Wicket viewer <i>does</i> use HTTP sessions and
+ * therefore supports impersonation, but the RestfulObjects viewer does <i>not</i>.
+ * This means that the result of this call varies on a request-by-request basis.
* </p>
*
- * @see #supportsImpersonation()
* @see #impersonateUser(String, List)
- * @see #getImpersonatedUser()
+ * @see #impersonatedUserIfAny()
* @see #isImpersonating()
+ * @see #stopImpersonating()
+ *
+ * @return whether impersonation is supported in the context of this (http) request.
*/
- default void stopImpersonating() {
- throw new RuntimeException("Not implemented");
+ public boolean supportsImpersonation() {
+ return impersonatingHolder()
+ .isPresent();
}
- /**
- * Whether this implementation supports impersonation.
- *
- * @see #impersonateUser(String, List)
- * @see #getImpersonatedUser()
- * @see #isImpersonating()
- * @see #stopImpersonating()
- */
- default boolean supportsImpersonation() {
- return false;
+ private Optional<ImpersonatedUserHolder> impersonatingHolder() {
+ return impersonatedUserHolders.stream()
+ .filter(ImpersonatedUserHolder::supportsImpersonation)
+ .findFirst();
}
/**
- * The impersonated user, if it has previously been set.
+ * Allows implementations to override the current user with another user.
+ *
+ * <p>
+ * If this service (for this request) does not
+ * {@link #supportsImpersonation() support impersonation}, then the
+ * request is just ignored.
+ * </p>
+ *
+ * <p>
+ * IMPORTANT: 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 #impersonateUser(String, List)
+ * @see #impersonatedUserIfAny()
* @see #isImpersonating()
* @see #stopImpersonating()
+ *
+ * @param userName - the name of the user to be impersonated
+ * @param roles - the collection of roles for the impersonated user to have.
*/
- default Optional<UserMemento> getImpersonatedUser() {
- return Optional.empty();
+ public void impersonateUser(final String userName, final List<String> roles) {
+ impersonatingHolder().ifPresent(x ->
+ {
+ val userMemento = UserMemento.ofNameAndRoleNames(userName, roles)
+ .withImpersonating(true);
+ x.setUserMemento(userMemento);
+ }
+ );
}
/**
- * Whether or not the user currently reported (in {@link #currentUser()}
- * and similar) is actually an impersonated user.
+ * For implementations that support impersonation, this is to
+ * programmatically stop impersonating a user
+ *
+ * <p>
+ * If this service (for this request) does not
+ * {@link #supportsImpersonation() support impersonation}, then the
+ * request is just ignored.
+ * </p>
+ *
+ * <p>
+ * Intended to be called at some point after
+ * {@link #impersonateUser(String, List)} would have been called.
+ * </p>
*
- * @see #currentUser()
* @see #supportsImpersonation()
* @see #impersonateUser(String, List)
- * @see #getImpersonatedUser()
- * @see #stopImpersonating()
+ * @see #isImpersonating()
*/
- default boolean isImpersonating() {
- return getImpersonatedUser().isPresent();
+ public void stopImpersonating() {
+ impersonatingHolder().ifPresent(ImpersonatedUserHolder::clearUserMemento);
+ }
+
+ private Optional<UserMemento> impersonatedUserIfAny() {
+ return impersonatingHolder().flatMap(ImpersonatedUserHolder::getUserMemento);
}
}
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 b731b99..4c94c65 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
@@ -58,7 +58,6 @@ import org.apache.isis.core.runtimeservices.transaction.TransactionServiceSpring
import org.apache.isis.core.runtimeservices.urlencoding.UrlEncodingServiceWithCompression;
import org.apache.isis.core.runtimeservices.user.ImpersonateMenuAdvisorDefault;
import org.apache.isis.core.runtimeservices.user.ImpersonatedUserHolderDefault;
-import org.apache.isis.core.runtimeservices.user.UserServiceDefault;
import org.apache.isis.core.runtimeservices.userreg.EmailNotificationServiceDefault;
import org.apache.isis.core.runtimeservices.wrapper.WrapperFactoryDefault;
import org.apache.isis.core.runtimeservices.xml.XmlServiceDefault;
@@ -98,7 +97,6 @@ import org.apache.isis.core.runtimeservices.xmlsnapshot.XmlSnapshotServiceDefaul
ScratchpadDefault.class,
TransactionServiceSpring.class,
UrlEncodingServiceWithCompression.class,
- UserServiceDefault.class,
WrapperFactoryDefault.class,
XmlServiceDefault.class,
XmlSnapshotServiceDefault.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/UserServiceDefault.java
deleted file mode 100644
index 4b85432..0000000
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/user/UserServiceDefault.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.runtimeservices.user;
-
-import java.util.List;
-import java.util.Optional;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.context.annotation.Primary;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Service;
-
-import org.apache.isis.applib.annotation.OrderPrecedence;
-import org.apache.isis.applib.services.iactnlayer.InteractionContext;
-import org.apache.isis.applib.services.iactnlayer.InteractionLayerTracker;
-import org.apache.isis.applib.services.user.ImpersonatedUserHolder;
-import org.apache.isis.applib.services.user.UserMemento;
-import org.apache.isis.applib.services.user.UserService;
-
-import lombok.RequiredArgsConstructor;
-import lombok.val;
-
-/**
- * This default implementation delegates to {@link ImpersonatedUserHolder} to
- * hold an impersonated user (if supported).
- */
-@Service
-@Named("isis.runtimeservices.UserServiceDefault")
-@Order(OrderPrecedence.MIDPOINT)
-@Primary
-@Qualifier("Default")
-@RequiredArgsConstructor(onConstructor_ = {@Inject})
-public class UserServiceDefault implements UserService {
-
- private final InteractionLayerTracker iInteractionLayerTracker;
- private final List<ImpersonatedUserHolder> impersonatedUserHolders;
-
- /**
- * Either the current user or the one being impersonated.
- */
- @Override
- public Optional<UserMemento> currentUser() {
- Optional<UserMemento> optional = getImpersonatedUser();
- return optional.isPresent()
- ? optional
- : iInteractionLayerTracker.currentInteractionContext().map(InteractionContext::getUser);
- }
-
- @Override
- public boolean supportsImpersonation() {
- return impersonatingHolder()
- .isPresent();
- }
-
- private Optional<ImpersonatedUserHolder> impersonatingHolder() {
- return impersonatedUserHolders.stream()
- .filter(ImpersonatedUserHolder::supportsImpersonation)
- .findFirst();
- }
-
- @Override
- public void impersonateUser(final String userName, final List<String> roles) {
- impersonatingHolder().ifPresent(x ->
- {
- val userMemento = UserMemento.ofNameAndRoleNames(userName, roles)
- .withImpersonating(true);
- x.setUserMemento(userMemento);
- }
- );
- }
-
- @Override
- public void stopImpersonating() {
- impersonatingHolder().ifPresent(ImpersonatedUserHolder::clearUserMemento);
- }
-
- @Override
- public Optional<UserMemento> getImpersonatedUser() {
- return impersonatingHolder().flatMap(ImpersonatedUserHolder::getUserMemento);
- }
-
-}
diff --git a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/shiro/ShiroSecmanLdap_restfulStressTest.java b/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/shiro/ShiroSecmanLdap_restfulStressTest.java
index 8e29fa5..46ba1b1 100644
--- a/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/shiro/ShiroSecmanLdap_restfulStressTest.java
+++ b/regressiontests/incubating/src/test/java/org/apache/isis/testdomain/shiro/ShiroSecmanLdap_restfulStressTest.java
@@ -129,7 +129,7 @@ class ShiroSecmanLdap_restfulStressTest extends AbstractShiroTest {
assertNotNull(httpSessionInfo);
- // impersonation in UserServiceDefault means that we _do_ now get
+ // impersonation in UserService means that we _do_ now get
// an httpSession as a side-effect
//assertEquals("no http-session", httpSessionInfo);
assertEquals("http-session attribute names: {}", httpSessionInfo);
diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/jpa/JpaInventoryResource.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/jpa/JpaInventoryResource.java
index 8406ba4..567fc4a 100644
--- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/jpa/JpaInventoryResource.java
+++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/jpa/JpaInventoryResource.java
@@ -93,7 +93,7 @@ public class JpaInventoryResource {
public String httpSessionInfo() {
// when running with basic-auth strategy, we don't want to create HttpSessions at all
- // however, this isn't the case if UserServiceDefault is in use, as that _dpes_
+ // however, this isn't the case if UserService is in use, as that _dpes_
// use HttpSession to hold any impersonated user.
val servletRequestAttributes =