Skip to content

Commit b245666

Browse files
authored
Rework project users for sanity and consistency (#7731)
1 parent 6001b16 commit b245666

10 files changed

Lines changed: 101 additions & 293 deletions

File tree

api/src/org/labkey/api/data/NestedRenderContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ public SimpleFilter buildFilter(TableInfo tinfo, List<ColumnInfo> displayColumns
131131
fullSQL.append(" ) Limited )");
132132

133133
// Apply a filter that restricts the group ids to the right "page" of data
134-
result.addClause(new SimpleFilter.SQLClause(fullSQL.getSQL(), fullSQL.getParamsArray()));
134+
result.addClause(new SimpleFilter.SQLClause(fullSQL));
135135
}
136136
return result;
137137
}

api/src/org/labkey/api/security/SecurityManager.java

Lines changed: 0 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
import org.labkey.api.data.PropertyManager.WritablePropertyMap;
5656
import org.labkey.api.data.RuntimeSQLException;
5757
import org.labkey.api.data.SQLFragment;
58-
import org.labkey.api.data.Selector;
5958
import org.labkey.api.data.SimpleFilter;
6059
import org.labkey.api.data.SqlExecutor;
6160
import org.labkey.api.data.SqlSelector;
@@ -1883,126 +1882,6 @@ public static String getMembershipPathwayHTMLDisplay(Set<List<UserPrincipal>> pa
18831882
sb.append("<BR/>");
18841883
}
18851884
return sb.toString();
1886-
}
1887-
1888-
// TODO: Redundant with getProjectUsers() -- this approach should be more efficient for simple cases
1889-
// TODO: Also redundant with getFolderUserids()
1890-
// TODO: Cache this set
1891-
public static Set<Integer> getProjectUsersIds(Container c)
1892-
{
1893-
SQLFragment sql = getProjectUsersSQL(c.getProject());
1894-
sql.insert(0, "SELECT DISTINCT members.UserId ");
1895-
1896-
Selector selector = new SqlSelector(core.getSchema(), sql);
1897-
return new HashSet<>(selector.getCollection(Integer.class));
1898-
}
1899-
1900-
// True fragment -- need to prepend SELECT DISTINCT() or IN () for this to be valid SQL
1901-
public static SQLFragment getProjectUsersSQL(Container c)
1902-
{
1903-
return new SQLFragment("FROM " + core.getTableInfoMembers() + " members INNER JOIN " + core.getTableInfoUsers() + " users ON members.UserId = users.UserId\n" +
1904-
"INNER JOIN " + core.getTableInfoPrincipals() + " groups ON members.GroupId = groups.UserId\n" +
1905-
"WHERE (groups.Container = ?)", c);
1906-
}
1907-
1908-
public static @NotNull List<User> getProjectUsers(Container c)
1909-
{
1910-
return getProjectUsers(c, false, true);
1911-
}
1912-
1913-
public static @NotNull List<User> getProjectUsers(Container c, boolean includeGlobal, boolean includeInactive)
1914-
{
1915-
if (c != null && !c.isProject())
1916-
c = c.getProject();
1917-
1918-
List<Group> groups = getGroups(c, includeGlobal);
1919-
Set<String> emails = new HashSet<>();
1920-
1921-
//get members for each group
1922-
ArrayList<User> projectUsers = new ArrayList<>();
1923-
Set<User> members;
1924-
1925-
for (Group g : groups)
1926-
{
1927-
if (g.isGuests() || g.isUsers())
1928-
continue;
1929-
1930-
// TODO: currently only getting members that are users (no groups). should this be changed to get users of member groups?
1931-
members = getGroupMembers(g, includeInactive ? MemberType.ACTIVE_AND_INACTIVE_USERS : MemberType.ACTIVE_USERS);
1932-
1933-
//add this group's members to hashset
1934-
if (!members.isEmpty())
1935-
{
1936-
//get list of users from email
1937-
for (UserPrincipal member : members)
1938-
{
1939-
User user = UserManager.getUser(member.getUserId());
1940-
if (null != user && emails.add(user.getEmail()))
1941-
projectUsers.add(user);
1942-
}
1943-
}
1944-
}
1945-
1946-
return projectUsers;
1947-
}
1948-
1949-
public static Collection<Integer> getFolderUserids(Container c)
1950-
{
1951-
Container project = (c.isProject() || c.isRoot()) ? c : c.getProject();
1952-
SecurityPolicy policy = c.getPolicy();
1953-
1954-
//don't filter if all site users is playing a role
1955-
Group allSiteUsers = getGroup(Group.groupUsers);
1956-
if (policy.getAssignedRoles(allSiteUsers).findAny().isPresent())
1957-
{
1958-
// Just select all users
1959-
SQLFragment sql = new SQLFragment("SELECT u.UserId FROM ");
1960-
sql.append(core.getTableInfoPrincipals(), "u");
1961-
sql.append(" WHERE u.type='u'");
1962-
1963-
return new SqlSelector(core.getSchema(), sql).getCollection(Integer.class);
1964-
}
1965-
1966-
//users "in the project" consists of:
1967-
// - users who are members of a project group
1968-
// - users who belong to a site group that has a role assignment in the policy for the specified folder
1969-
// - users who have a direct role assignment in the policy for the specified folder
1970-
1971-
Set<Integer> userIds = new HashSet<>();
1972-
1973-
// Add all project groups
1974-
Set<Group> groupsToExpand = new HashSet<>(getGroups(project, false));
1975-
1976-
// Look for users and site groups that have direct assignment to the container
1977-
for (RoleAssignment roleAssignment : c.getPolicy().getAssignments())
1978-
{
1979-
User user = UserManager.getUser(roleAssignment.getUserId());
1980-
if (user != null)
1981-
{
1982-
userIds.add(user.getUserId());
1983-
}
1984-
else
1985-
{
1986-
Group assignedGroup = getGroup(roleAssignment.getUserId());
1987-
if (assignedGroup != null && !assignedGroup.isProjectGroup())
1988-
{
1989-
// Add all site groups
1990-
groupsToExpand.add(assignedGroup);
1991-
}
1992-
}
1993-
}
1994-
1995-
// Find the users who are members of all the relevant site groups
1996-
for (Group group : groupsToExpand)
1997-
{
1998-
Set<User> groupMembers = getAllGroupMembers(group, MemberType.ACTIVE_AND_INACTIVE_USERS);
1999-
for (User groupMember : groupMembers)
2000-
{
2001-
userIds.add(groupMember.getUserId());
2002-
}
2003-
}
2004-
2005-
return userIds;
20061885
}
20071886

20081887
/**

core/src/org/labkey/core/query/CoreQuerySchema.java

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
import org.labkey.api.security.UserPrincipal;
6363
import org.labkey.api.security.permissions.AdminPermission;
6464
import org.labkey.api.security.permissions.ApplicationAdminPermission;
65+
import org.labkey.api.security.permissions.ReadPermission;
6566
import org.labkey.api.security.permissions.SeeGroupDetailsPermission;
6667
import org.labkey.api.security.permissions.SeeUserDetailsPermission;
6768
import org.labkey.api.security.permissions.TroubleshooterPermission;
@@ -76,14 +77,13 @@
7677

7778
import java.util.ArrayList;
7879
import java.util.Collections;
79-
import java.util.HashSet;
8080
import java.util.List;
81-
import java.util.Map;
8281
import java.util.Set;
82+
import java.util.stream.Collectors;
8383

8484
public class CoreQuerySchema extends UserSchema
8585
{
86-
private Set<Integer> _projectUserIds;
86+
private Set<Integer> _folderUserIds;
8787
private final boolean _mustCheckPermissions;
8888

8989
public static final String NAME = "core";
@@ -483,17 +483,15 @@ public TableInfo getUsers()
483483
}
484484
else
485485
{
486-
if (_projectUserIds == null)
486+
// All users with read permissions in this folder
487+
if (_folderUserIds == null)
487488
{
488-
Set<Integer> projectUserIds = new HashSet<>(SecurityManager.getFolderUserids(getContainer()));
489-
// Add app admins and site admins (they both have ApplicationAdminPermission)
490-
SecurityManager.getUsersWithPermissions(ContainerManager.getRoot(), Set.of(ApplicationAdminPermission.class)).stream()
491-
.map(User::getUserId)
492-
.forEach(projectUserIds::add);
493-
_projectUserIds = projectUserIds;
489+
_folderUserIds = SecurityManager.getUsersWithPermissions(getContainer(), Set.of(ReadPermission.class)).stream()
490+
.map(UserPrincipal::getUserId)
491+
.collect(Collectors.toSet());
494492
}
495493
ColumnInfo userid = users.getRealTable().getColumn("userid");
496-
users.addInClause(userid, _projectUserIds);
494+
users.addInClause(userid, _folderUserIds);
497495

498496
addGroupsColumn(users);
499497
addAvatarColumn(users);

core/src/org/labkey/core/query/UsersTable.java

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@
2828
import org.labkey.api.data.Container;
2929
import org.labkey.api.data.ContainerManager;
3030
import org.labkey.api.data.NullColumnInfo;
31-
import org.labkey.api.data.SQLFragment;
3231
import org.labkey.api.data.SimpleFilter;
32+
import org.labkey.api.data.SimpleFilter.InClause;
3333
import org.labkey.api.data.TableInfo;
34-
import org.labkey.api.data.dialect.SqlDialect;
3534
import org.labkey.api.exp.PropertyColumn;
3635
import org.labkey.api.exp.PropertyDescriptor;
3736
import org.labkey.api.exp.property.Domain;
@@ -92,10 +91,6 @@
9291

9392
import static org.labkey.api.util.IntegerUtils.asInteger;
9493

95-
/**
96-
* User: klum
97-
* Date: 9/19/12
98-
*/
9994
public class UsersTable extends SimpleUserSchema.SimpleTable<UserSchema>
10095
{
10196
private Set<String> _illegalColumns;
@@ -116,7 +111,7 @@ public UsersTable(UserSchema schema, TableInfo table)
116111
{
117112
super(schema, table, null);
118113

119-
setDescription("Contains all users who are members of the current project." +
114+
setDescription("Contains all users with read permissions in the current project." +
120115
" The data in this table are available only to users who are signed-in (not guests). Guests see no rows." +
121116
" Signed-in users see the columns UserId, EntityId, and DisplayName." +
122117
" Users granted the '" + SeeUserAndGroupDetailsRole.NAME + "' role see all standard and custom columns.");
@@ -414,6 +409,7 @@ public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class<? exten
414409
return super.hasPermission(user, perm);
415410
}
416411

412+
// Used by ShowUserHistoryAction only (is not applied to UsersTable)
417413
public static SimpleFilter authorizeAndGetProjectMemberFilter(@NotNull Container c, @NotNull User u, String userIdColumnName) throws UnauthorizedException
418414
{
419415
SimpleFilter filter = new SimpleFilter();
@@ -425,29 +421,10 @@ public static SimpleFilter authorizeAndGetProjectMemberFilter(@NotNull Container
425421
}
426422
else
427423
{
428-
SQLFragment sql = SecurityManager.getProjectUsersSQL(c.getProject());
429-
430424
final FieldKey userIdColumnFieldKey = new FieldKey(null, userIdColumnName);
431-
filter.addClause(new SimpleFilter.SQLClause(sql.getSQL(), sql.getParamsArray(), userIdColumnFieldKey)
432-
{
433-
@Override
434-
public SQLFragment toSQLFragment(Map<FieldKey, ? extends ColumnInfo> columnMap, SqlDialect dialect)
435-
{
436-
ColumnInfo col = columnMap.get(userIdColumnFieldKey);
437-
438-
// NOTE: Ideally we would use col.getValueSql() here instead
439-
SQLFragment sql = new SQLFragment();
440-
441-
if (col != null)
442-
sql.appendIdentifier(col.getAlias());
443-
else
444-
sql.append(userIdColumnFieldKey);
445-
sql.append(" IN (SELECT members.UserId ");
446-
sql.append(super.toSQLFragment(columnMap, dialect));
447-
sql.append(")");
448-
return sql;
449-
}
450-
});
425+
// Consider: could short-circuit optimize if guests or all site users have read permissions
426+
InClause clause = new InClause(userIdColumnFieldKey, SecurityManager.getUsersWithPermissions(c, Set.of(ReadPermission.class)));
427+
filter.addClause(clause);
451428
}
452429
return filter;
453430
}

core/src/org/labkey/core/security/SecurityApiActions.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,8 +1578,7 @@ public ApiResponse execute(IdForm form, BindException errors) throws Exception
15781578
Container c = getContainer();
15791579
if (!c.isRoot())
15801580
{
1581-
List<User> projectUsers = SecurityManager.getProjectUsers(c);
1582-
if (!projectUsers.contains(user))
1581+
if (!c.hasPermission(user, ReadPermission.class))
15831582
throw new IllegalArgumentException("User id " + form.getId() + " does not exist in the folder: " + c.getPath());
15841583
}
15851584

0 commit comments

Comments
 (0)