Skip to content

Commit

Permalink
SHIBUI-2394
Browse files Browse the repository at this point in the history
Incremental commit
  • Loading branch information
chasegawa committed Oct 10, 2022
1 parent 5e85383 commit e2d841b
Show file tree
Hide file tree
Showing 10 changed files with 180 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -49,15 +52,16 @@ class DevConfig {
RoleRepository roleRepository,
EntityDescriptorRepository entityDescriptorRepository,
OpenSamlObjects openSamlObjects,
IGroupService groupService) {
IGroupService groupService,
ApproversRepository approversRepository) {

this.userRepository = adminUserRepository
this.metadataResolverRepository = metadataResolverRepository
this.roleRepository = roleRepository
this.entityDescriptorRepository = entityDescriptorRepository
this.openSamlObjects = openSamlObjects
this.groupsRepository = groupsRepository

this.approversRepository = approversRepository
groupService.ensureAdminGroupExists()
}

Expand Down Expand Up @@ -85,7 +89,29 @@ class DevConfig {
}
}
groupsRepository.flush()


List<Group> 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<Approvers> 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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> approved = new ArrayList<>();
private List<String> approvedBy = new ArrayList<>();

@OneToOne(cascade = CascadeType.ALL)
@NotAudited
Expand Down Expand Up @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -37,4 +38,10 @@ public interface EntityDescriptorRepository extends JpaRepository<EntityDescript
*/
@Deprecated
List<EntityDescriptor> findAllByIdOfOwnerIsNull();

@Query(value = "select e from EntityDescriptor e" +
" where e.idOfOwner in (:groupIds)" +
" and e.serviceEnabled = false" +
" and e.approved = false")
List<EntityDescriptorProjection> getEntityDescriptorsNeedingApproval(@Param("groupIds") List<String> groupIds);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public class Group implements Owner {
public static Group ADMIN_GROUP;

@Transient
@JsonIgnore
List<String> approveForList = new ArrayList<>();

@Column(name = "group_description")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,6 @@ EntityDescriptorRepresentation updateEntityDescriptorEnabledStatus(String resour
EntityDescriptorRepresentation updateGroupForEntityDescriptor(String resourceId, String groupId);

EntityDescriptorRepresentation changeApproveStatusOfEntityDescriptor(String resourceId, boolean status) throws PersistentEntityNotFound, ForbiddenException;

List<EntityDescriptorProjection> getAllEntityDescriptorProjectionsNeedingApprovalBasedOnUserAccess();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -434,6 +438,16 @@ public List<EntityDescriptorProjection> getAllEntityDescriptorProjectionsBasedOn
}
}

/**
* Based on the current users group, find those entities that the user can approve that need approval
*/
@Override
public List<EntityDescriptorProjection> getAllEntityDescriptorProjectionsNeedingApprovalBasedOnUserAccess() {
List<String> groupsToApprove = userService.getGroupsCurrentUserCanApprove();
List<EntityDescriptorProjection> result = entityDescriptorRepository.getEntityDescriptorsNeedingApproval(groupsToApprove);
return result;
}

@Override
public List<String> getAttributeReleaseListFromAttributeList(List<Attribute> attributeList) {
if (attributeList == null) {
Expand Down Expand Up @@ -512,10 +526,13 @@ public EntityDescriptorRepresentation updateEntityDescriptorEnabledStatus(String
// check to see if approvals have been completed
int approvedCount = ed.approvedCount();
List<Approvers> 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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"))

Expand All @@ -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"))

Expand All @@ -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"))
Expand All @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
Loading

0 comments on commit e2d841b

Please sign in to comment.