Skip to content

Commit

Permalink
SHIBUI-2001
Browse files Browse the repository at this point in the history
mid-work save
  • Loading branch information
chasegawa committed Jul 29, 2021
1 parent 066318b commit 7f4eca8
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -1,23 +1,46 @@
package edu.internet2.tier.shibboleth.admin.ui.controller;

import javax.script.ScriptException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService;
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter;
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.exception.EntityNotFoundException;
import edu.internet2.tier.shibboleth.admin.ui.exception.ForbiddenException;
import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService;
import edu.internet2.tier.shibboleth.admin.ui.service.FilterService;

@RestController
@RequestMapping("/api/activate")
public class ActivateController {

@Autowired
private UserService userService;
private EntityDescriptorService entityDescriptorService;

@Autowired
private EntityDescriptorRepository entityDescriptorRepo;


private FilterService filterService;

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

// Enable/disable for : entity descriptor, provider, filter
@PatchMapping(path = "/MetadataResolvers/{metadataResolverId}/Filter/{resourceId}/{mode}")
@Transactional
public ResponseEntity<?> enableFilter(@PathVariable String metadataResolverId, @PathVariable String resourceId, @PathVariable String mode) throws EntityNotFoundException, ForbiddenException, ScriptException {
boolean status = "enable".equalsIgnoreCase(mode);
MetadataFilter persistedFilter = filterService.updateFilterEnabledStatus(metadataResolverId, resourceId, status);
return ResponseEntity.ok(persistedFilter);
}
// Enable/disable for : , provider
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package edu.internet2.tier.shibboleth.admin.ui.controller;

import javax.script.ScriptException;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import edu.internet2.tier.shibboleth.admin.ui.exception.EntityNotFoundException;
import edu.internet2.tier.shibboleth.admin.ui.exception.ForbiddenException;

@ControllerAdvice(assignableTypes = {ActivateController.class})
public class ActivateExceptionHandler extends ResponseEntityExceptionHandler {

@ExceptionHandler({ EntityNotFoundException.class })
public ResponseEntity<?> handleEntityNotFoundException(EntityNotFoundException e, WebRequest request) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ErrorResponse(HttpStatus.NOT_FOUND, e.getMessage()));
}

@ExceptionHandler({ ForbiddenException.class })
public ResponseEntity<?> handleForbiddenAccess(ForbiddenException e, WebRequest request) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ErrorResponse(String.valueOf(HttpStatus.FORBIDDEN.value()), e.getMessage()));
}

@ExceptionHandler({ ScriptException.class })
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()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package edu.internet2.tier.shibboleth.admin.ui.exception;

public class ForbiddenException extends Exception {
public ForbiddenException() {
super("You are not authorized to perform the requested operation.");
}

public ForbiddenException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package edu.internet2.tier.shibboleth.admin.ui.security.service;

import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.springframework.security.core.context.SecurityContextHolder;

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.RoleRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.core.context.SecurityContextHolder;

import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

/**
* @author Bill Smith (wsmith@unicon.net)
Expand Down Expand Up @@ -70,4 +72,12 @@ public Set<String> getUserRoles(String username) {
}
return result;
}

/**
* Current logic is pretty dumb, this will need to change/expand once a user can have more than one role.
*/
public boolean currentUserHasExpectedRole(List<String> acceptedRoles) {
User user = getCurrentUser();
return acceptedRoles.contains(user.getRole());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import edu.internet2.tier.shibboleth.admin.ui.domain.Attribute;
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.exception.EntityNotFoundException;
import edu.internet2.tier.shibboleth.admin.ui.exception.ForbiddenException;

import org.opensaml.saml.saml2.metadata.EntityDescriptor;

import java.util.List;
Expand Down Expand Up @@ -54,4 +57,6 @@ public interface EntityDescriptorService {
*/
Map<String, Object> getRelyingPartyOverridesRepresentationFromAttributeList(List<Attribute> attributeList);

EntityDescriptorRepresentation updateEntityDescriptorEnabledStatus(String resourceId, boolean status) throws EntityNotFoundException, ForbiddenException;

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package edu.internet2.tier.shibboleth.admin.ui.service;

import javax.script.ScriptException;

import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter;
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter;
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.FilterRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.exception.EntityNotFoundException;
import edu.internet2.tier.shibboleth.admin.ui.exception.ForbiddenException;

/**
* Main backend facade API that defines operations pertaining to manipulating <code>{@link EntityAttributesFilter}</code> objects.
Expand All @@ -25,4 +30,6 @@ public interface FilterService {
* @return FilterRepresentation front end representation
*/
FilterRepresentation createRepresentationFromFilter(final EntityAttributesFilter entityAttributesFilter);

MetadataFilter updateFilterEnabledStatus(String metadataResolverId, String resourceId, boolean status) throws EntityNotFoundException, ForbiddenException, ScriptException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.OrganizationRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.SecurityInfoRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.ServiceProviderSsoDescriptorRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.exception.EntityNotFoundException;
import edu.internet2.tier.shibboleth.admin.ui.exception.ForbiddenException;
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.security.service.UserService;
import edu.internet2.tier.shibboleth.admin.util.MDDCConstants;
import edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions;
Expand Down Expand Up @@ -76,8 +79,8 @@
* @since 1.0
*/
public class JPAEntityDescriptorServiceImpl implements EntityDescriptorService {

private static final Logger LOGGER = LoggerFactory.getLogger(JPAEntityDescriptorServiceImpl.class);
@Autowired
private EntityDescriptorRepository entityDescriptorRepository;

@Autowired
private OpenSamlObjects openSamlObjects;
Expand Down Expand Up @@ -709,4 +712,19 @@ public List<String> getAttributeReleaseListFromAttributeList(List<Attribute> att
public Map<String, Object> getRelyingPartyOverridesRepresentationFromAttributeList(List<Attribute> attributeList) {
return ModelRepresentationConversions.getRelyingPartyOverridesRepresentationFromAttributeList(attributeList);
}

@Override
public EntityDescriptorRepresentation updateEntityDescriptorEnabledStatus(String resourceId, boolean status) throws EntityNotFoundException, ForbiddenException {
EntityDescriptor ed = entityDescriptorRepository.findByResourceId(resourceId);
if (ed == null) {
throw new EntityNotFoundException("Entity with resourceid[" + resourceId + "] was not found for update");
}
// @TODO: when merged with groups, this should maybe be merged with group check as they have to have the role in the right group
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);
ed = entityDescriptorRepository.save(ed);
return createRepresentationFromDescriptor(ed);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
package edu.internet2.tier.shibboleth.admin.ui.service;

import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter;
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter;
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.FilterRepresentation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver;
import edu.internet2.tier.shibboleth.admin.ui.exception.EntityNotFoundException;
import edu.internet2.tier.shibboleth.admin.ui.exception.ForbiddenException;
import edu.internet2.tier.shibboleth.admin.ui.repository.FilterRepository;
import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import javax.script.ScriptException;

/**
* Default implementation of {@link FilterService}
Expand All @@ -16,18 +27,27 @@
* @author Bill Smith (wsmith@unicon.net)
*/
public class JPAFilterServiceImpl implements FilterService {

private static final Logger LOGGER = LoggerFactory.getLogger(JPAFilterServiceImpl.class);

@Autowired
EntityDescriptorService entityDescriptorService;

@Autowired
EntityService entityService;

@Autowired
FilterRepository filterRepository;

@Autowired
FilterTargetService filterTargetService;

@Autowired
private MetadataResolverRepository metadataResolverRepository;

@Autowired
private MetadataResolverService metadataResolverService;

@Autowired
private UserService userService;

@Override
public EntityAttributesFilter createFilterFromRepresentation(FilterRepresentation representation) {
//TODO? use OpenSamlObjects.buildDefaultInstanceOfType(EntityAttributesFilter.class)?
Expand Down Expand Up @@ -66,4 +86,52 @@ public FilterRepresentation createRepresentationFromFilter(EntityAttributesFilte
representation.setVersion(entityAttributesFilter.hashCode());
return representation;
}

private void reloadFiltersAndHandleScriptException(String resolverResourceId) throws ScriptException {
try {
metadataResolverService.reloadFilters(resolverResourceId);
} catch (Throwable ex) {
//explicitly mark transaction for rollback when we get ScriptException as we call reloadFilters
//after persistence call. Then re-throw the exception with pertinent message
if (ex instanceof ScriptException) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
throw new ScriptException("Caught invalid script parsing error when reloading filters. Please fix the script data");
}
}
}

/**
* Logic taken directly from the MetadataFiltersController and then modified slightly.
*/
@Override
public MetadataFilter updateFilterEnabledStatus(String metadataResolverId, String resourceId, boolean status)
throws EntityNotFoundException, ForbiddenException, ScriptException {

MetadataResolver metadataResolver = metadataResolverRepository.findByResourceId(metadataResolverId);
// Now we operate directly on the filter attached to MetadataResolver,
// Instead of fetching filter separately, to accommodate correct envers versioning with uni-directional one-to-many
Optional<MetadataFilter> filterTobeUpdatedOptional = metadataResolver.getMetadataFilters().stream()
.filter(it -> it.getResourceId().equals(resourceId)).findFirst();
if (filterTobeUpdatedOptional.isEmpty()) {
throw new EntityNotFoundException("Filter with resource id[" + resourceId + "] not found");
}

// @TODO: when merged with groups, this should maybe be merged with group check as they have to have the role in the right group
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 filter.");
}

MetadataFilter filterTobeUpdated = filterTobeUpdatedOptional.get();
filterTobeUpdated.setFilterEnabled(status);
MetadataFilter persistedFilter = filterRepository.save(filterTobeUpdated);

// To support envers versioning from MetadataResolver side
metadataResolver.markAsModified();
metadataResolverRepository.save(metadataResolver);

// TODO: do we need to reload filters here?
reloadFiltersAndHandleScriptException(metadataResolver.getResourceId());

return persistedFilter;
}
}

0 comments on commit 7f4eca8

Please sign in to comment.