diff --git a/index.adoc b/index.adoc index 4ba8583..6deb57e 100644 --- a/index.adoc +++ b/index.adoc @@ -2537,6 +2537,297 @@ ifdef::internal-generation[] endif::internal-generation[] +[.Users] +=== Users + + +[.getUser] +==== getUser + +`GET /users` + +Get a user + +===== Description + + + + +// markup not found, no include::{specDir}users/GET/spec.adoc[opts=optional] + + + +===== Parameters + + + + + +====== Query Parameters + +[cols="2,3,1,1,1"] +|=== +|Name| Description| Required| Default| Pattern + +| username +| +| X +| null +| + +|=== + + +===== Return Type + +<> + + +===== Content Type + +* application/json + +===== Responses + +.http response codes +[cols="2,3,1"] +|=== +| Code | Message | Datatype + + +| 200 +| Returns the requested user details +| <> + + +| 0 +| Returns a list of error messages. +| <> + +|=== + +===== Samples + + +// markup not found, no include::{snippetDir}users/GET/http-request.adoc[opts=optional] + + +// markup not found, no include::{snippetDir}users/GET/http-response.adoc[opts=optional] + + + +// file not found, no * wiremock data link :users/GET/GET.json[] + + +ifdef::internal-generation[] +===== Implementation + +// markup not found, no include::{specDir}users/GET/implementation.adoc[opts=optional] + + +endif::internal-generation[] + + +[.setUser] +==== setUser + +`PUT /users` + +Update an user + +===== Description + + + + +// markup not found, no include::{specDir}users/PUT/spec.adoc[opts=optional] + + + +===== Parameters + + +===== Body Parameter + +[cols="2,3,1,1,1"] +|=== +|Name| Description| Required| Default| Pattern + +| UserBean +| <> +| X +| +| + +|=== + + + +====== Query Parameters + +[cols="2,3,1,1,1"] +|=== +|Name| Description| Required| Default| Pattern + +| username +| +| X +| null +| + +|=== + + +===== Return Type + +<> + + +===== Content Type + +* application/json + +===== Responses + +.http response codes +[cols="2,3,1"] +|=== +| Code | Message | Datatype + + +| 200 +| Returns the updated user details +| <> + + +| 0 +| Returns a list of error messages. +| <> + +|=== + +===== Samples + + +// markup not found, no include::{snippetDir}users/PUT/http-request.adoc[opts=optional] + + +// markup not found, no include::{snippetDir}users/PUT/http-response.adoc[opts=optional] + + + +// file not found, no * wiremock data link :users/PUT/PUT.json[] + + +ifdef::internal-generation[] +===== Implementation + +// markup not found, no include::{specDir}users/PUT/implementation.adoc[opts=optional] + + +endif::internal-generation[] + + +[.setUserPassword] +==== setUserPassword + +`PUT /users/password` + +Update a user password + +===== Description + + + + +// markup not found, no include::{specDir}users/password/PUT/spec.adoc[opts=optional] + + + +===== Parameters + + +===== Body Parameter + +[cols="2,3,1,1,1"] +|=== +|Name| Description| Required| Default| Pattern + +| body +| <> +| X +| +| + +|=== + + + +====== Query Parameters + +[cols="2,3,1,1,1"] +|=== +|Name| Description| Required| Default| Pattern + +| username +| +| X +| null +| + +|=== + + +===== Return Type + +<> + + +===== Content Type + +* application/json + +===== Responses + +.http response codes +[cols="2,3,1"] +|=== +| Code | Message | Datatype + + +| 200 +| Returns the user details +| <> + + +| 0 +| Returns a list of error messages. +| <> + +|=== + +===== Samples + + +// markup not found, no include::{snippetDir}users/password/PUT/http-request.adoc[opts=optional] + + +// markup not found, no include::{snippetDir}users/password/PUT/http-response.adoc[opts=optional] + + + +// file not found, no * wiremock data link :users/password/PUT/PUT.json[] + + +ifdef::internal-generation[] +===== Implementation + +// markup not found, no include::{specDir}users/password/PUT/implementation.adoc[opts=optional] + + +endif::internal-generation[] + + [#models] == Models @@ -3757,3 +4048,40 @@ endif::internal-generation[] |=== +[#UserBean] +=== _UserBean_ + + + +[.fields-UserBean] +[cols="2,1,2,4,1"] +|=== +| Field Name| Required| Type| Description| Format + +| username +| +| String +| +| + +| fullName +| +| String +| +| + +| email +| +| String +| +| + +| password +| +| String +| +| + +|=== + + diff --git a/src/main/java/de/aservo/confapi/crowd/model/UsersBean.java b/src/main/java/de/aservo/confapi/crowd/model/UsersBean.java new file mode 100644 index 0000000..249b3b0 --- /dev/null +++ b/src/main/java/de/aservo/confapi/crowd/model/UsersBean.java @@ -0,0 +1,22 @@ +package de.aservo.confapi.crowd.model; + +import de.aservo.confapi.commons.constants.ConfAPI; +import de.aservo.confapi.commons.model.UserBean; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.Collection; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@XmlRootElement(name = ConfAPI.USERS) +public class UsersBean { + + @XmlElement + private Collection users; + +} diff --git a/src/main/java/de/aservo/confapi/crowd/model/util/UserBeanUtil.java b/src/main/java/de/aservo/confapi/crowd/model/util/UserBeanUtil.java new file mode 100644 index 0000000..65cb82b --- /dev/null +++ b/src/main/java/de/aservo/confapi/crowd/model/util/UserBeanUtil.java @@ -0,0 +1,35 @@ +package de.aservo.confapi.crowd.model.util; + +import com.atlassian.crowd.model.user.User; +import de.aservo.confapi.commons.model.UserBean; + +import javax.annotation.Nullable; + +public class UserBeanUtil { + + /** + * Build user bean. + * + * @param user the user + * @return the user bean + */ + @Nullable + public static UserBean toUserBean( + @Nullable final User user) { + + if (user == null) { + return null; + } + + final UserBean userBean = new UserBean(); + userBean.setUsername(user.getName()); + userBean.setFullName(user.getDisplayName()); + userBean.setEmail(user.getEmailAddress()); + + return userBean; + } + + private UserBeanUtil() { + } + +} diff --git a/src/main/java/de/aservo/confapi/crowd/rest/UsersResourceImpl.java b/src/main/java/de/aservo/confapi/crowd/rest/UsersResourceImpl.java new file mode 100644 index 0000000..0a2a5eb --- /dev/null +++ b/src/main/java/de/aservo/confapi/crowd/rest/UsersResourceImpl.java @@ -0,0 +1,25 @@ +package de.aservo.confapi.crowd.rest; + +import com.sun.jersey.spi.container.ResourceFilters; +import de.aservo.confapi.commons.constants.ConfAPI; +import de.aservo.confapi.commons.rest.AbstractUsersResourceImpl; +import de.aservo.confapi.commons.service.api.UsersService; +import de.aservo.confapi.crowd.filter.SysadminOnlyResourceFilter; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import javax.ws.rs.Path; + +@Path(ConfAPI.USERS) +@ResourceFilters(SysadminOnlyResourceFilter.class) +@Component +public class UsersResourceImpl extends AbstractUsersResourceImpl { + + @Inject + public UsersResourceImpl( + final UsersService usersService) { + + super(usersService); + } + +} diff --git a/src/main/java/de/aservo/confapi/crowd/service/UsersServiceImpl.java b/src/main/java/de/aservo/confapi/crowd/service/UsersServiceImpl.java new file mode 100644 index 0000000..77b7641 --- /dev/null +++ b/src/main/java/de/aservo/confapi/crowd/service/UsersServiceImpl.java @@ -0,0 +1,171 @@ +package de.aservo.confapi.crowd.service; + +import com.atlassian.crowd.embedded.api.Directory; +import com.atlassian.crowd.embedded.api.PasswordCredential; +import com.atlassian.crowd.exception.DirectoryNotFoundException; +import com.atlassian.crowd.exception.InvalidCredentialException; +import com.atlassian.crowd.exception.InvalidUserException; +import com.atlassian.crowd.exception.OperationFailedException; +import com.atlassian.crowd.exception.UserAlreadyExistsException; +import com.atlassian.crowd.exception.UserNotFoundException; +import com.atlassian.crowd.manager.directory.DirectoryManager; +import com.atlassian.crowd.manager.directory.DirectoryPermissionException; +import com.atlassian.crowd.model.user.User; +import com.atlassian.crowd.model.user.UserTemplate; +import com.atlassian.crowd.search.EntityDescriptor; +import com.atlassian.crowd.search.builder.QueryBuilder; +import com.atlassian.crowd.search.query.entity.EntityQuery; +import com.atlassian.plugin.spring.scanner.annotation.export.ExportAsService; +import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; +import de.aservo.confapi.commons.exception.BadRequestException; +import de.aservo.confapi.commons.exception.InternalServerErrorException; +import de.aservo.confapi.commons.exception.NotFoundException; +import de.aservo.confapi.commons.model.UserBean; +import de.aservo.confapi.commons.service.api.UsersService; +import de.aservo.confapi.crowd.model.util.UserBeanUtil; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import javax.validation.constraints.NotNull; +import java.util.List; + +@Component +@ExportAsService(UsersService.class) +public class UsersServiceImpl implements UsersService { + + @ComponentImport + private final DirectoryManager directoryManager; + + @Inject + public UsersServiceImpl( + final DirectoryManager directoryManager) { + + this.directoryManager = directoryManager; + } + + @Override + public UserBean getUser( + final String name) { + + return UserBeanUtil.toUserBean(findUser(name)); + } + + @Override + public UserBean updateUser( + final String name, + final UserBean userBean) { + + User user = findUser(name); + + if (userBean.getUsername() != null && !name.equals(userBean.getUsername())) { + user = renameUser(user, userBean.getUsername()); + } + + if (userBean.getFullName() != null || userBean.getEmail() != null) { + final UserTemplate userTemplate = new UserTemplate(user); + + if (userBean.getFullName() != null) { + userTemplate.setDisplayName(userBean.getFullName()); + } + + if (userBean.getEmail() != null) { + userTemplate.setEmailAddress(userBean.getEmail()); + } + + user = updateUser(user.getDirectoryId(), userTemplate); + } + + if (userBean.getPassword() != null) { + updatePassword(user, userBean.getPassword()); + } + + return UserBeanUtil.toUserBean(user); + } + + @Override + public UserBean updatePassword( + final String name, + final String password) { + + final User user = findUser(name); + updatePassword(user, password); + return UserBeanUtil.toUserBean(user); + } + + @NotNull + private User findUser( + final String name) { + + for (Directory directory : findDirectories()) { + try { + return directoryManager.findUserByName(directory.getId(), name); + } catch (UserNotFoundException e) { + // Ignore, will be handled below + } catch (DirectoryNotFoundException | OperationFailedException e) { + throw new InternalServerErrorException(e); + } + } + + throw new NotFoundException(String.format("User with name '%s' could not be found", name)); + } + + @NotNull + private User renameUser( + final User user, + final String newName) { + + try { + return directoryManager.renameUser(user.getDirectoryId(), user.getName(), newName); + } catch (DirectoryPermissionException | UserAlreadyExistsException | InvalidUserException e) { + // A permission exception should only happen if we try change the name + // of a user in a read-only directory, so treat this as a bad request + throw new BadRequestException(e); + } catch (DirectoryNotFoundException | UserNotFoundException | OperationFailedException e) { + // At this point, we know the user exists, thus directory or user not found + // should never happen, so if it does, treat it as an internal server error + throw new InternalServerErrorException(e); + } + } + + @NotNull + private User updateUser( + final long directoryId, + final UserTemplate userTemplate) { + + try { + return directoryManager.updateUser(directoryId, userTemplate); + } catch (DirectoryPermissionException | InvalidUserException e) { + // A permission exception should only happen if we try change the name + // of a user in a read-only directory, so treat this as a bad request + throw new BadRequestException(e); + } catch (DirectoryNotFoundException | UserNotFoundException | OperationFailedException e) { + // At this point, we know the user exists, thus directory or user not found + // should never happen, so if it does, treat it as an internal server error + throw new InternalServerErrorException(e); + } + } + + private void updatePassword( + final User user, + final String password) { + + try { + directoryManager.updateUserCredential(user.getDirectoryId(), user.getName(), PasswordCredential.unencrypted(password)); + } catch (DirectoryPermissionException | InvalidCredentialException e) { + // A permission exception should only happen if we try to update the password + // of a user in a read-only directory, so treat this as a bad request + throw new BadRequestException(e); + } catch (DirectoryNotFoundException | UserNotFoundException | OperationFailedException e) { + // At this point, we know the user exists, thus directory or user not found + // should never happen, so if it does, treat it as an internal server error + throw new InternalServerErrorException(e); + } + } + + private List findDirectories() { + final EntityQuery directoryEntityQuery = QueryBuilder.queryFor(Directory.class, EntityDescriptor.directory()) + .returningAtMost(EntityQuery.ALL_RESULTS); + + return directoryManager.searchDirectories(directoryEntityQuery); + } +} diff --git a/src/test/java/de/aservo/confapi/crowd/model/util/UserBeanUtilTest.java b/src/test/java/de/aservo/confapi/crowd/model/util/UserBeanUtilTest.java new file mode 100644 index 0000000..8f3e0f6 --- /dev/null +++ b/src/test/java/de/aservo/confapi/crowd/model/util/UserBeanUtilTest.java @@ -0,0 +1,37 @@ +package de.aservo.confapi.crowd.model.util; + +import com.atlassian.crowd.model.user.ImmutableUser; +import com.atlassian.crowd.model.user.User; +import de.aservo.confapi.commons.model.UserBean; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.junit.Assert.*; + +@RunWith(MockitoJUnitRunner.class) +public class UserBeanUtilTest { + + @Test + public void testToUserBean() { + final User user = getTestUser(); + final UserBean userBean = UserBeanUtil.toUserBean(user); + + assertNotNull(userBean); + assertEquals(user.getName(), userBean.getUsername()); + assertEquals(user.getDisplayName(), userBean.getFullName()); + assertEquals(user.getEmailAddress(), userBean.getEmail()); + } + + @Test + public void testToUserBeanNull() { + assertNull(UserBeanUtil.toUserBean(null)); + } + + private User getTestUser() { + return ImmutableUser.builder(1, "test") + .displayName("Test User") + .emailAddress("test@example.com") + .build(); + } +} diff --git a/src/test/java/de/aservo/confapi/crowd/service/UsersServiceTest.java b/src/test/java/de/aservo/confapi/crowd/service/UsersServiceTest.java new file mode 100644 index 0000000..8ebfd84 --- /dev/null +++ b/src/test/java/de/aservo/confapi/crowd/service/UsersServiceTest.java @@ -0,0 +1,288 @@ +package de.aservo.confapi.crowd.service; + +import com.atlassian.crowd.embedded.api.Directory; +import com.atlassian.crowd.embedded.api.MockDirectoryInternal; +import com.atlassian.crowd.embedded.api.PasswordCredential; +import com.atlassian.crowd.exception.CrowdException; +import com.atlassian.crowd.exception.DirectoryNotFoundException; +import com.atlassian.crowd.exception.InvalidCredentialException; +import com.atlassian.crowd.exception.InvalidUserException; +import com.atlassian.crowd.exception.OperationFailedException; +import com.atlassian.crowd.exception.PermissionException; +import com.atlassian.crowd.exception.UserAlreadyExistsException; +import com.atlassian.crowd.exception.UserNotFoundException; +import com.atlassian.crowd.manager.directory.DirectoryManager; +import com.atlassian.crowd.manager.directory.DirectoryPermissionException; +import com.atlassian.crowd.model.user.ImmutableUser; +import com.atlassian.crowd.model.user.User; +import com.atlassian.crowd.model.user.UserTemplate; +import de.aservo.confapi.commons.exception.NotFoundException; +import de.aservo.confapi.commons.model.UserBean; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import javax.ws.rs.WebApplicationException; +import java.util.Collections; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class UsersServiceTest { + + @Mock + private DirectoryManager directoryManager; + + private UsersServiceImpl usersService; + + @Before + public void setup() { + usersService = new UsersServiceImpl(directoryManager); + + setupDirectoryManager(); + } + + private void setupDirectoryManager() { + doReturn(Collections.singletonList(getTestDirectory())).when(directoryManager).searchDirectories(any()); + } + + @Test + public void testGetUser() throws CrowdException { + final User user = getTestUser(); + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + + final UserBean userBean = usersService.getUser(user.getName()); + assertEquals(user.getName(), userBean.getUsername()); + } + + @Test(expected = NotFoundException.class) + public void testGetUserNotFound() throws Exception { + final Directory directory = getTestDirectory(); + final String userName = "not_found"; + doThrow(new UserNotFoundException(userName)).when(directoryManager).findUserByName(directory.getId(), userName); + + usersService.getUser(userName); + } + + @Test + public void testUpdateUser() throws CrowdException, PermissionException { + final User user = getTestUser(); + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + + final UserBean userBean = new UserBean(); + userBean.setFullName("Other Full Name"); + userBean.setEmail("other@example.com"); + + final ArgumentCaptor userTemplateArgumentCaptor = ArgumentCaptor.forClass(UserTemplate.class); + usersService.updateUser(user.getName(), userBean); + verify(directoryManager).updateUser(anyLong(), userTemplateArgumentCaptor.capture()); + assertEquals(userBean.getFullName(), userTemplateArgumentCaptor.getValue().getDisplayName()); + assertEquals(userBean.getEmail(), userTemplateArgumentCaptor.getValue().getEmailAddress()); + } + + @Test + public void testUpdateUserWithRename() throws CrowdException, PermissionException { + final User user = getTestUser(); + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + + final UserBean userBean = new UserBean(); + userBean.setUsername("new_username"); + + usersService.updateUser(user.getName(), userBean); + verify(directoryManager).renameUser(user.getDirectoryId(), user.getName(), userBean.getUsername()); + } + + @Test + public void testUpdateUserNoOp() throws CrowdException, PermissionException { + final User user = getTestUser(); + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + + // Just setting the same username and nothing else should not trigger any update + final UserBean userBean = new UserBean(); + userBean.setUsername(user.getName()); + + usersService.updateUser(user.getName(), userBean); + verify(directoryManager, never()).renameUser(anyLong(), anyString(), anyString()); + verify(directoryManager, never()).updateUser(anyLong(), any()); + verify(directoryManager, never()).updateUserCredential(anyLong(), anyString(), any()); + } + + @Test + public void testUpdateUserWithPassword() throws CrowdException, PermissionException { + final User user = getTestUser(); + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + + final UserBean userBean = new UserBean(); + userBean.setPassword("s3cr3t"); + + final ArgumentCaptor passwordCredentialArgumentCaptor = ArgumentCaptor.forClass(PasswordCredential.class); + usersService.updateUser(user.getName(), userBean); + verify(directoryManager).updateUserCredential(anyLong(), anyString(), passwordCredentialArgumentCaptor.capture()); + assertEquals(userBean.getPassword(), passwordCredentialArgumentCaptor.getValue().getCredential()); + } + + @Test + public void testChangePassword() throws CrowdException, PermissionException { + final User user = getTestUser(); + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + + final String password = "pa55w0rd"; + + final ArgumentCaptor passwordCredentialArgumentCaptor = ArgumentCaptor.forClass(PasswordCredential.class); + usersService.updatePassword(user.getName(), password); + verify(directoryManager).updateUserCredential(anyLong(), anyString(), passwordCredentialArgumentCaptor.capture()); + assertEquals(password, passwordCredentialArgumentCaptor.getValue().getCredential()); + } + + // We kind of need to test all the exceptions here, but it's also pointless to test + // all the exact mappings, because that's like repeating the implementation. + // For this reason, we are just using WebApplicationException as a catch-all for now. + // We are also not testing DirectoryNotFoundException and UserNotFoundException here, + // because these cases won't happen anymore after a user has been found by its name. + + @Test(expected = WebApplicationException.class) + public void testGetUserDirectoryNotFoundException() throws CrowdException { + final User user = getTestUser(); + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + doThrow(new DirectoryNotFoundException(user.getDirectoryId())).when(directoryManager).findUserByName(anyLong(), anyString()); + usersService.getUser(user.getName()); + } + + @Test(expected = WebApplicationException.class) + public void testGetUserOperationFailedException() throws CrowdException { + final User user = getTestUser(); + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + doThrow(new OperationFailedException()).when(directoryManager).findUserByName(anyLong(), anyString()); + usersService.getUser(user.getName()); + } + + @Test(expected = WebApplicationException.class) + public void testRenameUserDirectoryPermissionException() throws CrowdException, PermissionException { + final User user = getTestUser(); + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + + final UserBean userBean = new UserBean(); + userBean.setUsername("new_username"); + + doThrow(new DirectoryPermissionException()).when(directoryManager).renameUser(anyLong(), anyString(), anyString()); + usersService.updateUser(user.getName(), userBean); + } + + @Test(expected = WebApplicationException.class) + public void testRenameUserUserAlreadyExistsException() throws CrowdException, PermissionException { + final User user = getTestUser(); + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + + final UserBean userBean = new UserBean(); + userBean.setUsername("new_username"); + + doThrow(new UserAlreadyExistsException(user.getDirectoryId(), userBean.getUsername())).when(directoryManager).renameUser(anyLong(), anyString(), anyString()); + usersService.updateUser(user.getName(), userBean); + } + + @Test(expected = WebApplicationException.class) + public void testRenameUserInvalidUserException() throws CrowdException, PermissionException { + final User user = getTestUser(); + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + + final UserBean userBean = new UserBean(); + userBean.setUsername("new_username"); + + doThrow(new InvalidUserException(user, "message")).when(directoryManager).renameUser(anyLong(), anyString(), anyString()); + usersService.updateUser(user.getName(), userBean); + } + + @Test(expected = WebApplicationException.class) + public void testRenameUserOperationFailedException() throws CrowdException, PermissionException { + final User user = getTestUser(); + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + + final UserBean userBean = new UserBean(); + userBean.setUsername("new_username"); + + doThrow(new OperationFailedException()).when(directoryManager).renameUser(anyLong(), anyString(), anyString()); + usersService.updateUser(user.getName(), userBean); + } + + @Test(expected = WebApplicationException.class) + public void testUpdateUserDirectoryPermissionException() throws CrowdException, PermissionException { + final User user = getTestUser(); + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + + final UserBean userBean = new UserBean(); + userBean.setFullName("Other Full Name"); + + doThrow(new DirectoryPermissionException()).when(directoryManager).updateUser(anyLong(), any()); + usersService.updateUser(user.getName(), userBean); + } + + @Test(expected = WebApplicationException.class) + public void testUpdateUserInvalidUserException() throws CrowdException, PermissionException { + final User user = getTestUser(); + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + + final UserBean userBean = new UserBean(); + userBean.setFullName("Other Full Name"); + + doThrow(new InvalidUserException(user, "message")).when(directoryManager).updateUser(anyLong(), any()); + usersService.updateUser(user.getName(), userBean); + } + + @Test(expected = WebApplicationException.class) + public void testUpdateUserOperationFailedException() throws CrowdException, PermissionException { + final User user = getTestUser(); + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + + final UserBean userBean = new UserBean(); + userBean.setEmail("other@example.com"); + + doThrow(new OperationFailedException()).when(directoryManager).updateUser(anyLong(), any()); + usersService.updateUser(user.getName(), userBean); + } + + @Test(expected = WebApplicationException.class) + public void testUpdatePasswordDirectoryPermissionException() throws CrowdException, PermissionException { + final User user = getTestUser(); + final String password = "pa55w0rd"; + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + + doThrow(new DirectoryPermissionException()).when(directoryManager).updateUserCredential(anyLong(), anyString(), any()); + usersService.updatePassword(user.getName(), password); + } + + @Test(expected = WebApplicationException.class) + public void testUpdatePasswordInvalidCredentialException() throws CrowdException, PermissionException { + final User user = getTestUser(); + final String password = "pa55w0rd"; + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + + doThrow(new InvalidCredentialException()).when(directoryManager).updateUserCredential(anyLong(), anyString(), any()); + usersService.updatePassword(user.getName(), password); + } + + @Test(expected = WebApplicationException.class) + public void testUpdatePasswordOperationFailedException() throws CrowdException, PermissionException { + final User user = getTestUser(); + final String password = "pa55w0rd"; + doReturn(user).when(directoryManager).findUserByName(user.getDirectoryId(), user.getName()); + + doThrow(new OperationFailedException()).when(directoryManager).updateUserCredential(anyLong(), anyString(), any()); + usersService.updatePassword(user.getName(), password); + } + + private Directory getTestDirectory() { + return new MockDirectoryInternal(); + } + + private User getTestUser() { + return ImmutableUser.builder(getTestDirectory().getId(), "test") + .displayName("Test User") + .emailAddress("test@example.com") + .build(); + } +}