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 2022/01/31 08:44:51 UTC
[isis] branch master updated: ISIS-2951: recognize System User as not to be vetoed (tenanted authorization)
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 e5a6b77 ISIS-2951: recognize System User as not to be vetoed (tenanted authorization)
e5a6b77 is described below
commit e5a6b77b77b35c6b9e008d386165f523b11ddb11
Author: Andi Huber <ah...@apache.org>
AuthorDate: Mon Jan 31 09:44:40 2022 +0100
ISIS-2951: recognize System User as not to be vetoed (tenanted
authorization)
---
.../isis/applib/services/user/UserMemento.java | 10 +-
.../isis/applib/services/user/UserService.java | 127 +++++++++++----------
.../facets/TenantedAuthorizationFacet.java | 3 +-
.../facets/TenantedAuthorizationFacetDefault.java | 29 +++--
4 files changed, 95 insertions(+), 74 deletions(-)
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/user/UserMemento.java b/api/applib/src/main/java/org/apache/isis/applib/services/user/UserMemento.java
index 922903d..76b1dff 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/user/UserMemento.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/user/UserMemento.java
@@ -24,6 +24,7 @@ import java.io.Serializable;
import java.net.URL;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.stream.Stream;
import org.springframework.context.event.EventListener;
@@ -304,8 +305,13 @@ public class UserMemento implements Serializable {
return streamRoleNames().anyMatch(myRoleName->myRoleName.equals(roleName));
}
-
-
+ /**
+ * Whether this {@link UserMemento} represent the <i>system user</i>.
+ */
+ @Programmatic
+ public boolean isSystem() {
+ return Objects.equals(SYSTEM_USER, this);
+ }
// -- UTILITY
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 831b179..0cfb909 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
@@ -21,13 +21,13 @@ package org.apache.isis.applib.services.user;
import java.util.List;
import java.util.Optional;
-import org.springframework.lang.Nullable;
import javax.annotation.Priority;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.apache.isis.applib.annotation.PriorityPrecedence;
@@ -45,16 +45,20 @@ import lombok.val;
*
* <p>
* If {@link SudoService} has been used to temporarily override the user and/or
- * roles, then this service will report the overridden values instead. This is within the context of a thread.
+ * 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
+ * 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).
+ * {@link InteractionLayerTracker#currentInteractionContext()
+ * InteractionLayerTracker} (the "real" user).
* </p>
*
* @see org.apache.isis.applib.services.iactnlayer.InteractionLayerTracker
@@ -69,7 +73,7 @@ import lombok.val;
@Named("isis.applib.UserService")
@Priority(PriorityPrecedence.MIDPOINT)
@Qualifier("Default")
-@RequiredArgsConstructor(onConstructor_ = {@Inject})
+@RequiredArgsConstructor(onConstructor_ = { @Inject })
public class UserService {
/**
@@ -80,61 +84,70 @@ public class UserService {
private final Provider<InteractionLayerTracker> iInteractionLayerTrackerProvider;
private final List<ImpersonatedUserHolder> impersonatedUserHolders;
-
/**
- * Returns the details about the current user, either the "effective" user (if being
- * {@link #impersonateUser(String, List, String) impersonated}) otherwise the "real" user (as obtained from
- * the {@link InteractionContext} of the current thread).
+ * Returns the details about the current user, either the "effective"
+ * user (if being {@link #impersonateUser(String, List, String) impersonated})
+ * otherwise the "real" user (as obtained from the
+ * {@link InteractionContext} of the current thread).
*/
public Optional<UserMemento> currentUser() {
val impersonatedUserIfAny = impersonatedUserIfAny();
return impersonatedUserIfAny.isPresent()
? impersonatedUserIfAny
- : iInteractionLayerTrackerProvider.get().currentInteractionContext().map(InteractionContext::getUser);
+ : iInteractionLayerTrackerProvider.get()
+ .currentInteractionContext()
+ .map(InteractionContext::getUser);
+ }
+
+ /**
+ * Whether the current user is the <i>system user</i> (as obtained from the
+ * {@link InteractionContext} of the current thread).
+ */
+ public boolean isCurrentUserWithSystemPrivileges() {
+ return currentUser()
+ .map(UserMemento::isSystem)
+ .orElse(false);
}
/**
- * Gets the details about the {@link #currentUser()} current user, if any (and returning <code>null</code> if
- * there is none).
+ * Gets the details about the {@link #currentUser()} current user, if any (and
+ * returning <code>null</code> if there is none).
*/
@Nullable
public UserMemento getUser() {
return currentUser().orElse(null);
}
-
/**
- * Gets the details about the {@link #currentUser()} current user, throwing an exception if there is none.
+ * 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.
+ * @throws IllegalStateException if no {@link InteractionContext} can be found
+ * with the current thread's context.
*/
public UserMemento currentUserElseFail() {
return currentUser()
- .orElseThrow(()->_Exceptions.illegalState("Current thread has no InteractionContext."));
+ .orElseThrow(() -> _Exceptions.illegalState("Current thread has no InteractionContext."));
}
/**
- * Optionally gets the {@link #currentUser() current user}'s name, obtained from {@link UserMemento}.
+ * Optionally gets the {@link #currentUser() current user}'s name, obtained from
+ * {@link UserMemento}.
*/
public Optional<String> currentUserName() {
- return currentUser()
- .map(UserMemento::getName);
+ return currentUser().map(UserMemento::getName);
}
/**
* Returns either the current user's name or else {@link #NOBODY}.
*/
public String currentUserNameElseNobody() {
- return currentUserName()
- .orElse(NOBODY);
+ return currentUserName().orElse(NOBODY);
}
-
-
-
/**
- * Whether or not the user currently reported (in {@link #currentUser()}
- * and similar) is actually an impersonated user.
+ * Whether or not the user currently reported (in {@link #currentUser()} and
+ * similar) is actually an impersonated user.
*
* @see #currentUser()
* @see #supportsImpersonation()
@@ -145,27 +158,26 @@ public class UserService {
return impersonatedUserIfAny().isPresent();
}
-
-
/**
* Whether impersonation is available for this request.
*
* <p>
- * 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.
+ * 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 #impersonateUser(String, List, String)
* @see #isImpersonating()
* @see #stopImpersonating()
*
- * @return whether impersonation is supported in the context of this (http) request.
+ * @return whether impersonation is supported in the context of this (http)
+ * request.
*/
public boolean supportsImpersonation() {
- return impersonatingHolder()
- .isPresent();
+ return impersonatingHolder().isPresent();
}
private Optional<ImpersonatedUserHolder> impersonatingHolder() {
@@ -185,50 +197,45 @@ public class UserService {
* 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.
+ * 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.
+ * 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 #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.
+ * @param userName - the name of the user to be impersonated
+ * @param roles - the collection of roles for the impersonated user
+ * to have.
* @param multiTenancyToken
*/
public void impersonateUser(
final String userName,
final List<String> roles,
final String multiTenancyToken) {
- impersonatingHolder().ifPresent(x ->
- x.setUserMemento(
- UserMemento.ofNameAndRoleNames(userName, roles)
- .withImpersonating(true)
- .withMultiTenancyToken(multiTenancyToken)
- )
- );
+ impersonatingHolder()
+ .ifPresent(x -> x.setUserMemento(UserMemento.ofNameAndRoleNames(userName, roles)
+ .withImpersonating(true).withMultiTenancyToken(multiTenancyToken)));
}
/**
- * For implementations that support impersonation, this is to
- * programmatically stop impersonating a 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.
+ * 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, String)} would have been called.
+ * Intended to be called at some point after
+ * {@link #impersonateUser(String, List, String)} would have been called.
* </p>
*
* @see #supportsImpersonation()
diff --git a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/facets/TenantedAuthorizationFacet.java b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/facets/TenantedAuthorizationFacet.java
index 25e8ee4..83b2ca6 100644
--- a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/facets/TenantedAuthorizationFacet.java
+++ b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/facets/TenantedAuthorizationFacet.java
@@ -26,6 +26,7 @@ import org.apache.isis.core.metamodel.interactions.HidingInteractionAdvisor;
* Optionally hide or disable an object, property, collection or action
* depending on the tenancy.
*/
-public interface TenantedAuthorizationFacet extends Facet, HidingInteractionAdvisor, DisablingInteractionAdvisor {
+public interface TenantedAuthorizationFacet
+extends Facet, HidingInteractionAdvisor, DisablingInteractionAdvisor {
}
diff --git a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/facets/TenantedAuthorizationFacetDefault.java b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/facets/TenantedAuthorizationFacetDefault.java
index 767ed2b..a861915 100644
--- a/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/facets/TenantedAuthorizationFacetDefault.java
+++ b/extensions/security/secman/integration/src/main/java/org/apache/isis/extensions/secman/integration/facets/TenantedAuthorizationFacetDefault.java
@@ -19,7 +19,6 @@
package org.apache.isis.extensions.secman.integration.facets;
import java.util.List;
-import java.util.concurrent.Callable;
import javax.inject.Provider;
@@ -34,7 +33,9 @@ import org.apache.isis.extensions.secman.applib.tenancy.spi.ApplicationTenancyEv
import org.apache.isis.extensions.secman.applib.user.dom.ApplicationUser;
import org.apache.isis.extensions.secman.applib.user.dom.ApplicationUserRepository;
-public class TenantedAuthorizationFacetDefault extends FacetAbstract implements TenantedAuthorizationFacet {
+public class TenantedAuthorizationFacetDefault
+extends FacetAbstract
+implements TenantedAuthorizationFacet {
private static final Class<? extends Facet> type() {
return TenantedAuthorizationFacet.class;
@@ -61,7 +62,9 @@ public class TenantedAuthorizationFacetDefault extends FacetAbstract implements
@Override
public String hides(final VisibilityContext ic) {
- if(evaluators == null || evaluators.isEmpty()) {
+ if(evaluators == null
+ || evaluators.isEmpty()
+ || userService.isCurrentUserWithSystemPrivileges()) {
return null;
}
@@ -86,7 +89,10 @@ public class TenantedAuthorizationFacetDefault extends FacetAbstract implements
@Override
public String disables(final UsabilityContext ic) {
- if(evaluators == null || evaluators.isEmpty()) {
+
+ if(evaluators == null
+ || evaluators.isEmpty()
+ || userService.isCurrentUserWithSystemPrivileges()) {
return null;
}
@@ -110,15 +116,16 @@ public class TenantedAuthorizationFacetDefault extends FacetAbstract implements
/**
- * Per {@link #findApplicationUserNoCache(String)}, cached for the request using the {@link QueryResultsCache}.
+ * Per {@link #findApplicationUserNoCache(String)},
+ * cached for the request using the {@link QueryResultsCache}.
*/
protected ApplicationUser findApplicationUser(final String userName) {
- return queryResultsCacheProvider.get().execute(new Callable<ApplicationUser>() {
- @Override
- public ApplicationUser call() throws Exception {
- return findApplicationUserNoCache(userName);
- }
- }, TenantedAuthorizationFacetDefault.class, "findApplicationUser", userName);
+ return queryResultsCacheProvider.get()
+ .execute(
+ ()->findApplicationUserNoCache(userName),
+ TenantedAuthorizationFacetDefault.class,
+ "findApplicationUser",
+ userName);
}
protected ApplicationUser findApplicationUserNoCache(final String userName) {