diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/DevConfig.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/DevConfig.groovy index a644a58a0..70892cbb7 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/DevConfig.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/DevConfig.groovy @@ -12,9 +12,11 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ReloadableMetadat import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository +import edu.internet2.tier.shibboleth.admin.ui.security.model.Approvers import edu.internet2.tier.shibboleth.admin.ui.security.model.Group 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.repository.ApproversRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.GroupsRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository @@ -39,6 +41,7 @@ class DevConfig { private final OpenSamlObjects openSamlObjects private final RoleRepository roleRepository private final UserRepository userRepository + private final ApproversRepository approversRepository @Autowired private UserService userService @@ -49,7 +52,8 @@ class DevConfig { RoleRepository roleRepository, EntityDescriptorRepository entityDescriptorRepository, OpenSamlObjects openSamlObjects, - IGroupService groupService) { + IGroupService groupService, + ApproversRepository approversRepository) { this.userRepository = adminUserRepository this.metadataResolverRepository = metadataResolverRepository @@ -57,7 +61,7 @@ class DevConfig { this.entityDescriptorRepository = entityDescriptorRepository this.openSamlObjects = openSamlObjects this.groupsRepository = groupsRepository - + this.approversRepository = approversRepository groupService.ensureAdminGroupExists() } @@ -85,7 +89,29 @@ class DevConfig { } } groupsRepository.flush() - + + List apprGroups = new ArrayList<>() + String[] groupNames = ['XXX', 'YYY', 'ZZZ'] + groupNames.each {name -> { + Group group = new Group().with({ + it.name = name + it.description = name + it.resourceId = name + it + }) + if (name != "ZZZ") { + apprGroups.add(groupsRepository.save(group)) + } else { + Approvers approvers = new Approvers() + approvers.setApproverGroups(apprGroups) + List apprList = new ArrayList<>() + apprList.add(approversRepository.save(approvers)) + group.setApproversList(apprList) + groupsRepository.save(group) + } + }} + groupsRepository.flush() + if (roleRepository.count() == 0) { def roles = [new Role().with { name = 'ROLE_ADMIN' diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorController.java index 0a6cda0b3..3e1f4db27 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorController.java @@ -85,6 +85,12 @@ public ResponseEntity getAll() throws ForbiddenException { return ResponseEntity.ok(entityDescriptorService.getAllEntityDescriptorProjectionsBasedOnUserAccess()); } + @GetMapping("/EntityDescriptors/needsApproval") + @Transactional + public ResponseEntity getAllNeedingApproval() throws ForbiddenException { + return ResponseEntity.ok(entityDescriptorService.getAllEntityDescriptorProjectionsNeedingApprovalBasedOnUserAccess()); + } + @GetMapping("/EntityDescriptor/{resourceId}/Versions") @Transactional public ResponseEntity getAllVersions(@PathVariable String resourceId) throws PersistentEntityNotFound, ForbiddenException { diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptor.java index 33e7ce6d1..872f78b1d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptor.java @@ -47,13 +47,17 @@ public class EntityDescriptor extends AbstractDescriptor implements org.opensaml @NotAudited private AffiliationDescriptor affiliationDescriptor; + @Getter + @Setter + private boolean approved; + @OneToOne(cascade = CascadeType.ALL) @NotAudited private AttributeAuthorityDescriptor attributeAuthorityDescriptor; @ElementCollection (fetch = FetchType.EAGER) @EqualsAndHashCode.Exclude - private List approved = new ArrayList<>(); + private List approvedBy = new ArrayList<>(); @OneToOne(cascade = CascadeType.ALL) @NotAudited @@ -317,16 +321,16 @@ public OwnableType getOwnableType() { } public void addApproval(Group group) { - approved.add(group.getName()); + approvedBy.add(group.getName()); } public int approvedCount() { - return approved.size(); + return approvedBy.size(); } public void removeLastApproval() { - if (!approved.isEmpty()) { - approved.remove(approved.size() - 1); + if (!approvedBy.isEmpty()) { + approvedBy.remove(approvedBy.size() - 1); } } } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepository.java index bb2b275d6..a4ff5c43f 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepository.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepository.java @@ -3,6 +3,7 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.util.List; import java.util.stream.Stream; @@ -37,4 +38,10 @@ public interface EntityDescriptorRepository extends JpaRepository findAllByIdOfOwnerIsNull(); + + @Query(value = "select e from EntityDescriptor e" + + " where e.idOfOwner in (:groupIds)" + + " and e.serviceEnabled = false" + + " and e.approved = false") + List getEntityDescriptorsNeedingApproval(@Param("groupIds") List groupIds); } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Approvers.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Approvers.java index dd0c4bec4..2a0739646 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Approvers.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Approvers.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.security.model; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import lombok.NoArgsConstructor; @@ -8,6 +9,7 @@ import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; +import javax.persistence.Transient; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -20,8 +22,24 @@ public class Approvers { @Id @Column(name = "resource_id") + @JsonIgnore private String resourceId = UUID.randomUUID().toString(); @ManyToMany + @JsonIgnore private List approverGroups = new ArrayList<>(); + + @Transient + private List approverGroupIds = new ArrayList<>(); + + public List getApproverGroupIds() { + if (approverGroupIds.isEmpty()) { + approverGroups.forEach(group -> approverGroupIds.add(group.getResourceId())); + } + return approverGroupIds; + } + + public void setApproverGroups(List appGroups) { + this.approverGroups = appGroups; + } } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Group.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Group.java index 14597deb2..2591e36b5 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Group.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Group.java @@ -34,6 +34,7 @@ public class Group implements Owner { public static Group ADMIN_GROUP; @Transient + @JsonIgnore List approveForList = new ArrayList<>(); @Column(name = "group_description") @@ -107,4 +108,9 @@ public List getApproveForList() { } return approveForList; } + + @Override + public String toString() { + return "Group resourceId=" + resourceId; + } } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/GroupServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/GroupServiceImpl.java index a9607534f..917b84fd7 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/GroupServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/GroupServiceImpl.java @@ -115,16 +115,13 @@ public List findAll() { return groupRepository.findAll(); } - @Override - public Group updateGroup(Group group) throws PersistentEntityNotFound, InvalidGroupRegexException { - manageApproversList(group); // have to make sure that approvers have been saved before a fetch or we can get data integrity errors on lookup... - Group g = find(group.getResourceId()); - if (g == null) { - throw new PersistentEntityNotFound(String.format("Unable to find group with resource id: [%s] and name: [%s]", - group.getResourceId(), group.getName())); + private List getGroupListFromIds(List approverGroupIds) { + List result = new ArrayList<>(); + for (String id : approverGroupIds) { + Group g = find(id); + result.add(g); } - validateGroupRegex(group); - return groupRepository.save(group); + return result; } private void manageApproversList(Group group) { @@ -134,14 +131,26 @@ private void manageApproversList(Group group) { List updatedApprovers = new ArrayList<>(); group.getApproversList().forEach(approvers -> { Approvers savedApprovers = approversRepository.findByResourceId(approvers.getResourceId()); - savedApprovers = savedApprovers == null ? approvers : savedApprovers; - savedApprovers.setApproverGroups(approvers.getApproverGroups()); + savedApprovers = savedApprovers == null ? approversRepository.save(approvers) : savedApprovers; + savedApprovers.setApproverGroups(getGroupListFromIds(approvers.getApproverGroupIds())); Approvers updatedApp = approversRepository.save(savedApprovers); updatedApprovers.add(updatedApp); }); group.setApproversList(updatedApprovers); } + @Override + public Group updateGroup(Group group) throws PersistentEntityNotFound, InvalidGroupRegexException { + manageApproversList(group); // have to make sure that approvers have been saved before a fetch or we can get data integrity errors on lookup... + Group g = find(group.getResourceId()); + if (g == null) { + throw new PersistentEntityNotFound(String.format("Unable to find group with resource id: [%s] and name: [%s]", + group.getResourceId(), group.getName())); + } + validateGroupRegex(group); + return groupRepository.save(group); + } + /** * If the regex is blank simply return */ diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityDescriptorService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityDescriptorService.java index 1f37b83bc..8ee4adb52 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityDescriptorService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityDescriptorService.java @@ -124,4 +124,6 @@ EntityDescriptorRepresentation updateEntityDescriptorEnabledStatus(String resour EntityDescriptorRepresentation updateGroupForEntityDescriptor(String resourceId, String groupId); EntityDescriptorRepresentation changeApproveStatusOfEntityDescriptor(String resourceId, boolean status) throws PersistentEntityNotFound, ForbiddenException; + + List getAllEntityDescriptorProjectionsNeedingApprovalBasedOnUserAccess(); } \ No newline at end of file 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 0b1f67932..667477f09 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 @@ -91,10 +91,14 @@ public EntityDescriptorRepresentation changeApproveStatusOfEntityDescriptor(Stri throw new ForbiddenException("You do not have the permissions necessary to approve this entity descriptor."); } ed.addApproval(userService.getCurrentUserGroup()); + Group ownerGroup = groupService.find(ed.getIdOfOwner()); + ed.setApproved(ed.approvedCount() == ownerGroup.getApproversList().size()); // safe check in case of weird race conditions from the UI ed = entityDescriptorRepository.save(ed); } } else { // un-approve ed.removeLastApproval(); + Group ownerGroup = groupService.find(ed.getIdOfOwner()); + ed.setApproved(ed.approvedCount() == ownerGroup.getApproversList().size()); // safe check in case of weird race conditions from the UI ed = entityDescriptorRepository.save(ed); } return createRepresentationFromDescriptor(ed); @@ -434,6 +438,16 @@ public List getAllEntityDescriptorProjectionsBasedOn } } + /** + * Based on the current users group, find those entities that the user can approve that need approval + */ + @Override + public List getAllEntityDescriptorProjectionsNeedingApprovalBasedOnUserAccess() { + List groupsToApprove = userService.getGroupsCurrentUserCanApprove(); + List result = entityDescriptorRepository.getEntityDescriptorsNeedingApproval(groupsToApprove); + return result; + } + @Override public List getAttributeReleaseListFromAttributeList(List attributeList) { if (attributeList == null) { @@ -512,10 +526,13 @@ public EntityDescriptorRepresentation updateEntityDescriptorEnabledStatus(String // check to see if approvals have been completed int approvedCount = ed.approvedCount(); List approversList = groupService.find(ed.getIdOfOwner()).getApproversList(); - if (!ed.isServiceEnabled() && !userService.currentUserIsAdmin() && approversList.size() > approvedCount) { + if (status == true && !ed.isServiceEnabled() && !userService.currentUserIsAdmin() && approversList.size() > approvedCount) { throw new ForbiddenException("Approval must be completed before you can change the enable status of this entity descriptor."); } ed.setServiceEnabled(status); + if (status == true) { + ed.setApproved(true); + } ed = entityDescriptorRepository.save(ed); return createRepresentationFromDescriptor(ed); } diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 6593646f9..c37e6c203 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -52,6 +52,7 @@ spring.jpa.properties.hibernate.show_sql=false spring.jpa.properties.hibernate.format_sql=true spring.jpa.properties.hibernate.check_nullability=true spring.jpa.hibernate.use-new-id-generator-mappings=true +spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true #Envers versioning spring.jpa.properties.org.hibernate.envers.store_data_at_delete=true diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/ApproveControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/ApproveControllerTests.groovy index b18c40aad..2a5b46883 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/ApproveControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/ApproveControllerTests.groovy @@ -110,27 +110,34 @@ class ApproveControllerTests extends AbstractBaseDataJpaTest { @WithMockUser(value = "AUser", roles = ["USER"]) def 'Owner group cannot approve their own entity descriptor'() { expect: + entityDescriptorRepository.findByResourceId(defaultEntityDescriptorResourceId).isApproved() == false try { mockMvc.perform(patch("/api/approve/entityDescriptor/" + defaultEntityDescriptorResourceId + "/approve")) } catch (Exception e) { e instanceof ForbiddenException } + entityDescriptorRepository.findByResourceId(defaultEntityDescriptorResourceId).isApproved() == false } @WithMockUser(value = "DUser", roles = ["USER"]) def 'non-approver group cannot approve entity descriptor'() { expect: + entityDescriptorRepository.findByResourceId(defaultEntityDescriptorResourceId).isApproved() == false try { mockMvc.perform(patch("/api/approve/entityDescriptor/" + defaultEntityDescriptorResourceId + "/approve")) } catch (Exception e) { e instanceof ForbiddenException } + entityDescriptorRepository.findByResourceId(defaultEntityDescriptorResourceId).isApproved() == false } @WithMockUser(value = "BUser", roles = ["USER"]) def 'Approver group can approve an entity descriptor'() { + expect: + entityDescriptorRepository.findByResourceId(defaultEntityDescriptorResourceId).isApproved() == false + when: def result = mockMvc.perform(patch("/api/approve/entityDescriptor/" + defaultEntityDescriptorResourceId + "/approve")) @@ -139,10 +146,14 @@ class ApproveControllerTests extends AbstractBaseDataJpaTest { .andExpect(jsonPath("\$.id").value(defaultEntityDescriptorResourceId)) .andExpect(jsonPath("\$.serviceEnabled").value(false)) .andExpect(jsonPath("\$.approved").value(true)) + entityDescriptorRepository.findByResourceId(defaultEntityDescriptorResourceId).isApproved() } @WithMockUser(value = "BUser", roles = ["USER"]) def 'Approver can approve and un-approve an entity descriptor'() { + expect: + entityDescriptorRepository.findByResourceId(defaultEntityDescriptorResourceId).isApproved() == false + when: def result = mockMvc.perform(patch("/api/approve/entityDescriptor/" + defaultEntityDescriptorResourceId + "/approve")) @@ -151,6 +162,7 @@ class ApproveControllerTests extends AbstractBaseDataJpaTest { .andExpect(jsonPath("\$.id").value(defaultEntityDescriptorResourceId)) .andExpect(jsonPath("\$.serviceEnabled").value(false)) .andExpect(jsonPath("\$.approved").value(true)) + entityDescriptorRepository.findByResourceId(defaultEntityDescriptorResourceId).isApproved() when: def result2 = mockMvc.perform(patch("/api/approve/entityDescriptor/" + defaultEntityDescriptorResourceId + "/unapprove")) @@ -160,6 +172,6 @@ class ApproveControllerTests extends AbstractBaseDataJpaTest { .andExpect(jsonPath("\$.id").value(defaultEntityDescriptorResourceId)) .andExpect(jsonPath("\$.serviceEnabled").value(false)) .andExpect(jsonPath("\$.approved").value(false)) - + entityDescriptorRepository.findByResourceId(defaultEntityDescriptorResourceId).isApproved() == false } } \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/controller/GroupsControllerIntegrationTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/controller/GroupsControllerIntegrationTests.groovy index 6fae2ed87..a4c46e1b1 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/controller/GroupsControllerIntegrationTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/controller/GroupsControllerIntegrationTests.groovy @@ -72,7 +72,6 @@ class GroupsControllerIntegrationTests extends AbstractBaseDataJpaTest { when: "POST new group with approvers" groupService.clearAllForTesting() - List apprGroups = new ArrayList<>() String[] groupNames = ['AAA', 'BBB', 'CCC', 'DDD'] groupNames.each {name -> { Group group = new Group().with({ @@ -86,13 +85,14 @@ class GroupsControllerIntegrationTests extends AbstractBaseDataJpaTest { entityManager.flush() entityManager.clear() + List apprGroups = new ArrayList<>() groupNames.each {name ->{ if (!name.equals('AAA')) { - apprGroups.add(groupRepository.findByResourceId(name)) + apprGroups.add(name) } }} Approvers approvers = new Approvers() - approvers.setApproverGroups(apprGroups) + approvers.setApproverGroupIds(apprGroups) def apprList = new ArrayList<>() apprList.add(approvers) def newGroup2 = [name: 'Foo', description: 'Bar', resourceId: 'FooBar', approversList: apprList] @@ -104,9 +104,9 @@ class GroupsControllerIntegrationTests extends AbstractBaseDataJpaTest { .andExpect(jsonPath("\$.name").value("Foo")) .andExpect(jsonPath("\$.resourceId").value("FooBar")) .andExpect(jsonPath("\$.description").value("Bar")) - .andExpect(jsonPath("\$.approversList[0].approverGroups[0].resourceId").value("BBB")) - .andExpect(jsonPath("\$.approversList[0].approverGroups[1].resourceId").value("CCC")) - .andExpect(jsonPath("\$.approversList[0].approverGroups[2].resourceId").value("DDD")) + .andExpect(jsonPath("\$.approversList[0].approverGroupIds[0]").value("BBB")) + .andExpect(jsonPath("\$.approversList[0].approverGroupIds[1]").value("CCC")) + .andExpect(jsonPath("\$.approversList[0].approverGroupIds[2]").value("DDD")) } @WithMockAdmin diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/GroupServiceForTesting.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/GroupServiceForTesting.groovy index a3f223efb..6d18c2cd8 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/GroupServiceForTesting.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/GroupServiceForTesting.groovy @@ -14,6 +14,7 @@ class GroupServiceForTesting extends GroupServiceImpl { @Transactional void clearAllForTesting() { + approversRepository.deleteAll() groupRepository.deleteAll() ownershipRepository.clearAllOwnedByGroup() ensureAdminGroupExists() diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/GroupServiceTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/GroupServiceTests.groovy index 16045bca7..6da1ff232 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/GroupServiceTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/GroupServiceTests.groovy @@ -85,7 +85,7 @@ class GroupServiceTests extends AbstractBaseDataJpaTest { def "CRUD operations - approver groups" () { given: groupService.clearAllForTesting() - List apprGroups = new ArrayList<>() + List apprGroups = new ArrayList<>() String[] groupNames = ['AAA', 'BBB', 'CCC', 'DDD'] groupNames.each {name -> { Group group = new Group().with({ @@ -102,12 +102,12 @@ class GroupServiceTests extends AbstractBaseDataJpaTest { when: "Adding approval list to a group" groupNames.each {name ->{ if (!name.equals('AAA')) { - apprGroups.add(groupRepository.findByResourceId(name)) + apprGroups.add(name) } }} Approvers approvers = new Approvers() - approvers.setApproverGroups(apprGroups) - def apprList = new ArrayList<>() + approvers.setApproverGroupIds(apprGroups) + List apprList = new ArrayList<>() apprList.add(approvers) Group aaaGroup = groupService.find('AAA') aaaGroup.setApproversList(apprList) @@ -118,12 +118,12 @@ class GroupServiceTests extends AbstractBaseDataJpaTest { lookupGroup.getApproversList().size() == 1 def approvalGroups = lookupGroup.getApproversList().get(0).getApproverGroups() approvalGroups.size() == 3 - apprGroups.each {group -> { - assert approvalGroups.contains(group)} + approvalGroups.each {group -> { + assert apprGroups.contains(group.getResourceId())} } when: "removing approver group from existing list" - approvers.getApproverGroups().remove(groupService.find('BBB')) + approvers.setApproverGroupIds(Arrays.asList("CCC", "DDD")) apprList = new ArrayList<>() apprList.add(approvers) aaaGroup.setApproversList(apprList) @@ -134,13 +134,7 @@ class GroupServiceTests extends AbstractBaseDataJpaTest { lookupGroup2.getApproversList().size() == 1 def approvalGroups2 = lookupGroup2.getApproversList().get(0).getApproverGroups() approvalGroups2.size() == 2 - apprGroups.each { group -> - { - if (group.getResourceId() != 'BBB') { - assert approvalGroups2.contains(group) - } - } - } + approvalGroups2.forEach(group -> group.getResourceId().equals("CCC") || group.getResourceId().equals("DDD")) when: "removing all approver groups" apprList = new ArrayList<>() diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserServiceTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserServiceTests.groovy index d40e2bd1c..95c8dc5e6 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserServiceTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserServiceTests.groovy @@ -5,6 +5,7 @@ import edu.internet2.tier.shibboleth.admin.ui.configuration.DevConfig import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository +import edu.internet2.tier.shibboleth.admin.ui.security.repository.ApproversRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.GroupsRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository @@ -74,9 +75,10 @@ class AdminUserServiceTests extends AbstractBaseDataJpaTest { @Bean DevConfig devConfig(UserRepository adminUserRepository, GroupsRepository groupsRepository, IGroupService groupService, MetadataResolverRepository metadataResolverRepository, OpenSamlObjects openSamlObjects, UserService userService, - RoleRepository roleRepository, EntityDescriptorRepository entityDescriptorRepository) { + RoleRepository roleRepository, EntityDescriptorRepository entityDescriptorRepository, + ApproversRepository approversRepository) { DevConfig dc = new DevConfig( adminUserRepository, groupsRepository, metadataResolverRepository, roleRepository, - entityDescriptorRepository, openSamlObjects, groupService).with { + entityDescriptorRepository, openSamlObjects, groupService, approversRepository).with { it.userService = userService it } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy index 9e65bf28a..6e7e2cf43 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy @@ -15,6 +15,12 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.ServiceProviderSso import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaLocationLookup import edu.internet2.tier.shibboleth.admin.ui.jsonschema.LowLevelJsonSchemaValidator import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects +import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorProjection +import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository +import edu.internet2.tier.shibboleth.admin.ui.security.model.Approvers +import edu.internet2.tier.shibboleth.admin.ui.security.model.Group +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.util.RandomGenerator import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator import edu.internet2.tier.shibboleth.admin.util.EntityDescriptorConversionUtils @@ -24,6 +30,8 @@ import org.springframework.boot.test.json.JacksonTester import org.springframework.context.annotation.PropertySource import org.springframework.core.io.DefaultResourceLoader import org.springframework.mock.http.MockHttpInputMessage +import org.springframework.security.test.context.support.WithMockUser +import org.springframework.transaction.annotation.Transactional import org.xmlunit.builder.DiffBuilder import org.xmlunit.builder.Input import org.xmlunit.diff.DefaultNodeMatcher @@ -52,14 +60,64 @@ class JPAEntityDescriptorServiceImplTests extends AbstractBaseDataJpaTest { RandomGenerator generator JacksonTester jacksonTester + @Autowired + EntityDescriptorRepository entityDescriptorRepository + + @Transactional def setup() { JacksonTester.initFields(this, mapper) generator = new RandomGenerator() EntityDescriptorConversionUtils.openSamlObjects = openSamlObjects EntityDescriptorConversionUtils.entityService = entityService openSamlObjects.init() + + groupService.clearAllForTesting() + List apprGroups = new ArrayList<>() + String[] groupNames = ['BBB', 'CCC', 'EEE', 'AAA'] + groupNames.each {name -> { + Group group = new Group().with({ + it.name = name + it.description = name + it.resourceId = name + it + }) + if (name != "AAA") { + apprGroups.add(groupRepository.save(group)) + } else { + Approvers approvers = new Approvers() + approvers.setApproverGroups(apprGroups) + List apprList = new ArrayList<>() + apprList.add(approversRepository.save(approvers)) + group.setApproversList(apprList) + groupRepository.save(group) + } + }} + Group group = new Group().with({ + it.name = 'DDD' + it.description = 'DDD' + it.resourceId = 'DDD' + it + }) + Approvers approvers = new Approvers() + apprGroups = new ArrayList<>() + apprGroups.add(groupRepository.findByResourceId('BBB')) + approvers.setApproverGroups(apprGroups) + List apprList = new ArrayList<>() + apprList.add(approversRepository.save(approvers)) + group.setApproversList(apprList) + groupRepository.save(group) + + Optional userRole = roleRepository.findByName("ROLE_USER") + User user = new User(username: "bbUser", roles:[userRole.get()], password: "foo") + user.setGroup(groupRepository.findByResourceId("BBB")) + userService.save(user) + + entityManager.flush() + entityManager.clear() } + + def "simple Entity Descriptor"() { when: def expected = '''