package org.molgenis.ui.admin.usermanager; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.molgenis.auth.*; import org.molgenis.data.DataService; import org.molgenis.data.Query; import org.molgenis.data.support.QueryImpl; import org.molgenis.security.core.utils.SecurityUtils; import org.molgenis.security.user.UserDetailsService; import org.molgenis.ui.admin.usermanager.UserManagerServiceImplTest.Config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.stream.Stream; import static java.util.Collections.singletonList; import static org.mockito.Mockito.*; import static org.molgenis.auth.GroupMemberMetaData.GROUP_MEMBER; import static org.testng.Assert.assertEquals; @ContextConfiguration(classes = { Config.class }) public class UserManagerServiceImplTest extends AbstractTestNGSpringContextTests { private static Authentication AUTHENTICATION_PREVIOUS; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) static class Config extends WebSecurityConfigurerAdapter { @Bean public DataService dataService() { return mock(DataService.class); } @Bean public UserManagerService userManagerService() { return new UserManagerServiceImpl(dataService(), molgenisGroupMemberFactory()); } @Override protected org.springframework.security.core.userdetails.UserDetailsService userDetailsService() { return mock(UserDetailsService.class); } @Bean @Override public org.springframework.security.core.userdetails.UserDetailsService userDetailsServiceBean() throws Exception { return userDetailsService(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Autowired @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser("user").password("password").authorities("ROLE_USER"); } @Bean GroupMemberFactory molgenisGroupMemberFactory() { return mock(GroupMemberFactory.class); } } @Autowired private GroupMemberFactory groupMemberFactory; @Autowired private UserManagerService userManagerService; @Autowired private DataService dataService; @BeforeClass public void setUpBeforeClass() { AUTHENTICATION_PREVIOUS = SecurityContextHolder.getContext().getAuthentication(); } @AfterClass public static void tearDownAfterClass() { SecurityContextHolder.getContext().setAuthentication(AUTHENTICATION_PREVIOUS); } @Test(expectedExceptions = NullPointerException.class) public void userManagerServiceImpl() { new UserManagerServiceImpl(null, groupMemberFactory); } @Test public void getAllMolgenisUsersSu() { String molgenisUserId0 = "id0"; String molgenisUserName0 = "user0"; User user0 = when(mock(User.class).getId()).thenReturn(molgenisUserId0).getMock(); when(user0.getIdValue()).thenReturn(molgenisUserId0); when(user0.getUsername()).thenReturn(molgenisUserName0); String molgenisUserId1 = "id1"; String molgenisUserName1 = "user1"; User user1 = when(mock(User.class).getId()).thenReturn(molgenisUserId1).getMock(); when(user1.getIdValue()).thenReturn(molgenisUserId1); when(user1.getUsername()).thenReturn(molgenisUserName1); when(dataService.findOneById(UserMetaData.USER, molgenisUserId0, User.class)) .thenReturn(user0); when(dataService.findOneById(UserMetaData.USER, molgenisUserId1, User.class)) .thenReturn(user1); when(dataService.findAll(UserMetaData.USER, User.class)) .thenReturn(Stream.of(user0, user1)); GroupMember groupMember0 = mock(GroupMember.class); Group group0 = mock(Group.class); when(groupMember0.getGroup()).thenReturn(group0); GroupMember groupMember1 = mock(GroupMember.class); Group group1 = mock(Group.class); when(groupMember1.getGroup()).thenReturn(group1); when(dataService.findAll(GROUP_MEMBER, new QueryImpl<GroupMember>().eq(GroupMemberMetaData.USER, user0), GroupMember.class)).thenReturn(Stream.of(groupMember0)); when(dataService.findAll(GROUP_MEMBER, new QueryImpl<GroupMember>().eq(GroupMemberMetaData.USER, user1), GroupMember.class)).thenReturn(Stream.of(groupMember1)); this.setSecurityContextSuperUser(); assertEquals(userManagerService.getAllUsers(), Arrays.asList(new UserViewData(user0, singletonList(group0)), new UserViewData(user1, singletonList(group1)))); } @Test(expectedExceptions = AccessDeniedException.class) public void getAllMolgenisUsersNonSu() { this.setSecurityContextNonSuperUserWrite(); this.userManagerService.getAllUsers(); } @Test public void getAllMolgenisGroupsSu() { Group group0 = mock(Group.class); Group group1 = mock(Group.class); when(dataService.findAll(GroupMetaData.GROUP, Group.class)) .thenReturn(Stream.of(group0, group1)); this.setSecurityContextSuperUser(); assertEquals(userManagerService.getAllGroups(), Arrays.asList(group0, group1)); } @Test(expectedExceptions = AccessDeniedException.class) public void getAllMolgenisGroups_Non_SU() { this.setSecurityContextNonSuperUserWrite(); this.userManagerService.getAllGroups(); } @Test(expectedExceptions = AccessDeniedException.class) public void getGroupsWhereUserIsMemberNonUs() { this.setSecurityContextNonSuperUserWrite(); this.userManagerService.getGroupsWhereUserIsMember("1"); } @Test public void getGroupsWhereUserIsMemberSu() { this.setSecurityContextSuperUser(); User user1 = when(mock(User.class).getId()).thenReturn("1").getMock(); Group group20 = mock(Group.class); Group group21 = mock(Group.class); final GroupMember groupMemberOne = mock(GroupMember.class); groupMemberOne.setGroup(group20); groupMemberOne.setUser(user1); final GroupMember groupMemberTwo = mock(GroupMember.class); groupMemberTwo.setGroup(group21); groupMemberTwo.setUser(user1); when(dataService.findOneById(UserMetaData.USER, "1", User.class)).thenReturn(user1); when(dataService.findAll(GROUP_MEMBER, new QueryImpl<GroupMember>().eq(GroupMemberMetaData.USER, user1), GroupMember.class)).thenReturn(Stream.of(groupMemberOne, groupMemberTwo)); List<Group> groups = this.userManagerService.getGroupsWhereUserIsMember("1"); assertEquals(groups.size(), 2); } @Test(expectedExceptions = AccessDeniedException.class) public void getUsersMemberInGroupNonSu() { this.setSecurityContextNonSuperUserWrite(); this.userManagerService.getUsersMemberInGroup("22"); } @Test public void getUsersMemberInGroup() { reset(dataService); this.setSecurityContextSuperUser(); User user1 = mock(User.class); when(user1.getId()).thenReturn("1"); when(user1.getUsername()).thenReturn("Jonathan"); Group group22 = when(mock(Group.class).getId()).thenReturn("22").getMock(); GroupMember groupMember = mock(GroupMember.class); when(groupMember.getUser()).thenReturn(user1); when(groupMember.getGroup()).thenReturn(group22); when(dataService.findOneById(UserMetaData.USER, "1", User.class)).thenReturn(user1); when(dataService.findOneById(GroupMetaData.GROUP, "22", Group.class)) .thenReturn(group22); when(dataService.findAll(GROUP_MEMBER, new QueryImpl<GroupMember>().eq(GroupMemberMetaData.USER, user1), GroupMember.class)).thenAnswer(new Answer<Stream<GroupMember>>() { @Override public Stream<GroupMember> answer(InvocationOnMock invocation) throws Throwable { return Stream.of(groupMember); } }); when(dataService.findAll(GROUP_MEMBER, new QueryImpl<GroupMember>().eq(GroupMemberMetaData.GROUP, group22), GroupMember.class)).thenAnswer(new Answer<Stream<GroupMember>>() { @Override public Stream<GroupMember> answer(InvocationOnMock invocation) throws Throwable { return Stream.of(groupMember); } }); List<UserViewData> users = this.userManagerService.getUsersMemberInGroup("22"); assertEquals(users.size(), 1); } @Test(expectedExceptions = AccessDeniedException.class) public void getGroupsWhereUserIsNotMemberNonUs() { this.setSecurityContextNonSuperUserWrite(); this.userManagerService.getGroupsWhereUserIsNotMember("1"); } @Test public void getGroupsWhereUserIsNotMemberSu() { setSecurityContextSuperUser(); User user1 = when(mock(User.class).getId()).thenReturn("1").getMock(); Group group22 = when(mock(Group.class).getId()).thenReturn("22").getMock(); Group group33 = when(mock(Group.class).getId()).thenReturn("33").getMock(); Group group44 = when(mock(Group.class).getId()).thenReturn("44").getMock(); GroupMember groupMember = mock(GroupMember.class); when(groupMember.getUser()).thenReturn(user1); when(groupMember.getGroup()).thenReturn(group22); when(dataService.findOneById(UserMetaData.USER, "1", User.class)).thenReturn(user1); when(dataService.findAll(GroupMemberMetaData.USER, new QueryImpl<GroupMember>().eq(GroupMemberMetaData.USER, user1), GroupMember.class)).thenReturn(Stream.of(groupMember)); when(dataService.findAll(GroupMetaData.GROUP, Group.class)) .thenReturn(Stream.of(group22, group33, group44)); groupMember = mock(GroupMember.class); when(groupMember.getGroup()).thenReturn(group22); List groupMemberships = new ArrayList<GroupMember>(); groupMemberships.add(groupMember); when(dataService.findAll(GROUP_MEMBER, new QueryImpl<GroupMember>().eq(GroupMemberMetaData.USER, user1), GroupMember.class)).thenReturn(groupMemberships.stream()); List<Group> groups = this.userManagerService.getGroupsWhereUserIsNotMember("1"); assertEquals(groups.size(), 2); } @Test public void addUserToGroupSu() throws NumberFormatException { when(groupMemberFactory.create()).thenReturn(mock(GroupMember.class)); setSecurityContextSuperUser(); this.userManagerService.addUserToGroup("22", "1"); } @Test(expectedExceptions = AccessDeniedException.class) public void addUserToGroupNonSu() throws NumberFormatException { setSecurityContextNonSuperUserWrite(); this.userManagerService.addUserToGroup("22", "1"); } @Test public void removeUserFromGroupSu() throws NumberFormatException { setSecurityContextSuperUser(); User user1 = when(mock(User.class).getId()).thenReturn("1").getMock(); Group group22 = when(mock(Group.class).getId()).thenReturn("22").getMock(); GroupMember groupMember = mock(GroupMember.class); when(groupMember.getUser()).thenReturn(user1); when(groupMember.getGroup()).thenReturn(group22); when(dataService.findOneById(UserMetaData.USER, "1", User.class)).thenReturn(user1); when(dataService.findOneById(GroupMetaData.GROUP, "22", Group.class)) .thenReturn(group22); Query<GroupMember> q = new QueryImpl<GroupMember>() .eq(GroupMemberMetaData.USER, user1).and() .eq(GroupMemberMetaData.GROUP, group22); when(dataService.findAll(GROUP_MEMBER, q, GroupMember.class)) .thenReturn(Stream.of(groupMember)); this.userManagerService.removeUserFromGroup("22", "1"); } @Test(expectedExceptions = AccessDeniedException.class) public void removeUserFromGroupNonSu() throws NumberFormatException { setSecurityContextNonSuperUserWrite(); this.userManagerService.removeUserFromGroup("22", "1"); } private void setSecurityContextSuperUser() { Collection<? extends GrantedAuthority> authorities = Arrays.<SimpleGrantedAuthority>asList( new SimpleGrantedAuthority(SecurityUtils.AUTHORITY_SU)); SecurityContextHolder.getContext() .setAuthentication(new UsernamePasswordAuthenticationToken(null, null, authorities)); } private void setSecurityContextNonSuperUserWrite() { Collection<? extends GrantedAuthority> authorities = Arrays.<SimpleGrantedAuthority>asList( new SimpleGrantedAuthority(SecurityUtils.AUTHORITY_PLUGIN_READ_PREFIX + "USERMANAGER"), new SimpleGrantedAuthority(SecurityUtils.AUTHORITY_PLUGIN_WRITE_PREFIX + "USERMANAGER")); SecurityContextHolder.getContext() .setAuthentication(new UsernamePasswordAuthenticationToken(null, null, authorities)); } }