Skip to content

Commit

Permalink
SHIBUI-2394
Browse files Browse the repository at this point in the history
Moving logic where auth checks are being done out of EntityDescriptorService implementations to allow for plugable implementations to make the determinations
  • Loading branch information
chasegawa committed Nov 1, 2022
1 parent 3d2a6bf commit 61895fe
Show file tree
Hide file tree
Showing 22 changed files with 204 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ 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.opensaml.OpenSamlObjects
import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository
import edu.internet2.tier.shibboleth.admin.ui.security.permission.IShibUiPermissionEvaluator
import edu.internet2.tier.shibboleth.admin.ui.security.permission.PermissionType
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService
import edu.internet2.tier.shibboleth.admin.util.OpenSamlChainingMetadataResolverUtil
import groovy.util.logging.Slf4j
Expand Down Expand Up @@ -79,6 +81,9 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService {
@Autowired
private ShibUIConfiguration shibUIConfiguration

@Autowired
private IShibUiPermissionEvaluator shibUiService;

@Autowired
private UserService userService

Expand Down Expand Up @@ -733,11 +738,13 @@ 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)) {
throw new ForbiddenException("You do not have the permissions necessary to change the enable status of this filter.")
public edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver updateMetadataResolverEnabledStatus(String resourceId, boolean status) throws ForbiddenException, MetadataFileNotFoundException, InitializationException {
edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver updatedResolver = findByResourceId(resourceId);
if (!shibUiService.hasPermission(userService.getCurrentUserAuthentication(), updatedResolver, PermissionType.enable)) {
throw new ForbiddenException("You do not have the permissions necessary to change the enable status of this resolver.")
}

updatedResolver.setEnabled(status);
edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver persistedResolver = metadataResolverRepository.save(updatedResolver)

if (persistedResolver.getDoInitialization()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import edu.internet2.tier.shibboleth.admin.ui.scheduled.MetadataProvidersScheduledTasks;
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.permission.IShibUiPermissionEvaluator;
import edu.internet2.tier.shibboleth.admin.ui.security.permission.ShibUiPermissionDelegate;
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;
Expand Down Expand Up @@ -230,4 +232,10 @@ public UserUpdatedEntityListener userUpdatedEntityListener(OwnershipRepository r
listener.init(repo, groupRepo);
return listener;
}

@Bean
public IShibUiPermissionEvaluator shibUiPermissionEvaluator(EntityDescriptorRepository entityDescriptorRepository, UserService userService) {
// TODO: @jj define type to return for Grouper integration
return new ShibUiPermissionDelegate(entityDescriptorRepository, userService);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,7 @@ public ResponseEntity<?> enableFilter(@PathVariable String metadataResolverId, @
@Transactional
public ResponseEntity<?> enableProvider(@PathVariable String resourceId, @PathVariable String mode) throws PersistentEntityNotFound, ForbiddenException, MetadataFileNotFoundException, InitializationException {
boolean status = "enable".equalsIgnoreCase(mode);
MetadataResolver existingResolver = metadataResolverService.findByResourceId(resourceId);
existingResolver.setEnabled(status);
existingResolver = metadataResolverService.updateMetadataResolverEnabledStatus(existingResolver);

return ResponseEntity.ok(existingResolver);
MetadataResolver metadataResolver = metadataResolverService.updateMetadataResolverEnabledStatus(resourceId, status);
return ResponseEntity.ok(metadataResolver);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,14 @@ public ResponseEntity<?> getAllVersions(@PathVariable String resourceId) throws
return ResponseEntity.ok(versionService.findVersionsForEntityDescriptor(ed.getResourceId()));
}

/**
* @throws ForbiddenException This call is used for the admin needs action list, therefore the user must be an admin
*/
@Secured("ROLE_ADMIN")
@Transactional
@GetMapping(value = "/EntityDescriptor/disabledNonAdmin")
public ResponseEntity<?> getDisabledAndNotOwnedByAdmin() throws ForbiddenException {
return ResponseEntity.ok(entityDescriptorService.getAllDisabledAndNotOwnedByAdmin());
@GetMapping(value = "/EntityDescriptor/disabledSources")
public ResponseEntity<?> getDisabledMetadataSources() throws ForbiddenException {
return ResponseEntity.ok(entityDescriptorService.getDisabledMetadataSources());
}

@GetMapping("/EntityDescriptor/{resourceId}")
Expand All @@ -121,8 +124,7 @@ public ResponseEntity<?> getOneXml(@PathVariable String resourceId) throws Marsh
}

@GetMapping("/EntityDescriptor/{resourceId}/Versions/{versionId}")
public ResponseEntity<?> getSpecificVersion(@PathVariable String resourceId, @PathVariable String versionId) throws
PersistentEntityNotFound, ForbiddenException {
public ResponseEntity<?> getSpecificVersion(@PathVariable String resourceId, @PathVariable String versionId) throws PersistentEntityNotFound, ForbiddenException {
// this "get by resource id" verifies that both the ED exists and the user has proper access, so needs to remain
EntityDescriptor ed = entityDescriptorService.getEntityDescriptorByResourceId(resourceId);
EntityDescriptorRepresentation result = versionService.findSpecificVersionOfEntityDescriptor(ed.getResourceId(), versionId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
@Entity
@EqualsAndHashCode(callSuper = true)
@Audited
public class EntityDescriptor extends AbstractDescriptor implements org.opensaml.saml.saml2.metadata.EntityDescriptor, Ownable, IActivatable {
public class EntityDescriptor extends AbstractDescriptor implements org.opensaml.saml.saml2.metadata.EntityDescriptor, Ownable, IActivatable, IApprovable {
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "entitydesc_addlmetdatlocations_id")
@OrderColumn
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package edu.internet2.tier.shibboleth.admin.ui.domain;

public interface IApprovable {
String getIdOfOwner();
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
package edu.internet2.tier.shibboleth.admin.ui.security.permission;

import edu.internet2.tier.shibboleth.admin.ui.exception.ForbiddenException;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;

import java.util.Collection;
import java.util.Map;

public interface IShibUiPermissionEvaluator extends PermissionEvaluator {
//
// /**
// * For a given permission, find all the persistant entities a user has rights to.
// */
// Collection getPersistentEntitiesWithPermission(Authentication authentication, Object permission);
//
// /**
// * Get ALL persistent entities that user has access to
// * @param authentication
// * @return a map. The key value will be the entity tuple and the value portions will be the set of permissions a user has on those objects
// */
// Map<IPersistentEntityTuple, Object> getPersistentEntities(Authentication authentication);

Collection getPersistentEntities(Authentication authentication, ShibUiType type, PermissionType permissionType);
/**
* Return a Collection of items matching the type describing those types that can be asked for and for which the authenticated
* user has the correct permission to access
* @param authentication The security Authorization
* @param type The permissible type that should be returned in the collection. This is an abstraction
* @param permissionType The type of permissions the user should have to access the items returned in the collection. Determining
* the relationship is up to the implementation
* @return Collection of objects representing the type described by the ShibUiPermissibleType enumeration
* @throws ForbiddenException if the user does not have the correct authority required
*/
Collection getPersistentEntities(Authentication authentication, ShibUiPermissibleType type, PermissionType permissionType) throws ForbiddenException;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package edu.internet2.tier.shibboleth.admin.ui.security.permission;

public enum PermissionType {
admin, enable, approver, user;
admin, approver, enable, fetch, viewOrEdit;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package edu.internet2.tier.shibboleth.admin.ui.security.permission;

public enum ShibUiPermissibleType {
entityDescriptorProjection // represents EntityDescriptorProjections
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package edu.internet2.tier.shibboleth.admin.ui.security.permission;

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.exception.ForbiddenException;
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.Ownable;
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserAccess;
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService;
import lombok.AllArgsConstructor;
import org.springframework.security.core.Authentication;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

/**
* The ShibUiPermissionDelegate is the default service for SHIBUI, which delegates calls (primarily) to the the userService to determine
* whether a user has the correct abilty to act a particular way (possibly on certain objects).
*/
@AllArgsConstructor
public class ShibUiPermissionDelegate implements IShibUiPermissionEvaluator {
private EntityDescriptorRepository entityDescriptorRepository;

private UserService userService;

@Override
public Collection getPersistentEntities(Authentication authentication, ShibUiPermissibleType shibUiType, PermissionType permissionType) throws ForbiddenException {
switch (shibUiType) {
case entityDescriptorProjection:
switch (permissionType) {
case approver:
return getAllEntityDescriptorProjectionsNeedingApprovalBasedOnUserAccess();
case enable:
// This particular list is used for an admin function, so the user must be an ADMIN
if (!hasPermission(authentication, null, PermissionType.admin)) {
throw new ForbiddenException();
}
return entityDescriptorRepository.getEntityDescriptorsNeedingEnabling();
case fetch:
if (!hasPermission(authentication, null, PermissionType.fetch)) {
throw new ForbiddenException("User has no access rights to get a list of Metadata Sources");
}
return getAllEntityDescriptorProjectionsBasedOnUserAccess();
}
}
return null;
}

private List<EntityDescriptorProjection> getAllEntityDescriptorProjectionsBasedOnUserAccess() {
if (userService.currentUserIsAdmin()) {
return entityDescriptorRepository.findAllReturnProjections();
} else {
return entityDescriptorRepository.findAllByIdOfOwner(userService.getCurrentUser().getGroup().getOwnerId());
}
}

private List<EntityDescriptorProjection> getAllEntityDescriptorProjectionsNeedingApprovalBasedOnUserAccess() {
List<String> groupsToApprove = userService.getGroupsCurrentUserCanApprove();
List<EntityDescriptorProjection> result = entityDescriptorRepository.getEntityDescriptorsNeedingApproval(groupsToApprove);
return result;
}

@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
switch ((PermissionType) permission) {
case admin: // we don't care about the object - the user is an admin or not
return userService.currentUserIsAdmin();
case approver:
if (userService.currentUserIsAdmin()) { return true; }
return targetDomainObject instanceof IApprovable ? userService.getGroupsCurrentUserCanApprove().contains(((IApprovable)targetDomainObject).getIdOfOwner()) : false;
case enable:
return targetDomainObject instanceof IActivatable ? userService.currentUserCanEnable((IActivatable) targetDomainObject) : false;
case fetch:
return userService.currentUserIsAdmin() || userService.getCurrentUserAccess().equals(UserAccess.GROUP);
case viewOrEdit:
return userService.canViewOrEditTarget((Ownable) targetDomainObject);
default: return false;
}
}

@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String target, Object permission) {
return false; // Unused and Unimplemented - we don't need for this implementation to lookup objects
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ EntityDescriptorRepresentation createNew(EntityDescriptorRepresentation edRepres
* "admin"
* @throws ForbiddenException - If user is not an ADMIN
*/
Iterable<EntityDescriptorProjection> getAllDisabledAndNotOwnedByAdmin() throws ForbiddenException;
Iterable<EntityDescriptorProjection> getDisabledMetadataSources() throws ForbiddenException;

/**
* @return a list of EntityDescriptorProjections that a user has the rights to access
Expand Down Expand Up @@ -125,5 +125,5 @@ EntityDescriptorRepresentation updateEntityDescriptorEnabledStatus(String resour

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

List<EntityDescriptorProjection> getAllEntityDescriptorProjectionsNeedingApprovalBasedOnUserAccess();
List<EntityDescriptorProjection> getAllEntityDescriptorProjectionsNeedingApprovalBasedOnUserAccess() throws ForbiddenException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ public List<Version> findVersionsForEntityDescriptor(String resourceId) throws P
}

@Override
public EntityDescriptorRepresentation findSpecificVersionOfEntityDescriptor(String resourceId, String versionId) throws
PersistentEntityNotFound {
public EntityDescriptorRepresentation findSpecificVersionOfEntityDescriptor(String resourceId, String versionId) throws PersistentEntityNotFound {
Object edObject = enversVersionServiceSupport.findSpecificVersionOfPersistentEntity(resourceId, versionId, EntityDescriptor.class);
if (edObject == null) {
throw new PersistentEntityNotFound("Unable to find specific version requested - version: " + versionId);
Expand Down
Loading

0 comments on commit 61895fe

Please sign in to comment.