Skip to content

Commit

Permalink
SHIBUI-2393
Browse files Browse the repository at this point in the history
Added approval and enable endpoints for dynamic registration
  • Loading branch information
chasegawa committed Nov 14, 2022
1 parent 8320b88 commit ca1f1b1
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

import edu.internet2.tier.shibboleth.admin.ui.domain.exceptions.MetadataFileNotFoundException;
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter;
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.DynamicRegistrationRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.domain.oidc.DynamicRegistrationInfo;
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver;
import edu.internet2.tier.shibboleth.admin.ui.exception.ForbiddenException;
import edu.internet2.tier.shibboleth.admin.ui.exception.InitializationException;
import edu.internet2.tier.shibboleth.admin.ui.exception.PersistentEntityNotFound;
import edu.internet2.tier.shibboleth.admin.ui.exception.UnsupportedShibUiOperationException;
import edu.internet2.tier.shibboleth.admin.ui.service.DynamicRegistrationService;
import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService;
import edu.internet2.tier.shibboleth.admin.ui.service.FilterService;
import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService;
Expand All @@ -26,6 +30,8 @@
@RequestMapping("/api/activate")
@Tags(value = {@Tag(name = "activate")})
public class ActivateController {
@Autowired
private DynamicRegistrationService dynamicRegistrationService;

@Autowired
private EntityDescriptorService entityDescriptorService;
Expand All @@ -36,14 +42,24 @@ public class ActivateController {
@Autowired
private MetadataResolverService metadataResolverService;

@PatchMapping(path = "/DynamicRegistration/{resourceId}/{mode}")
@Transactional
public ResponseEntity<?> enableDynamicRegistration(@PathVariable String resourceId, @PathVariable String mode) throws PersistentEntityNotFound, ForbiddenException, UnsupportedShibUiOperationException {
if ("enable".equalsIgnoreCase(mode)) {
DynamicRegistrationRepresentation drr = dynamicRegistrationService.enableDynamicRegistration(resourceId);
return ResponseEntity.ok(drr);
}
throw new UnsupportedShibUiOperationException("Disable is not a valid operation for Dynamic Registrations at this time");
}

@PatchMapping(path = "/entityDescriptor/{resourceId}/{mode}")
@Transactional
public ResponseEntity<?> enableEntityDescriptor(@PathVariable String resourceId, @PathVariable String mode) throws PersistentEntityNotFound, ForbiddenException {
boolean status = "enable".equalsIgnoreCase(mode);
EntityDescriptorRepresentation edr = entityDescriptorService.updateEntityDescriptorEnabledStatus(resourceId, status);
return ResponseEntity.ok(edr);
}

@PatchMapping(path = "/MetadataResolvers/{metadataResolverId}/Filter/{resourceId}/{mode}")
@Transactional
public ResponseEntity<?> enableFilter(@PathVariable String metadataResolverId, @PathVariable String resourceId, @PathVariable String mode) throws PersistentEntityNotFound, ForbiddenException, ScriptException {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package edu.internet2.tier.shibboleth.admin.ui.controller;

import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.DynamicRegistrationRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.exception.ForbiddenException;
import edu.internet2.tier.shibboleth.admin.ui.exception.PersistentEntityNotFound;
import edu.internet2.tier.shibboleth.admin.ui.exception.UnsupportedShibUiOperationException;
import edu.internet2.tier.shibboleth.admin.ui.service.DynamicRegistrationService;
import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.tags.Tags;
Expand All @@ -18,9 +21,20 @@
@RequestMapping("/api/approve")
@Tags(value = {@Tag(name = "approve")})
public class ApprovalController {
@Autowired
private DynamicRegistrationService dynamicRegistrationService;

@Autowired
private EntityDescriptorService entityDescriptorService;

@PatchMapping(path = "/DynamicRegistration/{resourceId}/{mode}")
@Transactional
public ResponseEntity<?> approveDynamicRegistration(@PathVariable String resourceId, @PathVariable String mode) throws PersistentEntityNotFound, ForbiddenException {
boolean status = "approve".equalsIgnoreCase(mode);
DynamicRegistrationRepresentation drr = dynamicRegistrationService.approveDynamicRegistration(resourceId, status);
return ResponseEntity.ok(drr);
}

@PatchMapping(path = "/entityDescriptor/{resourceId}/{mode}")
@Transactional
public ResponseEntity<?> approveEntityDescriptor(@PathVariable String resourceId, @PathVariable String mode) throws PersistentEntityNotFound, ForbiddenException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import edu.internet2.tier.shibboleth.admin.ui.exception.ForbiddenException;
import edu.internet2.tier.shibboleth.admin.ui.exception.InitializationException;
import edu.internet2.tier.shibboleth.admin.ui.exception.PersistentEntityNotFound;
import edu.internet2.tier.shibboleth.admin.ui.exception.UnsupportedShibUiOperationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
Expand Down Expand Up @@ -42,6 +43,9 @@ public ResponseEntity<?> handleMetadataFileNotFoundException(MetadataFileNotFoun
public ResponseEntity<?> handleScriptException(ScriptException e, WebRequest request) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ErrorResponse(String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()), e.getMessage()));
}



@ExceptionHandler({ UnsupportedShibUiOperationException.class })
public ResponseEntity<?> handleUnsupportedShibUiOperationException(UnsupportedShibUiOperationException e, WebRequest request) {
return ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED).body(new ErrorResponse(String.valueOf(HttpStatus.NOT_IMPLEMENTED.value()), e.getMessage()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import edu.internet2.tier.shibboleth.admin.ui.domain.ActivatableType;
import edu.internet2.tier.shibboleth.admin.ui.domain.IActivatable;
import edu.internet2.tier.shibboleth.admin.ui.domain.IApprovable;
import edu.internet2.tier.shibboleth.admin.ui.security.model.Group;
import edu.internet2.tier.shibboleth.admin.ui.security.model.Ownable;
import edu.internet2.tier.shibboleth.admin.ui.security.model.OwnableType;
import lombok.Data;
Expand Down Expand Up @@ -79,4 +80,8 @@ public void removeLastApproval() {
public int approvedCount() {
return approvedBy.size();
}

public void addApproval(Group currentUserGroup) {
approvedBy.add(currentUserGroup.getName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package edu.internet2.tier.shibboleth.admin.ui.exception;

public class UnsupportedShibUiOperationException extends Exception {
public UnsupportedShibUiOperationException() {
super("Operation unsupport in ShibUI at this time");
}

public UnsupportedShibUiOperationException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@
import edu.internet2.tier.shibboleth.admin.ui.exception.PersistentEntityNotFound;

public interface DynamicRegistrationService {
Object getAllDynamicRegistrationsBasedOnUserAccess() throws ForbiddenException;
DynamicRegistrationRepresentation approveDynamicRegistration(String resourceId, boolean status)
throws PersistentEntityNotFound, ForbiddenException;

DynamicRegistrationRepresentation createNew(DynamicRegistrationRepresentation dynRegRepresentation) throws ObjectIdExistsException;

void delete(String resourceId) throws ForbiddenException, PersistentEntityNotFound;

DynamicRegistrationRepresentation update(DynamicRegistrationRepresentation dynRegRepresentation)
throws PersistentEntityNotFound, ForbiddenException;
DynamicRegistrationRepresentation enableDynamicRegistration(String resourceId) throws PersistentEntityNotFound, ForbiddenException;

Object getAllDynamicRegistrationsBasedOnUserAccess() throws ForbiddenException;

DynamicRegistrationRepresentation update(DynamicRegistrationRepresentation dynRegRepresentation) throws PersistentEntityNotFound, ForbiddenException;

DynamicRegistrationRepresentation updateGroupForDynamicRegistration(String resourceId, String groupId)
throws ForbiddenException, PersistentEntityNotFound;
DynamicRegistrationRepresentation updateGroupForDynamicRegistration(String resourceId, String groupId) throws ForbiddenException, PersistentEntityNotFound;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor;
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.DynamicRegistrationRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.domain.oidc.DynamicRegistrationInfo;
import edu.internet2.tier.shibboleth.admin.ui.exception.ForbiddenException;
import edu.internet2.tier.shibboleth.admin.ui.exception.ObjectIdExistsException;
import edu.internet2.tier.shibboleth.admin.ui.exception.PersistentEntityNotFound;
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.Owner;
import edu.internet2.tier.shibboleth.admin.ui.security.model.OwnerType;
Expand Down Expand Up @@ -47,6 +49,36 @@ public class JPADynamicRegistrationServiceImpl implements DynamicRegistrationSer
@Autowired
UserService userService;

@Override
public DynamicRegistrationRepresentation approveDynamicRegistration(String resourceId, boolean status) throws PersistentEntityNotFound, ForbiddenException {
DynamicRegistrationInfo dri = repository.findByResourceId(resourceId);
if (dri == null) {
throw new PersistentEntityNotFound("Dynamic Registration with resourceid[ " + resourceId + " ] was not found for approval");
}
return changeApproveStatusOfDynamicRepresentation(dri, status);
}

private DynamicRegistrationRepresentation changeApproveStatusOfDynamicRepresentation(DynamicRegistrationInfo dri, boolean status) throws ForbiddenException {
if (!shibUiAuthorizationDelegate.hasPermission(userService.getCurrentUserAuthentication(), dri, PermissionType.approve)) {
throw new ForbiddenException("You do not have the permissions necessary to approve this dynamic registration.");
}
if (status) { // approve
int approvedCount = dri.approvedCount(); // total number of approvals so far
List<Approvers> theApprovers = groupService.find(dri.getIdOfOwner()).getApproversList();
if (theApprovers.size() > approvedCount) { // don't add if we already have enough approvals
dri.addApproval(userService.getCurrentUserGroup());
}
dri.setApproved(dri.approvedCount() >= theApprovers.size()); // future check for multiple approvals needed
dri = repository.save(dri);
} else { // un-approve
dri.removeLastApproval();
Group ownerGroup = groupService.find(dri.getIdOfOwner());
dri.setApproved(dri.approvedCount() >= ownerGroup.getApproversList().size()); // safe check in case of weird race conditions from the UI
dri = repository.save(dri);
}
return new DynamicRegistrationRepresentation(dri);
}

@Override
public DynamicRegistrationRepresentation createNew(DynamicRegistrationRepresentation dynRegRepresentation) throws ObjectIdExistsException {
if (entityExists(dynRegRepresentation.getResourceId())) {
Expand Down Expand Up @@ -76,16 +108,28 @@ public void delete(String resourceId) throws ForbiddenException, PersistentEntit
if (!shibUiAuthorizationDelegate.hasPermission(userService.getCurrentUserAuthentication(), null, PermissionType.admin)) {
throw new ForbiddenException("Deleting a Dynamic Registration Source is only allowed by an admin.");
}

DynamicRegistrationInfo ed = repository.findByResourceId(resourceId);
if (ed==null) {
DynamicRegistrationInfo dri = repository.findByResourceId(resourceId);
if (dri == null) {
throw new PersistentEntityNotFound("Dynamic Registration not found for resource id: " + resourceId);
}
if (ed.isEnabled()) {
if (dri.isEnabled()) {
throw new ForbiddenException("Deleting an enabled Dynamic Registration Source is not allowed.");
}
ownershipRepository.deleteEntriesForOwnedObject(ed);
repository.delete(ed);
ownershipRepository.deleteEntriesForOwnedObject(dri);
repository.delete(dri);
}

@Override
public DynamicRegistrationRepresentation enableDynamicRegistration(String resourceId) throws PersistentEntityNotFound, ForbiddenException {
DynamicRegistrationInfo existingDri = repository.findByResourceId(resourceId);
if (existingDri == null) {
throw new PersistentEntityNotFound(String.format("The dynamic registration with id [%s] was not found for update.", existingDri.getResourceId()));
}
if (!shibUiAuthorizationDelegate.hasPermission(userService.getCurrentUserAuthentication(), existingDri, PermissionType.enable)) {
throw new ForbiddenException("You do not have the permissions necessary to enable this service");
}
// TODO do something...
return new DynamicRegistrationRepresentation(existingDri);
}

private boolean entityExists(String id) {
Expand All @@ -102,7 +146,7 @@ public DynamicRegistrationRepresentation update(DynamicRegistrationRepresentatio
throws PersistentEntityNotFound, ForbiddenException, ConcurrentModificationException {
DynamicRegistrationInfo existingDri = repository.findByResourceId(dynRegRepresentation.getResourceId());
if (existingDri == null) {
throw new PersistentEntityNotFound(String.format("The dynamic registration with entity id [%s] was not found for update.", existingDri.getResourceId()));
throw new PersistentEntityNotFound(String.format("The dynamic registration with id [%s] was not found for update.", existingDri.getResourceId()));
}
if (dynRegRepresentation.isEnabled() && !shibUiAuthorizationDelegate.hasPermission(userService.getCurrentUserAuthentication(), existingDri, PermissionType.enable)) {
throw new ForbiddenException("You do not have the permissions necessary to enable this service.");
Expand Down Expand Up @@ -132,7 +176,7 @@ public DynamicRegistrationRepresentation update(DynamicRegistrationRepresentatio
public DynamicRegistrationRepresentation updateGroupForDynamicRegistration(String resourceId, String groupId) throws ForbiddenException, PersistentEntityNotFound {
DynamicRegistrationInfo existingDri = repository.findByResourceId(resourceId);
if (existingDri == null) {
throw new PersistentEntityNotFound(String.format("The dynamic registration with entity id [%s] was not found for update.", existingDri.getResourceId()));
throw new PersistentEntityNotFound(String.format("The dynamic registration with id [%s] was not found for update.", existingDri.getResourceId()));
}
if (!shibUiAuthorizationDelegate.hasPermission(userService.getCurrentUserAuthentication(), existingDri, PermissionType.admin)) {
throw new ForbiddenException("You do not have the permissions necessary to change the group for this service.");
Expand Down
Loading

0 comments on commit ca1f1b1

Please sign in to comment.