Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/src/org/labkey/api/ApiModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
import org.labkey.api.security.SecurityManager;
import org.labkey.api.security.UserManager;
import org.labkey.api.security.ValidEmail;
import org.labkey.api.security.impersonation.ImpersonationTestCase;
import org.labkey.api.settings.AppProps;
import org.labkey.api.settings.AppPropsTestCase;
import org.labkey.api.settings.BaseServerProperties;
Expand Down Expand Up @@ -398,6 +399,7 @@ public void registerServlets(ServletContext servletCtx)
FileUtil.TestCase.class,
GenerateUniqueDataIterator.TestCase.class,
HelpTopic.TestCase.class,
ImpersonationTestCase.class,
InlineInClauseGenerator.TestCase.class,
JSONDataLoader.HeaderMatchTest.class,
JSONDataLoader.MetadataTest.class,
Expand Down
5 changes: 3 additions & 2 deletions api/src/org/labkey/api/data/Container.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Strings;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -827,7 +828,7 @@ public static boolean isLegalName(String name, boolean isProject, StringBuilder
return false;
}

if (StringUtils.endsWithIgnoreCase(name, ".view") || StringUtils.endsWithIgnoreCase(name, ".api") || StringUtils.endsWithIgnoreCase(name, ".post"))
if (Strings.CI.endsWith(name, ".view") || Strings.CI.endsWith(name, ".api") || Strings.CI.endsWith(name, ".post"))
{
error.append("Folder name should not end with '.view', '.api', or '.post'.");
return false;
Expand Down Expand Up @@ -1249,7 +1250,7 @@ public boolean getAsBoolean()
}

// always put the required modules in the set
// note that this will pickup the modules from the folder type's getActiveModules()
// note that this will pick up the modules from the folder type's getActiveModules()
Set<Module> modules = new HashSet<>(getRequiredModules());

// add all modules found in user preferences:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
/**
* Designates actions that should not enforce forbidden project checking, which is used to support impersonation
* container restrictions and project locking. Use with caution. Allows project admins to perform actions outside
* of the project they administer and non-admins the ability to invoke actions in locked projects.
* the project they administer and non-admins the ability to invoke actions in locked projects.
*
* @see org.labkey.api.action.PermissionCheckableAction#checkPermissions()
* @see org.labkey.api.data.Container#isForbiddenProject(org.labkey.api.security.User)
Expand Down
15 changes: 4 additions & 11 deletions api/src/org/labkey/api/security/NormalPermissionsContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@
import org.labkey.api.security.impersonation.ImpersonationContextFactory;
import org.labkey.api.security.impersonation.RoleImpersonationContextFactory;
import org.labkey.api.security.impersonation.UserImpersonationContextFactory;
import org.labkey.api.security.permissions.AdminPermission;
import org.labkey.api.security.permissions.CanImpersonatePrivilegedSiteRolesPermission;
import org.labkey.api.security.permissions.ImpersonatePermission;
import org.labkey.api.util.PageFlowUtil;
import org.labkey.api.view.ActionURL;
import org.labkey.api.view.NavTree;
Expand Down Expand Up @@ -93,22 +92,16 @@ public void addMenu(NavTree menu, Container c, User user, ActionURL currentURL)
{
@Nullable Container project = c.getProject();

// Must be site or project admin (folder admins can't impersonate)
if (user.hasRootAdminPermission() || (null != project && project.hasPermission(user, AdminPermission.class)))
// Site admin, app admin, and impersonating troubleshooter can impersonate anywhere; project admin can
// impersonate in that project. Folder admins can't impersonate.
if (user.hasRootPermission(ImpersonatePermission.class) || (project != null && project.hasPermission(user, ImpersonatePermission.class)))
{
NavTree impersonateMenu = new NavTree("Impersonate");
UserImpersonationContextFactory.addMenu(impersonateMenu);
GroupImpersonationContextFactory.addMenu(impersonateMenu);
RoleImpersonationContextFactory.addMenu(impersonateMenu);
menu.addChild(impersonateMenu);
}
// Or Impersonating Troubleshooter to impersonate site roles only
else if (null == project && user.hasRootPermission(CanImpersonatePrivilegedSiteRolesPermission.class))
{
NavTree impersonateMenu = new NavTree("Impersonate");
RoleImpersonationContextFactory.addMenu(impersonateMenu);
menu.addChild(impersonateMenu);
}
}

NavTree signOut = new NavTree("Sign Out", PageFlowUtil.urlProvider(LoginUrls.class).getLogoutURL(c));
Expand Down
2 changes: 1 addition & 1 deletion api/src/org/labkey/api/security/PermissionsContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ default Stream<Role> getAssignedRoles(User user, SecurableResource resource)
if (!(role instanceof AbstractRootContainerRole siteRole))
throw new IllegalStateException("Root roles should all be AbstractRootContainerRole");

return siteRole.isAvailableEverywhere() || resource.equals(root);
return siteRole.isApplicableOutsideRoot() || resource.equals(root);
});

if (!resource.equals(root))
Expand Down
8 changes: 4 additions & 4 deletions api/src/org/labkey/api/security/SecurityManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
import org.labkey.api.security.permissions.AbstractPermission;
import org.labkey.api.security.permissions.AddUserPermission;
import org.labkey.api.security.permissions.AdminPermission;
import org.labkey.api.security.permissions.CanImpersonateSiteRolesPermission;
import org.labkey.api.security.permissions.ImpersonatePermission;
import org.labkey.api.security.permissions.DeletePermission;
import org.labkey.api.security.permissions.InsertPermission;
import org.labkey.api.security.permissions.Permission;
Expand Down Expand Up @@ -822,7 +822,7 @@ public static void impersonateUser(ViewContext viewContext, User impersonatedUse
@Nullable Container project = viewContext.getContainer().getProject();
User user = viewContext.getUser();

if (user.hasRootAdminPermission())
if (user.hasRootPermission(ImpersonatePermission.class))
project = null;

impersonate(viewContext, new UserImpersonationContextFactory(project, user, impersonatedUser, returnUrl));
Expand All @@ -839,7 +839,7 @@ public static void impersonateRoles(ViewContext viewContext, Collection<Role> ne
@Nullable Container project = viewContext.getContainer().getProject();
User user = viewContext.getUser();

if (user.hasRootPermission(CanImpersonateSiteRolesPermission.class))
if (user.hasRootPermission(ImpersonatePermission.class))
project = null;

impersonate(viewContext, new RoleImpersonationContextFactory(project, user, newImpersonationRoles, currentImpersonationRoles, returnUrl));
Expand Down Expand Up @@ -1412,7 +1412,7 @@ public static void ensureAtLeastOneRootAdminExists()
}

// A permission class that uniquely identifies the root admins, of which we insist there must be at least one
public static final Class<? extends Permission> ROOT_ADMIN_PERMISSION = CanImpersonateSiteRolesPermission.class;
public static final Class<? extends Permission> ROOT_ADMIN_PERMISSION = ImpersonatePermission.class;

public static boolean isRootAdmin(User user)
{
Expand Down
4 changes: 2 additions & 2 deletions api/src/org/labkey/api/security/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
import org.labkey.api.security.permissions.AnalystPermission;
import org.labkey.api.security.permissions.ApplicationAdminPermission;
import org.labkey.api.security.permissions.BrowserDeveloperPermission;
import org.labkey.api.security.permissions.CanImpersonateSiteRolesPermission;
import org.labkey.api.security.permissions.ImpersonatePermission;
import org.labkey.api.security.permissions.DeletePermission;
import org.labkey.api.security.permissions.InsertPermission;
import org.labkey.api.security.permissions.Permission;
Expand Down Expand Up @@ -594,7 +594,7 @@ public static JSONObject getUserProps(User user, User currentUser, @Nullable Con
props.put("isAdmin", nonNullContainer && container.hasPermission(user, AdminPermission.class));
props.put("isRootAdmin", user.hasRootAdminPermission());
props.put("isSystemAdmin", user.hasSiteAdminPermission());
props.put("canImpersonateSiteRoles", user.hasRootPermission(CanImpersonateSiteRolesPermission.class));
props.put("canImpersonateSiteRoles", user.hasRootPermission(ImpersonatePermission.class));
props.put("isGuest", user.isGuest());
props.put("isDeveloper", user.isBrowserDev());
props.put("isAnalyst", user.hasRootPermission(AnalystPermission.class));
Expand Down
10 changes: 5 additions & 5 deletions api/src/org/labkey/api/security/UserCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ private UserCache()
return null != user ? user.cloneUser() : null;
}

// Returns a deep copy of the active users list, allowing callers to interrogate user permissions without affecting
// cached users. Collection is ordered by email... maybe it should be ordered by display name?
// Returns a mutable deep copy of the active users list, allowing callers to interrogate user permissions without
// affecting cached users. Collection is ordered by email... maybe it should be ordered by display name?
static @NotNull Collection<User> getActiveUsers()
{
List<User> activeUsers = getUserCollections().getActiveUsers();
Expand All @@ -95,7 +95,7 @@ private UserCache()
.collect(Collectors.toCollection(LinkedList::new));
}

// Returns a deep copy of the users list including deactivated accounts, allowing callers to interrogate user permissions
// Returns a mutable deep copy of the users list including deactivated accounts, allowing callers to interrogate user permissions
// without affecting cached users. Collection is ordered randomly... maybe it should be ordered by display name?
static @NotNull Collection<User> getActiveAndInactiveUsers()
{
Expand All @@ -104,7 +104,7 @@ private UserCache()
return users
.stream()
.map(User::cloneUser)
.collect(Collectors.toList());
.collect(Collectors.toCollection(LinkedList::new));
}

static @NotNull List<Integer> getUserIds()
Expand All @@ -114,7 +114,7 @@ private UserCache()

static @NotNull Map<ValidEmail, User> getUserEmailMap()
{
return Collections.unmodifiableMap(getUserCollections().getEmailMap());
return getUserCollections().getEmailMap();
}

static int getActiveUserCount()
Expand Down
8 changes: 5 additions & 3 deletions api/src/org/labkey/api/security/UserManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
import org.labkey.api.security.SecurityManager.UserManagementException;
import org.labkey.api.security.permissions.AbstractActionPermissionTest;
import org.labkey.api.security.permissions.ApplicationAdminPermission;
import org.labkey.api.security.permissions.CanImpersonatePrivilegedSiteRolesPermission;
import org.labkey.api.security.permissions.ImpersonatePrivilegedSiteRolesPermission;
import org.labkey.api.security.permissions.SiteAdminPermission;
import org.labkey.api.security.roles.ApplicationAdminRole;
import org.labkey.api.security.roles.SiteAdminRole;
Expand Down Expand Up @@ -162,7 +162,7 @@ public static void addUserListener(UserListener listener)
public static void addUserListener(UserListener listener, boolean meFirst)
{
if (meFirst)
_listeners.add(0, listener);
_listeners.addFirst(listener);
else
_listeners.add(listener);
}
Expand Down Expand Up @@ -670,12 +670,14 @@ public static String getEmailForId(Integer userId)
}

@NotNull
// Returns a mutable collection
public static Collection<User> getActiveUsers()
{
return getUsers(false);
}

@NotNull
// Returns a mutable collection
public static Collection<User> getUsers(boolean includeInactive)
{
return includeInactive ? UserCache.getActiveAndInactiveUsers() : UserCache.getActiveUsers() ;
Expand Down Expand Up @@ -1305,7 +1307,7 @@ public void testPermissionsRetrieval()
assertTrue("Expected all SiteAdmins to be in the AppAdmins list",
appAdmins.containsAll(siteAdmins));

List<User> privilegedUsers = SecurityManager.getUsersWithPermissions(ContainerManager.getRoot(), Set.of(CanImpersonatePrivilegedSiteRolesPermission.class));
List<User> privilegedUsers = SecurityManager.getUsersWithPermissions(ContainerManager.getRoot(), Set.of(ImpersonatePrivilegedSiteRolesPermission.class));
assertTrue("Expected all users with privileged role impersonation permissions to have a privileged role",
privilegedUsers.stream().allMatch(User::hasPrivilegedRole));

Expand Down
Loading