diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy index abd4ec060..03e041322 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy @@ -560,7 +560,6 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { public edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver updateMetadataResolverEnabledStatus(edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver updatedResolver) throws ForbiddenException, MetadataFileNotFoundException, InitializationException { if (!userService.currentUserCanEnable(updatedResolver)) { -// if (!userService.currentUserHasExpectedRole(["ROLE_ADMIN", "ROLE_ENABLE"])) { throw new ForbiddenException("You do not have the permissions necessary to change the enable status of this filter.") } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java index 33b763231..548c97356 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java @@ -1,9 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain.filters; -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.*; import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; import edu.internet2.tier.shibboleth.admin.ui.domain.ActivatableType; import edu.internet2.tier.shibboleth.admin.ui.domain.IActivatable; @@ -58,6 +55,7 @@ public abstract class MetadataFilter extends AbstractAuditable implements IConcr @Transient private transient Integer version; + @JsonIgnore public ActivatableType getActivatableType() { return FILTER; } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java index a777aa3ca..995fab868 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java @@ -104,7 +104,9 @@ public void entityAttributesFilterIntoTransientRepresentation() { .forEach(EntityAttributesFilter::intoTransientRepresentation); } - @Override public ActivatableType getActivatableType() { + @Override + @JsonIgnore + public ActivatableType getActivatableType() { return METADATA_RESOLVER; } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java index 4c1bdc4c2..4118b8881 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java @@ -50,18 +50,18 @@ public UserService(IGroupService groupService, OwnershipRepository ownershipRepo } public boolean currentUserCanEnable(IActivatable activatableObject) { + if (currentUserIsAdmin()) { return true; } switch (activatableObject.getActivatableType()) { case ENTITY_DESCRIPTOR: { - if (getCurrentUserAccess() == ADMIN) { return true; } - if (currentUserHasExpectedRole(Arrays.asList("ROLE_ENABLE" )) && getCurrentUserGroup().getOwnerId().equals(((EntityDescriptor) activatableObject).getIdOfOwner())) { - return true; - } + return currentUserHasExpectedRole(Arrays.asList("ROLE_ENABLE" )) && getCurrentUserGroup().getOwnerId().equals(((EntityDescriptor) activatableObject).getIdOfOwner()); } + // Currently filters and providers dont have ownership, so we just look for the right role case FILTER: case METADATA_RESOLVER: - return currentUserHasExpectedRole(Arrays.asList("ROLE_ADMIN", "ROLE_ENABLE" )); + return currentUserHasExpectedRole(Arrays.asList("ROLE_ENABLE" )); + default: + return false; } - return false; } /** @@ -137,6 +137,7 @@ public Set getUserRoles(String username) { return result; } + // @TODO - probably delegate this out to something plugable at some point public boolean isAuthorizedFor(Ownable ownableObject) { switch (getCurrentUserAccess()) { case ADMIN: // Pure admin is authorized to do anything diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java index 668f2636b..764d75f6b 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java @@ -363,7 +363,7 @@ public EntityDescriptorRepresentation update(EntityDescriptorRepresentation edRe if (existingEd == null) { throw new EntityNotFoundException(String.format("The entity descriptor with entity id [%s] was not found for update.", edRep.getId())); } - if (edRep.isServiceEnabled() && !userService.currentUserIsAdmin()) { + if (edRep.isServiceEnabled() && !userService.currentUserCanEnable(existingEd)) { throw new ForbiddenException("You do not have the permissions necessary to enable this service."); } if (!userService.isAuthorizedFor(existingEd)) { @@ -392,7 +392,6 @@ public EntityDescriptorRepresentation updateEntityDescriptorEnabledStatus(String throw new EntityNotFoundException("Entity with resourceid[" + resourceId + "] was not found for update"); } if (!userService.currentUserCanEnable(ed)) { -// if (!userService.currentUserHasExpectedRole(Arrays.asList(new String[] { "ROLE_ADMIN", "ROLE_ENABLE" }))) { throw new ForbiddenException("You do not have the permissions necessary to change the enable status of this entity descriptor."); } ed.setServiceEnabled(status); diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 83f2635e0..6f8b837e3 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -87,7 +87,7 @@ shibui.mail.text-email-template-path-prefix=/mail/text/ shibui.mail.html.email-template-path-prefix=/mail/html/ shibui.mail.system-email-address=doNotReply@shibui.org -shibui.roles=ROLE_ADMIN,ROLE_USER,ROLE_NONE +shibui.roles=ROLE_ADMIN,ROLE_ENABLE,ROLE_USER,ROLE_NONE #In order to enable authentication via configured pac4j library (with external SAMl Idp, for example) #This property must be set to true and pac4j properties configured. For sample pac4j properties, see application.yml @@ -97,4 +97,4 @@ shibui.roles=ROLE_ADMIN,ROLE_USER,ROLE_NONE #This property must be set to true in order to enable posting stats to beacon endpoint. Furthermore, appropriate #environment variables must be set for beacon publisher to be used (the ones that are set when running shib-ui in #docker container -shibui.beacon-enabled=true +shibui.beacon-enabled=true \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversControllerIntegrationTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversControllerIntegrationTests.groovy index b8487c9c8..05f6b62c8 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversControllerIntegrationTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversControllerIntegrationTests.groovy @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomPropertiesConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.StringTrimModule import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver @@ -21,6 +22,7 @@ import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.context.TestConfiguration import org.springframework.boot.test.web.client.TestRestTemplate import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Profile import org.springframework.http.HttpEntity import org.springframework.http.HttpHeaders import org.springframework.test.annotation.DirtiesContext @@ -63,6 +65,7 @@ class MetadataResolversControllerIntegrationTests extends Specification { mapper.enable(SerializationFeature.INDENT_OUTPUT) mapper.setSerializationInclusion(NON_NULL) mapper.registerModule(new JavaTimeModule()) + mapper.registerModule(new StringTrimModule()) metadataResolverRepository.deleteAll() } @@ -206,7 +209,7 @@ class MetadataResolversControllerIntegrationTests extends Specification { 'ResourceBacked' | _ 'Filesystem' | _ } - + @DirtiesContext def "SHIBUI-1992 - error creating FileBackedHTTPMetadata"() { def resolver = new FileBackedHttpMetadataResolver().with { @@ -360,10 +363,10 @@ class MetadataResolversControllerIntegrationTests extends Specification { } @TestConfiguration - static class Config { + static class LocalConfig { @Bean MetadataResolver metadataResolver() { new OpenSamlChainingMetadataResolver() } } -} +} \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/UserServiceTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/UserServiceTests.groovy index b6431f79f..80ce68ab9 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/UserServiceTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/UserServiceTests.groovy @@ -1,19 +1,28 @@ package edu.internet2.tier.shibboleth.admin.ui.security.service -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter - -import javax.persistence.EntityManager - +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer +import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomPropertiesConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.ShibUIConfiguration +import edu.internet2.tier.shibboleth.admin.ui.domain.ActivatableType +import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor +import edu.internet2.tier.shibboleth.admin.ui.domain.IActivatable +import edu.internet2.tier.shibboleth.admin.ui.security.model.* +import edu.internet2.tier.shibboleth.admin.ui.security.repository.GroupsRepository +import edu.internet2.tier.shibboleth.admin.ui.security.repository.OwnershipRepository +import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository +import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository +import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest -import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.context.TestConfiguration import org.springframework.context.annotation.Bean import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.Profile -import org.springframework.context.annotation.PropertySource import org.springframework.data.jpa.repository.config.EnableJpaRepositories import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder import org.springframework.security.test.context.support.WithMockUser @@ -22,34 +31,12 @@ import org.springframework.test.annotation.Rollback import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.ContextConfiguration import org.springframework.transaction.annotation.Transactional - -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.databind.SerializationFeature -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule -import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer - -import edu.internet2.tier.shibboleth.admin.ui.ShibbolethUiApplication -import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomPropertiesConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration -import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration -import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor -import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation -import edu.internet2.tier.shibboleth.admin.ui.security.model.Group -import edu.internet2.tier.shibboleth.admin.ui.security.model.OwnerType -import edu.internet2.tier.shibboleth.admin.ui.security.model.Ownership -import edu.internet2.tier.shibboleth.admin.ui.security.model.Role -import edu.internet2.tier.shibboleth.admin.ui.security.model.User -import edu.internet2.tier.shibboleth.admin.ui.security.model.listener.GroupUpdatedEntityListener -import edu.internet2.tier.shibboleth.admin.ui.security.model.listener.UserUpdatedEntityListener -import edu.internet2.tier.shibboleth.admin.ui.security.repository.GroupsRepository -import edu.internet2.tier.shibboleth.admin.ui.security.repository.OwnershipRepository -import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository -import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository -import edu.internet2.tier.shibboleth.admin.ui.security.service.IGroupService -import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService import spock.lang.Specification +import javax.persistence.EntityManager +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + @DataJpaTest @ContextConfiguration(classes=[CoreShibUiConfiguration, CustomPropertiesConfiguration, LocalConfig]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @@ -70,13 +57,18 @@ class UserServiceTests extends Specification { @Autowired RoleRepository roleRepository - + + @Autowired + ShibUIConfiguration shibUIConfiguration + @Autowired UserRepository userRepository @Autowired UserService userService - + + def users + @Transactional def setup() { userRepository.findAll().forEach { @@ -87,22 +79,217 @@ class UserServiceTests extends Specification { roleRepository.deleteAll() roleRepository.flush() groupService.clearAllForTesting() //leaves us just the admingroup - - def roles = [new Role().with { - name = 'ROLE_ADMIN' - it - }, new Role().with { - name = 'ROLE_USER' + + shibUIConfiguration.roles.each { it -> + def role = new Role(name: it) + roleRepository.saveAndFlush(role) + } + + if (userRepository.count() == 0) { + users = [new User().with { + username = 'admin' + password = '{noop}adminpass' + firstName = 'Joe' + lastName = 'Doe' + emailAddress = 'joe@institution.edu' + roles.add(roleRepository.findByName('ROLE_ADMIN').get()) + it + }, new User().with { + username = 'nonadmin' + password = '{noop}nonadminpass' + firstName = 'Peter' + lastName = 'Vandelay' + emailAddress = 'peter@institution.edu' + roles.add(roleRepository.findByName('ROLE_USER').get()) + it + }, new User().with { + username = 'robot' + password = '{noop}nonepass' + firstName = 'Bad' + lastName = 'robot' + emailAddress = 'badboy@institution.edu' + roles.add(roleRepository.findByName('ROLE_ENABLE').get()) + it + }, new User().with { + username = 'robot2' + password = '{noop}nonepass' + firstName = 'Bad2' + lastName = 'robot2' + emailAddress = 'badboy2@institution.edu' + roles.add(roleRepository.findByName('ROLE_ENABLE').get()) + it + }] + users.each { + it = userService.save(it) + } + } + } + + @WithMockUser(value = "admin", roles = ["ADMIN"]) + def "Validate isAuthorizedFor with admin user (always auth)"() { + when: "the object doesn't matter if user is admin" + def isAuth = userService.isAuthorizedFor(new Ownable(){ + String getObjectId() { return null } + OwnableType getOwnableType() { return null } + }); + + then: + isAuth + } + + @WithMockUser(value = "admin", roles = ["ADMIN"]) + def "Admin can activate"() { + when: "the object doesn't matter if user is admin" + def canEnable = userService.currentUserCanEnable(new IActivatable() { + ActivatableType getActivatableType() { return ActivatableType.ENTITY_DESCRIPTOR } + void setEnabled(Boolean enabled) { } + }); + + then: + canEnable + + when: "the object doesn't matter if user is admin" + canEnable = userService.currentUserCanEnable(new IActivatable() { + ActivatableType getActivatableType() { return ActivatableType.FILTER } + void setEnabled(Boolean enabled) { } + }); + + then: + canEnable + + when: "the object doesn't matter if user is admin" + canEnable = userService.currentUserCanEnable(new IActivatable() { + ActivatableType getActivatableType() { return ActivatableType.METADATA_RESOLVER } + void setEnabled(Boolean enabled) { } + }); + + then: + canEnable + } + + @WithMockUser(value = "nonadmin", roles = ["USER"]) + def "nonadmin cannot activate without enable role"() { + when: + def canEnable = userService.currentUserCanEnable(new IActivatable() { + ActivatableType getActivatableType() { return ActivatableType.ENTITY_DESCRIPTOR } + void setEnabled(Boolean enabled) { } + }); + + then: + !canEnable + + when: + canEnable = userService.currentUserCanEnable(new IActivatable() { + ActivatableType getActivatableType() { return ActivatableType.FILTER } + void setEnabled(Boolean enabled) { } + }); + + then: + !canEnable + + when: + canEnable = userService.currentUserCanEnable(new IActivatable() { + ActivatableType getActivatableType() { return ActivatableType.METADATA_RESOLVER } + void setEnabled(Boolean enabled) { } + }); + + then: + !canEnable + } + + @WithMockUser(value = "robot", roles = ["ENABLE"]) + def "nonadmin can activate with enable role"() { + given: + def ed = new EntityDescriptor().with { + it.idOfOwner = 'robot' it - }, new Role().with { - name = 'ROLE_NONE' + } + when: + def canEnable = userService.currentUserCanEnable(ed); + + then: + canEnable + + when: + canEnable = userService.currentUserCanEnable(new IActivatable() { + ActivatableType getActivatableType() { return ActivatableType.FILTER } + void setEnabled(Boolean enabled) { } + }); + + then: + canEnable + + when: + canEnable = userService.currentUserCanEnable(new IActivatable() { + ActivatableType getActivatableType() { return ActivatableType.METADATA_RESOLVER } + void setEnabled(Boolean enabled) { } + }); + + then: + canEnable + } + + @WithMockUser(value = "robot2", roles = ["ENABLE"]) + def "nonadmin cannot activate entity descriptor with enable role"() { + given: + def ed = new EntityDescriptor().with { + it.idOfOwner = 'robot' it - }] - roles.each { - roleRepository.save(it) - } + } + when: + def canEnable = userService.currentUserCanEnable(ed); + + then: + !canEnable + + when: + canEnable = userService.currentUserCanEnable(new IActivatable() { + ActivatableType getActivatableType() { return ActivatableType.FILTER } + void setEnabled(Boolean enabled) { } + }); + + then: + canEnable + + when: + canEnable = userService.currentUserCanEnable(new IActivatable() { + ActivatableType getActivatableType() { return ActivatableType.METADATA_RESOLVER } + void setEnabled(Boolean enabled) { } + }); + + then: + canEnable } - + + @WithMockUser(value = "nonadmin", roles = ["USER"]) + @Rollback + def "Validate isAuthorizedFor with non-admin user"() { + given: + ownershipRepository.saveAndFlush(new Ownership(new Owner() { + String getOwnerId() { return "nonadmin" } + OwnerType getOwnerType() { return OwnerType.GROUP } + }, new Ownable() { + String getObjectId() { return "foothing" } + OwnableType getOwnableType() { return OwnableType.ENTITY_DESCRIPTOR } + })) + when: + def isAuth = userService.isAuthorizedFor(new Ownable(){ + String getObjectId() { return "foothing" } + OwnableType getOwnableType() { return OwnableType.ENTITY_DESCRIPTOR } + }); + + then: + isAuth + } + + def "Double Check that shibUIConfiguration includes the enable role"() { + when: + def Optional role = roleRepository.findByName("ROLE_ENABLE") + + then: + role.isPresent() + } + @Rollback def "When creating user, user is set to the correct group"() { given: @@ -110,24 +297,24 @@ class UserServiceTests extends Specification { gb.setResourceId("testingGroupBBB") gb.setName("Group BBB") gb = groupService.createGroup(gb) - + Optional userRole = roleRepository.findByName("ROLE_USER") def User user = new User(username: "someUser", roles:[userRole.get()], password: "foo") user.setGroup(gb) - - when: + + when: def User result = userService.save(user) - + then: result.groupId == "testingGroupBBB" result.username == "someUser" result.userGroups.size() == 1 - + // Raw check that the DB is correct for ownership def Set users = ownershipRepository.findUsersByOwner(gb) users.size() == 1 users.getAt(0).ownedId == "someUser" - + // Validate that loading the group has the correct list as well Group g = groupService.find("testingGroupBBB"); g.ownedItems.size() == 1 @@ -140,31 +327,31 @@ class UserServiceTests extends Specification { ga.setResourceId("testingGroup") ga.setName("Group A") ga = groupService.createGroup(ga) - + Group gb = new Group(); gb.setResourceId("testingGroupBBB") gb.setName("Group BBB") gb = groupService.createGroup(gb) - + Optional userRole = roleRepository.findByName("ROLE_USER") def User user = new User(username: "someUser", roles:[userRole.get()], password: "foo") user.setGroup(gb) def User userInB = userService.save(user) - + when: userInB.setGroupId("testingGroup") // changing groups will happen by updating the user's groupid (from the ui) def User result = userService.save(userInB) - + then: result.groupId == "testingGroup" result.username == "someUser" result.userGroups.size() == 1 - + // Raw check that the DB is correct for ownership def Set users = ownershipRepository.findUsersByOwner(ga) users.size() == 1 users.getAt(0).ownedId == "someUser" - + // check db is correct for the previous group as well def Set users2 = ownershipRepository.findUsersByOwner(gb) users2.size() == 0 @@ -172,11 +359,11 @@ class UserServiceTests extends Specification { // Validate that loading the group has the correct list as well Group g = groupService.find("testingGroup"); g.ownedItems.size() == 1 - + Group g2 = groupService.find("testingGroupBBB"); g2.ownedItems.size() == 0 } - + @Rollback def "logically try to match user controller test causing headaches"() { given: @@ -184,26 +371,26 @@ class UserServiceTests extends Specification { ga.setResourceId("testingGroup") ga.setName("Group A") ga = groupService.createGroup(ga) - + Optional userRole = roleRepository.findByName("ROLE_USER") def User user = new User(username: "someUser", firstName: "Fred", lastName: "Flintstone", roles:[userRole.get()], password: "foo") user.setGroup(ga) userService.save(user) - + when: def User flintstoneUser = userRepository.findByUsername("someUser").get() flintstoneUser.setFirstName("Wilma") flintstoneUser.setGroupId("testingGroup") - + def User result = userService.save(flintstoneUser) - + then: result.groupId == "testingGroup" result.username == "someUser" result.userGroups.size() == 1 result.firstName == "Wilma" } - + @Rollback def "When creating user, user with multiple groups is saved correctly"() { given: @@ -211,12 +398,12 @@ class UserServiceTests extends Specification { ga.setResourceId("testingGroup") ga.setName("Group A") ga = groupService.createGroup(ga) - + Group gb = new Group(); gb.setResourceId("testingGroupBBB") gb.setName("Group BBB") gb = groupService.createGroup(gb) - + Optional userRole = roleRepository.findByName("ROLE_USER") User user = new User().with( { it.username = "someUser" @@ -224,41 +411,41 @@ class UserServiceTests extends Specification { it.password = "foo" it }) - + HashSet groups = new HashSet<>() groups.add(ga) groups.add(gb) user.setGroups(groups) - + when: def result = userService.save(user) - + then: result.userGroups.size() == 2 - + // Raw check that the DB is correct for ownership def Set users = ownershipRepository.findUsersByOwner(ga) users.size() == 1 users.getAt(0).ownedId == "someUser" - + def Set users2 = ownershipRepository.findUsersByOwner(gb) users2.size() == 1 users2.getAt(0).ownedId == "someUser" - + when: def userFromDb = userRepository.findById(result.id).get(); - + then: userFromDb.getUserGroups().size() == 2 - + when: Group gbUpdated = groupService.find("testingGroupBBB") - + then: gbUpdated.ownedItems.size() == 1 } - @org.springframework.boot.test.context.TestConfiguration + @TestConfiguration @Profile("local") static class LocalConfig { @Bean