1818import org .junit .BeforeClass ;
1919import org .junit .Test ;
2020import org .junit .experimental .categories .Category ;
21+ import org .labkey .api .security .permissions .Permission ;
22+ import org .labkey .api .security .permissions .ReadPermission ;
2123import org .labkey .remoteapi .CommandException ;
24+ import org .labkey .remoteapi .CommandResponse ;
2225import org .labkey .remoteapi .Connection ;
2326import org .labkey .remoteapi .SimpleGetCommand ;
2427import org .labkey .test .BaseWebDriverTest ;
2932import org .labkey .test .components .DomainDesignerPage ;
3033import org .labkey .test .components .domain .DomainFormPanel ;
3134import org .labkey .test .pages .query .ExecuteQueryPage ;
35+ import org .labkey .test .pages .user .ShowUsersPage ;
3236import org .labkey .test .pages .user .UpdateUserDetailsPage ;
3337import org .labkey .test .params .FieldDefinition ;
3438import org .labkey .test .util .ApiPermissionsHelper ;
39+ import org .labkey .test .util .DataRegionTable ;
3540import org .labkey .test .util .DataRegionTable .DataRegionFinder ;
3641import org .labkey .test .util .LogMethod ;
3742import org .labkey .test .util .PasswordUtil ;
3843import org .labkey .test .util .PortalHelper ;
3944
4045import java .io .IOException ;
46+ import java .util .HashSet ;
4147import java .util .List ;
4248import java .util .Map ;
49+ import java .util .Set ;
50+ import java .util .stream .Collectors ;
4351
4452import static org .hamcrest .CoreMatchers .allOf ;
4553import static org .hamcrest .CoreMatchers .containsString ;
4654import static org .hamcrest .CoreMatchers .not ;
4755import static org .hamcrest .MatcherAssert .assertThat ;
56+ import static org .junit .Assert .assertEquals ;
57+ import static org .junit .Assert .assertFalse ;
58+ import static org .labkey .test .util .PermissionsHelper .PROJECT_ADMIN_ROLE ;
4859import static org .labkey .test .util .PermissionsHelper .READER_ROLE ;
4960import static org .labkey .test .util .PermissionsHelper .SITE_ADMIN_ROLE ;
5061
@@ -57,6 +68,8 @@ public class UserDetailsPermissionTest extends BaseWebDriverTest
5768 private static final String USER_INFO_VIEWER =
"[email protected] " ;
5869 private static final String IMPERSONATED_USER =
"[email protected] " ;
5970 private static final String CHECKED_USER =
"[email protected] " ;
71+ private static final String PROJECT_ADMIN =
"[email protected] " ;
72+ private static final String NON_MEMBER =
"[email protected] " ;
6073 private static final String EMAIL_TEST_LIST = "My Users" ;
6174 private static final String CUSTOM_USER_COLUMN = "UserTablePermTest" ;
6275 private static final String HIDDEN_COL_VIEW = "hiddenColView" ;
@@ -73,7 +86,7 @@ protected void doCleanup(boolean afterTest) throws TestTimeoutException
7386 {
7487 super .doCleanup (afterTest );
7588
76- _userHelper .deleteUsers (false , USER_INFO_VIEWER , IMPERSONATED_USER , CHECKED_USER , ADMIN_USER );
89+ _userHelper .deleteUsers (false , USER_INFO_VIEWER , IMPERSONATED_USER , CHECKED_USER , ADMIN_USER , PROJECT_ADMIN , NON_MEMBER );
7790 }
7891
7992 @ Override
@@ -116,6 +129,14 @@ private void doSetup()
116129 apiPermissionsHelper .setPermissions (TEST_GROUP , READER_ROLE );
117130 apiPermissionsHelper .setSiteRoleUserPermissions (USER_INFO_VIEWER , "See User and Group Details" );
118131
132+ // A project administrator (no site-wide User Management permission) is used to verify that user details
133+ // render from the Project Users page.
134+ _userHelper .createUser (PROJECT_ADMIN , true , true );
135+ new ApiPermissionsHelper ("/" + getProjectName ()).setUserPermissions (PROJECT_ADMIN , PROJECT_ADMIN_ROLE );
136+
137+ // A user with no role in the project (no read access) that should never appear on the Project Users page.
138+ _userHelper .createUser (NON_MEMBER , true , true );
139+
119140 impersonate (ADMIN_USER );
120141 {
121142 UpdateUserDetailsPage page = goToMyAccount ().clickEdit ();
@@ -208,6 +229,52 @@ public void testUserVisibilityViaContactsWebPart()
208229 assertTextPresent (CHECKED_USER , ADMIN_USER , HIDDEN_STRING , TEST_GROUP );
209230 }
210231
232+ @ Test
233+ public void testProjectUsers () throws Exception
234+ {
235+ // The list of project users will be all users with read access to the folder.
236+ Set <String > projectUsers = getUsersWithAccess (getProjectName (), ReadPermission .class );
237+ assertFalse ("A user with no project role must not have read access" ,
238+ projectUsers .contains (NON_MEMBER ));
239+
240+ // Both a site administrator and a project administrator should see the project's users and be able to open
241+ // the details view for every one of them.
242+ for (String admin : List .of (ADMIN_USER , PROJECT_ADMIN ))
243+ {
244+ impersonate (admin );
245+ goToProjectHome ();
246+
247+ log ("Verify the Project Users page, impersonating " + admin );
248+ ShowUsersPage projectUsersPage = goToProjectUsers ();
249+ assertEquals ("Project Users page displaying the wrong set of users." ,
250+ projectUsers , new HashSet <>(projectUsersPage .getUsersTable ().getColumnDataAsText ("Email" )));
251+
252+ log ("Verify " + admin + " can view the details page of every project user" );
253+ for (String userEmail : projectUsers )
254+ {
255+ DataRegionTable usersTable = goToProjectUsers ().getUsersTable ();
256+ clickAndWait (usersTable .detailsLink (usersTable .getRowIndexStrict ("Email" , userEmail )));
257+ assertTextPresent (userEmail , _userHelper .getDisplayNameForEmail (userEmail ));
258+ }
259+
260+ stopImpersonating ();
261+ }
262+ }
263+
264+ /**
265+ * Returns the set of user emails who have permission to the folder
266+ */
267+ private Set <String > getUsersWithAccess (String folderPath , Class <? extends Permission > perm ) throws IOException , CommandException
268+ {
269+ SimpleGetCommand command = new SimpleGetCommand ("user" , "getUsersWithPermissions" );
270+ command .setParameters (Map .of ("permissions" , perm .getName ()));
271+ CommandResponse response = command .execute (createDefaultConnection (), folderPath );
272+ List <Map <String , Object >> users = response .getProperty ("users" );
273+
274+ return users .stream ().
275+ map (m -> (String ) m .get ("email" )).collect (Collectors .toSet ());
276+ }
277+
211278 @ Test
212279 public void testUserVisibilityAutoCompleteApi () throws Exception
213280 {
0 commit comments