diff --git a/index.adoc b/index.adoc index d158663..95f7880 100644 --- a/index.adoc +++ b/index.adoc @@ -3402,6 +3402,18 @@ endif::internal-generation[] | | +| groups +| +| List of <> +| +| + +| users +| +| List of <> +| +| + | schema | | DirectoryLdapSchema @@ -4205,6 +4217,18 @@ endif::internal-generation[] | | +| groups +| +| List of <> +| +| + +| users +| +| List of <> +| +| + |=== @@ -4515,6 +4539,37 @@ endif::internal-generation[] |=== +[#GroupBean] +=== _GroupBean_ + + + +[.fields-GroupBean] +[cols="2,1,2,4,1"] +|=== +| Field Name| Required| Type| Description| Format + +| name +| +| String +| +| + +| description +| +| String +| +| + +| active +| +| Boolean +| +| + +|=== + + [#LicenseBean] === _LicenseBean_ @@ -4813,6 +4868,18 @@ endif::internal-generation[] | | +| firstName +| +| String +| +| + +| lastName +| +| String +| +| + | fullName | | String @@ -4831,6 +4898,12 @@ endif::internal-generation[] | | +| groups +| +| List of <> +| +| + |=== diff --git a/pom.xml b/pom.xml index b11927a..311f1d4 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ confapi-crowd-plugin - 0.2.2-SNAPSHOT + 0.3.0-SNAPSHOT atlassian-plugin ConfAPI for Crowd @@ -66,7 +66,7 @@ 8.0.2 ${project.groupId}.${project.artifactId} 2.1.5 - 0.1.1 + 0.2.0-SNAPSHOT 2.0.1 8 diff --git a/src/main/java/de/aservo/confapi/crowd/exception/NotFoundExceptionForDirectory.java b/src/main/java/de/aservo/confapi/crowd/exception/NotFoundExceptionForDirectory.java new file mode 100644 index 0000000..22c06a8 --- /dev/null +++ b/src/main/java/de/aservo/confapi/crowd/exception/NotFoundExceptionForDirectory.java @@ -0,0 +1,26 @@ +package de.aservo.confapi.crowd.exception; + +import de.aservo.confapi.commons.exception.NotFoundException; +import de.aservo.confapi.commons.model.AbstractDirectoryBean; + +public class NotFoundExceptionForDirectory extends NotFoundException { + + public NotFoundExceptionForDirectory( + final AbstractDirectoryBean directoryBean) { + + this(directoryBean.getName()); + } + + public NotFoundExceptionForDirectory( + final String name) { + + super(String.format("Directory with name '%s' could not be found", name)); + } + + public NotFoundExceptionForDirectory( + final long id) { + + super(String.format("Directory with id '%s' could not be found", id)); + } + +} diff --git a/src/main/java/de/aservo/confapi/crowd/exception/NotFoundExceptionForUser.java b/src/main/java/de/aservo/confapi/crowd/exception/NotFoundExceptionForUser.java new file mode 100644 index 0000000..6eb9396 --- /dev/null +++ b/src/main/java/de/aservo/confapi/crowd/exception/NotFoundExceptionForUser.java @@ -0,0 +1,20 @@ +package de.aservo.confapi.crowd.exception; + +import de.aservo.confapi.commons.exception.NotFoundException; +import de.aservo.confapi.commons.model.UserBean; + +public class NotFoundExceptionForUser extends NotFoundException { + + public NotFoundExceptionForUser( + final UserBean userBean) { + + this(userBean.getUsername()); + } + + public NotFoundExceptionForUser( + final String name) { + + super(String.format("User with name '%s' could not be found", name)); + } + +} diff --git a/src/main/java/de/aservo/confapi/crowd/service/DirectoriesServiceImpl.java b/src/main/java/de/aservo/confapi/crowd/service/DirectoriesServiceImpl.java index 36d46be..0b0d45a 100644 --- a/src/main/java/de/aservo/confapi/crowd/service/DirectoriesServiceImpl.java +++ b/src/main/java/de/aservo/confapi/crowd/service/DirectoriesServiceImpl.java @@ -16,7 +16,9 @@ import de.aservo.confapi.commons.exception.ServiceUnavailableException; import de.aservo.confapi.commons.model.AbstractDirectoryBean; import de.aservo.confapi.commons.model.DirectoriesBean; +import de.aservo.confapi.commons.model.DirectoryInternalBean; import de.aservo.confapi.commons.service.api.DirectoriesService; +import de.aservo.confapi.commons.service.api.UsersService; import de.aservo.confapi.crowd.model.util.DirectoryBeanUtil; import org.springframework.stereotype.Component; @@ -40,11 +42,16 @@ public class DirectoriesServiceImpl implements DirectoriesService { @ComponentImport private final DirectoryManager directoryManager; + @ComponentImport + private final UsersService usersService; + @Inject public DirectoriesServiceImpl( - final DirectoryManager directoryManager) { + final DirectoryManager directoryManager, + final UsersService usersService) { this.directoryManager = directoryManager; + this.usersService = usersService; } @Override @@ -89,11 +96,12 @@ public AbstractDirectoryBean setDirectory( final boolean testConnection) { final Directory existingDirectory = findDirectory(id); + final AbstractDirectoryBean resultDirectoryBean; try { final Directory mergedDirectory = DirectoryBeanUtil.toDirectory(directoryBean, existingDirectory); final Directory updatedDirectory = directoryManager.updateDirectory(mergedDirectory); - return DirectoryBeanUtil.toDirectoryInternalBean(updatedDirectory); + resultDirectoryBean = DirectoryBeanUtil.toDirectoryBean(updatedDirectory); } catch (DirectoryBeanUtil.UnsupportedDirectoryBeanException e) { throw new BadRequestException(String.format( "Setting directory type '%s' is not supported (yet)", e.getMessage())); @@ -102,6 +110,15 @@ public AbstractDirectoryBean setDirectory( throw new InternalServerErrorException(String.format( "When trying to update directory '%s', it could not be found anymore", existingDirectory.getName())); } + + if (DirectoryInternalBean.class.equals(directoryBean.getClass()) && directoryBean.getClass().equals(resultDirectoryBean.getClass())) { + final DirectoryInternalBean directoryInternalBean = (DirectoryInternalBean) directoryBean; + final DirectoryInternalBean resultDirectoryInternalBean = (DirectoryInternalBean) resultDirectoryBean; + + resultDirectoryInternalBean.setUsers(usersService.setUsers(directoryInternalBean.getId(), directoryInternalBean.getUsers())); + } + + return resultDirectoryBean; } @Override @@ -109,16 +126,27 @@ public AbstractDirectoryBean addDirectory( final @NotNull AbstractDirectoryBean directoryBean, final boolean testConnection) { + final AbstractDirectoryBean resultDirectoryBean; + try { final Directory directory = DirectoryBeanUtil.toDirectory(directoryBean); final Directory addedDirectory = directoryManager.addDirectory(directory); - return DirectoryBeanUtil.toDirectoryBean(addedDirectory); + resultDirectoryBean = DirectoryBeanUtil.toDirectoryBean(addedDirectory); } catch (DirectoryBeanUtil.UnsupportedDirectoryBeanException e) { throw new BadRequestException(String.format( "Adding directory type '%s' is not supported (yet)", e.getMessage())); } catch (DirectoryInstantiationException e) { throw new InternalServerErrorException(String.format("Could not create directory '%s'", directoryBean.getName())); } + + if (DirectoryInternalBean.class.equals(directoryBean.getClass()) && directoryBean.getClass().equals(resultDirectoryBean.getClass())) { + final DirectoryInternalBean directoryInternalBean = (DirectoryInternalBean) directoryBean; + final DirectoryInternalBean resultDirectoryInternalBean = (DirectoryInternalBean) resultDirectoryBean; + + resultDirectoryInternalBean.setUsers(usersService.setUsers(directoryInternalBean.getId(), directoryInternalBean.getUsers())); + } + + return resultDirectoryBean; } @Override diff --git a/src/main/java/de/aservo/confapi/crowd/service/UsersServiceImpl.java b/src/main/java/de/aservo/confapi/crowd/service/UsersServiceImpl.java index 77b7641..48b533a 100644 --- a/src/main/java/de/aservo/confapi/crowd/service/UsersServiceImpl.java +++ b/src/main/java/de/aservo/confapi/crowd/service/UsersServiceImpl.java @@ -12,6 +12,7 @@ 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.model.user.UserTemplateWithAttributes; import com.atlassian.crowd.search.EntityDescriptor; import com.atlassian.crowd.search.builder.QueryBuilder; import com.atlassian.crowd.search.query.entity.EntityQuery; @@ -19,15 +20,19 @@ 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.exception.NotFoundExceptionForDirectory; +import de.aservo.confapi.crowd.exception.NotFoundExceptionForUser; import de.aservo.confapi.crowd.model.util.UserBeanUtil; import org.springframework.stereotype.Component; +import javax.annotation.Nullable; import javax.inject.Inject; import javax.validation.constraints.NotNull; +import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; @Component @ExportAsService(UsersService.class) @@ -44,35 +49,134 @@ public UsersServiceImpl( } @Override + @Deprecated public UserBean getUser( - final String name) { + final String username) { + + final User user = findUserAllDirectories(username); + + if (user == null) { + throw new NotFoundExceptionForUser(username); + } + + return UserBeanUtil.toUserBean(user); + } + + @Override + public UserBean getUser( + final long directoryId, + final String username) { + + final User user = findUser(directoryId, username); + + if (user == null) { + throw new NotFoundExceptionForUser(username); + } + + return UserBeanUtil.toUserBean(user); + } + + public UserBean setUser( + final long directoryId, + final UserBean userBean) { + + final User user = findUser(directoryId, userBean.getUsername()); + + if (user == null) { + return addUser(directoryId, userBean.getUsername(), userBean); + } - return UserBeanUtil.toUserBean(findUser(name)); + return updateUser(directoryId, user.getName(), userBean); } @Override + public List setUsers( + final long directoryId, + final Collection userBeans) { + + return userBeans.stream() + .map(userBean -> setUser(directoryId, userBean)) + .collect(Collectors.toList()); + } + + @Override + @Deprecated public UserBean updateUser( final String name, final UserBean userBean) { - User user = findUser(name); + final User user = findUserAllDirectories(name); - if (userBean.getUsername() != null && !name.equals(userBean.getUsername())) { - user = renameUser(user, userBean.getUsername()); + if (user == null) { + throw new NotFoundExceptionForUser(name); } - if (userBean.getFullName() != null || userBean.getEmail() != null) { - final UserTemplate userTemplate = new UserTemplate(user); + return updateUser(user.getDirectoryId(), name, userBean); + } - if (userBean.getFullName() != null) { - userTemplate.setDisplayName(userBean.getFullName()); - } + private UserBean addUser( + final long directoryId, + final String username, + final UserBean userBean) { - if (userBean.getEmail() != null) { - userTemplate.setEmailAddress(userBean.getEmail()); - } + User user = findUser(directoryId, userBean.getUsername()); - user = updateUser(user.getDirectoryId(), userTemplate); + if (user != null) { + throw new BadRequestException(String.format("User '%s' already exists", userBean.getUsername())); + } + + if (userBean.getUsername() != null && !username.equals(userBean.getUsername())) { + throw new BadRequestException("Cannot create user, two different usernames provided"); + } + + if (userBean.getUsername() == null) { + throw new BadRequestException("Cannot create user, username is required"); + } + + if (userBean.getFirstName() == null || userBean.getLastName() == null || userBean.getFullName() == null || userBean.getEmail() == null) { + throw new BadRequestException("Cannot create user, first name, last name, display (full) name and email are required"); + } + + if (userBean.getPassword() == null) { + throw new BadRequestException("Cannot create user, password is required"); + } + + final UserTemplateWithAttributes userTemplate = new UserTemplateWithAttributes(userBean.getUsername(), directoryId); + userTemplate.setDisplayName(userBean.getFullName()); + userTemplate.setEmailAddress(userBean.getEmail()); + + final PasswordCredential passwordCredential = PasswordCredential.unencrypted(userBean.getPassword()); + + try { + user = directoryManager.addUser(directoryId, userTemplate, passwordCredential); + } catch (InvalidCredentialException | InvalidUserException | DirectoryPermissionException | UserAlreadyExistsException e) { + throw new BadRequestException(e); + } catch (DirectoryNotFoundException e) { + throw new NotFoundExceptionForDirectory(directoryId); + } catch (OperationFailedException e) { + throw new InternalServerErrorException(e); + } + + return UserBeanUtil.toUserBean(user); + } + + private UserBean updateUser( + final long directoryId, + final String username, + final UserBean userBean) { + + User user = findUser(directoryId, username); + + if (user == null) { + throw new NotFoundExceptionForUser(username); + } + + if (userBean.getUsername() != null && !username.equals(userBean.getUsername())) { + user = renameUser(user, userBean.getUsername()); + } + + if (userBean.getFirstName() != null || userBean.getLastName() != null || userBean.getFullName() != null || userBean.getEmail() != null) { + user = updateUser(user.getDirectoryId(), getUserTemplate(user, userBean)); } if (userBean.getPassword() != null) { @@ -83,17 +187,42 @@ public UserBean updateUser( } @Override + @Deprecated public UserBean updatePassword( final String name, final String password) { - final User user = findUser(name); + final User user = findUserAllDirectories(name); + + if (user == null) { + throw new NotFoundExceptionForUser(name); + } + updatePassword(user, password); return UserBeanUtil.toUserBean(user); } - @NotNull + @Nullable private User findUser( + final long directoryId, + final String name) { + + try { + return directoryManager.findUserByName(directoryId, name); + } catch (DirectoryNotFoundException e) { + throw new NotFoundExceptionForDirectory(directoryId); + } catch (UserNotFoundException e) { + // Ignore, will be handled differently + } catch (OperationFailedException e) { + throw new InternalServerErrorException(e); + } + + return null; + } + + @Nullable + @Deprecated + private User findUserAllDirectories( final String name) { for (Directory directory : findDirectories()) { @@ -106,7 +235,7 @@ private User findUser( } } - throw new NotFoundException(String.format("User with name '%s' could not be found", name)); + return null; } @NotNull @@ -162,10 +291,37 @@ private void updatePassword( } } + private static UserTemplate getUserTemplate( + final User user, + final UserBean userBean) { + + final UserTemplate userTemplate = new UserTemplate(user); + + if (userBean.getFirstName() != null) { + userTemplate.setFirstName(userBean.getFirstName()); + } + + if (userBean.getLastName() != null) { + userTemplate.setLastName(userBean.getLastName()); + } + + if (userBean.getFullName() != null) { + userTemplate.setDisplayName(userBean.getFullName()); + } + + if (userBean.getEmail() != null) { + userTemplate.setEmailAddress(userBean.getEmail()); + } + + return userTemplate; + } + + @Deprecated 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/service/DirectoriesServiceTest.java b/src/test/java/de/aservo/confapi/crowd/service/DirectoriesServiceTest.java index 05151b4..f51189e 100644 --- a/src/test/java/de/aservo/confapi/crowd/service/DirectoriesServiceTest.java +++ b/src/test/java/de/aservo/confapi/crowd/service/DirectoriesServiceTest.java @@ -15,6 +15,7 @@ import de.aservo.confapi.commons.exception.ServiceUnavailableException; import de.aservo.confapi.commons.model.AbstractDirectoryBean; import de.aservo.confapi.commons.model.DirectoriesBean; +import de.aservo.confapi.commons.service.api.UsersService; import de.aservo.confapi.crowd.model.util.DirectoryBeanUtil; import org.junit.Before; import org.junit.Test; @@ -41,11 +42,14 @@ public class DirectoriesServiceTest { @Mock private DirectoryManager directoryManager; + @Mock + private UsersService usersService; + private DirectoriesServiceImpl directoriesService; @Before public void setup() { - directoriesService = new DirectoriesServiceImpl(directoryManager); + directoriesService = new DirectoriesServiceImpl(directoryManager, usersService); } @Test diff --git a/src/test/java/de/aservo/confapi/crowd/service/UsersServiceTest.java b/src/test/java/de/aservo/confapi/crowd/service/UsersServiceTest.java index 8ebfd84..9384b66 100644 --- a/src/test/java/de/aservo/confapi/crowd/service/UsersServiceTest.java +++ b/src/test/java/de/aservo/confapi/crowd/service/UsersServiceTest.java @@ -16,8 +16,8 @@ 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 de.aservo.confapi.crowd.exception.NotFoundExceptionForUser; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -60,7 +60,7 @@ public void testGetUser() throws CrowdException { assertEquals(user.getName(), userBean.getUsername()); } - @Test(expected = NotFoundException.class) + @Test(expected = NotFoundExceptionForUser.class) public void testGetUserNotFound() throws Exception { final Directory directory = getTestDirectory(); final String userName = "not_found";