Skip to content

Commit

Permalink
SHIBUI-2059
Browse files Browse the repository at this point in the history
added logic for create bundles
  • Loading branch information
chasegawa committed Sep 11, 2021
1 parent 6790100 commit de71b30
Show file tree
Hide file tree
Showing 13 changed files with 138 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
package edu.internet2.tier.shibboleth.admin.ui.controller;

import edu.internet2.tier.shibboleth.admin.ui.domain.AttributeBundle;
import edu.internet2.tier.shibboleth.admin.ui.exception.ObjectIdExistsException;
import edu.internet2.tier.shibboleth.admin.ui.security.exception.GroupExistsConflictException;
import edu.internet2.tier.shibboleth.admin.ui.security.model.Group;
import edu.internet2.tier.shibboleth.admin.ui.service.AttributeBundleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -21,7 +29,13 @@ public ResponseEntity<?> getAll() {
return ResponseEntity.ok(attributeBundleService.findAll());
}

//POST
@Secured("ROLE_ADMIN")
@PostMapping
@Transactional
public ResponseEntity<?> create(@RequestBody AttributeBundle bundle) throws ObjectIdExistsException {
AttributeBundle result = attributeBundleService.create(bundle);
return ResponseEntity.status(HttpStatus.CREATED).body(result);
}

//DELETE

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package edu.internet2.tier.shibboleth.admin.ui.controller;

import edu.internet2.tier.shibboleth.admin.ui.exception.ObjectIdExistsException;
import org.springframework.http.HttpHeaders;
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;

@ControllerAdvice(assignableTypes = {AttributeBundleController.class})
public class AttributeBundleExceptionHandler {
@ExceptionHandler({ ObjectIdExistsException.class })
public ResponseEntity<?> handleObjectIdExistsException(ObjectIdExistsException e, WebRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.setLocation(EntityDescriptorController.getResourceUriFor(e.getMessage()));
return ResponseEntity.status(HttpStatus.CONFLICT).headers(headers).body(new ErrorResponse(
String.valueOf(HttpStatus.CONFLICT.value()),
String.format("The attribute bundle with resource id [%s] already exists.", e.getMessage())));

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,21 @@

import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor;
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.domain.versioning.Version;
import edu.internet2.tier.shibboleth.admin.ui.exception.EntityIdExistsException;
import edu.internet2.tier.shibboleth.admin.ui.exception.ObjectIdExistsException;
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.model.User;
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService;
import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService;
import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorVersionService;
import lombok.extern.slf4j.Slf4j;

import org.opensaml.core.xml.io.MarshallingException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -39,8 +32,6 @@

import java.net.URI;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api")
Expand Down Expand Up @@ -73,7 +64,8 @@ public EntityDescriptorController(EntityDescriptorVersionService versionService)

@PostMapping("/EntityDescriptor")
@Transactional
public ResponseEntity<?> create(@RequestBody EntityDescriptorRepresentation edRepresentation) throws ForbiddenException, EntityIdExistsException {
public ResponseEntity<?> create(@RequestBody EntityDescriptorRepresentation edRepresentation) throws ForbiddenException,
ObjectIdExistsException {
EntityDescriptorRepresentation persistedEd = entityDescriptorService.createNew(edRepresentation);
return ResponseEntity.created(getResourceUriFor(persistedEd.getId())).body(persistedEd);
}
Expand Down Expand Up @@ -171,4 +163,4 @@ public ResponseEntity<?> upload(@RequestParam String metadataUrl, @RequestParam
.body(String.format("Error fetching XML metadata from the provided URL. Error: %s", e.getMessage()));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

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

Expand All @@ -22,23 +22,23 @@ public ResponseEntity<?> handleConcurrentModificationException(ConcurrentModific
return ResponseEntity.status(HttpStatus.CONFLICT).body(new ErrorResponse(HttpStatus.CONFLICT, e.getMessage()));
}

@ExceptionHandler({ EntityIdExistsException.class })
public ResponseEntity<?> handleEntityExistsException(EntityIdExistsException e, WebRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.setLocation(EntityDescriptorController.getResourceUriFor(e.getMessage()));
return ResponseEntity.status(HttpStatus.CONFLICT).headers(headers).body(new ErrorResponse(
String.valueOf(HttpStatus.CONFLICT.value()),
String.format("The entity descriptor with entity id [%s] already exists.", e.getMessage())));

}

@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(HttpStatus.FORBIDDEN, e.getMessage()));
}
}

@ExceptionHandler({ ObjectIdExistsException.class })
public ResponseEntity<?> handleObjectIdExistsException(ObjectIdExistsException e, WebRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.setLocation(EntityDescriptorController.getResourceUriFor(e.getMessage()));
return ResponseEntity.status(HttpStatus.CONFLICT).headers(headers).body(new ErrorResponse(
String.valueOf(HttpStatus.CONFLICT.value()),
String.format("The entity descriptor with entity id [%s] already exists.", e.getMessage())));

}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.internet2.tier.shibboleth.admin.ui.domain;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import edu.internet2.tier.shibboleth.admin.util.BundleableAttributeTypeValueSerializer;

Expand All @@ -26,9 +27,10 @@ public enum BundleableAttributeType {

public String label() {return label;}

@JsonCreator
public static BundleableAttributeType valueOfLabel(String label) {
for (BundleableAttributeType e : values()) {
if (e.name().equals(label)) {
if (e.label.equals(label)) {
return e;
}
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package edu.internet2.tier.shibboleth.admin.ui.exception;

public class ObjectIdExistsException extends Exception {
public ObjectIdExistsException(String entityId) {
super(entityId);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;
import java.util.Optional;

/**
* Repository to manage {@link edu.internet2.tier.shibboleth.admin.ui.domain.AttributeBundle} instances.
*/
public interface AttributeBundleRepository extends JpaRepository<AttributeBundle, String> {
List<AttributeBundle> findAll();

Optional<AttributeBundle> findByResourceId(String resourceId);

AttributeBundle save(AttributeBundle target);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package edu.internet2.tier.shibboleth.admin.ui.service;

import edu.internet2.tier.shibboleth.admin.ui.domain.AttributeBundle;
import edu.internet2.tier.shibboleth.admin.ui.exception.ObjectIdExistsException;
import edu.internet2.tier.shibboleth.admin.ui.repository.AttributeBundleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
Expand All @@ -12,6 +13,13 @@ public class AttributeBundleService {
@Autowired
AttributeBundleRepository attributeBundleRepository;

public AttributeBundle create(AttributeBundle bundle) throws ObjectIdExistsException {
if (attributeBundleRepository.findByResourceId(bundle.getResourceId()).isPresent()) {
throw new ObjectIdExistsException(bundle.getResourceId());
}
return attributeBundleRepository.save(bundle);
}

public List<AttributeBundle> findAll() {
return attributeBundleRepository.findAll();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import edu.internet2.tier.shibboleth.admin.ui.domain.Attribute;
import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor;
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.exception.EntityIdExistsException;
import edu.internet2.tier.shibboleth.admin.ui.exception.ObjectIdExistsException;
import edu.internet2.tier.shibboleth.admin.ui.exception.EntityNotFoundException;
import edu.internet2.tier.shibboleth.admin.ui.exception.ForbiddenException;

Expand All @@ -29,17 +29,18 @@ public interface EntityDescriptorService {
* @param ed - JPA EntityDescriptor to base creation on
* @return EntityDescriptorRepresentation of the created object
* @throws ForbiddenException If user is unauthorized to perform this operation
* @throws EntityIdExistsException If any EntityDescriptor already exists with the same EntityId
* @throws ObjectIdExistsException If any EntityDescriptor already exists with the same EntityId
*/
EntityDescriptorRepresentation createNew(EntityDescriptor ed) throws ForbiddenException, EntityIdExistsException;
EntityDescriptorRepresentation createNew(EntityDescriptor ed) throws ForbiddenException, ObjectIdExistsException;

/**
* @param edRepresentation Incoming representation to save
* @return EntityDescriptorRepresentation
* @throws ForbiddenException If user is unauthorized to perform this operation
* @throws EntityIdExistsException If the entity already exists
* @throws ObjectIdExistsException If the entity already exists
*/
EntityDescriptorRepresentation createNew(EntityDescriptorRepresentation edRepresentation) throws ForbiddenException, EntityIdExistsException;
EntityDescriptorRepresentation createNew(EntityDescriptorRepresentation edRepresentation) throws ForbiddenException,
ObjectIdExistsException;

/**
* Map from opensaml implementation of entity descriptor model to front-end data representation of entity descriptor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import edu.internet2.tier.shibboleth.admin.ui.domain.*;
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.*;
import edu.internet2.tier.shibboleth.admin.ui.exception.EntityIdExistsException;
import edu.internet2.tier.shibboleth.admin.ui.exception.ObjectIdExistsException;
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;
Expand Down Expand Up @@ -66,18 +66,19 @@ public EntityDescriptor createDescriptorFromRepresentation(final EntityDescripto
}

@Override
public EntityDescriptorRepresentation createNew(EntityDescriptor ed) throws ForbiddenException, EntityIdExistsException {
public EntityDescriptorRepresentation createNew(EntityDescriptor ed) throws ForbiddenException, ObjectIdExistsException {
return createNew(createRepresentationFromDescriptor(ed));
}

@Override
public EntityDescriptorRepresentation createNew(EntityDescriptorRepresentation edRep) throws ForbiddenException, EntityIdExistsException {
public EntityDescriptorRepresentation createNew(EntityDescriptorRepresentation edRep) throws ForbiddenException,
ObjectIdExistsException {
if (edRep.isServiceEnabled() && !userService.currentUserIsAdmin()) {
throw new ForbiddenException("You do not have the permissions necessary to enable this service.");
}

if (entityDescriptorRepository.findByEntityID(edRep.getEntityId()) != null) {
throw new EntityIdExistsException(edRep.getEntityId());
throw new ObjectIdExistsException(edRep.getEntityId());
}

EntityDescriptor ed = (EntityDescriptor) createDescriptorFromRepresentation(edRep);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.MapperFeature
import com.fasterxml.jackson.databind.ObjectMapper
import edu.internet2.tier.shibboleth.admin.ui.configuration.ShibUIConfiguration
import edu.internet2.tier.shibboleth.admin.ui.domain.AttributeBundle
import edu.internet2.tier.shibboleth.admin.ui.exception.ObjectIdExistsException
import edu.internet2.tier.shibboleth.admin.ui.repository.AttributeBundleRepository
import edu.internet2.tier.shibboleth.admin.ui.service.AttributeBundleService
import org.springframework.beans.factory.annotation.Autowired
Expand All @@ -15,16 +16,20 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.web.servlet.setup.MockMvcBuilders
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.util.NestedServletException
import spock.lang.Specification

import static org.hamcrest.CoreMatchers.containsString
import static org.hamcrest.Matchers.containsInAnyOrder
import static org.springframework.http.MediaType.APPLICATION_JSON
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status

@DataJpaTest
@DataJpaTest(properties = ["spring.jackson.mapper.accept-case-insensitive-enums=true"])
@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"])
@EntityScan("edu.internet2.tier.shibboleth.admin.ui")
@ContextConfiguration(classes = [ShibUIConfiguration, ABCTConfig])
Expand Down Expand Up @@ -81,7 +86,48 @@ class AttributeBundleControllerTests extends Specification {
.andExpect(jsonPath("\$.[0].attributes", containsInAnyOrder("eduPersonPrincipalName", "surname", "givenName")))
}

// can go away with merge to develop...
def "CREATE checks" () {
expect:
attributeBundleRepository.findAll().isEmpty()

when: "add a bundle"
def json = """
{
"name": "bundleName",
"resourceId": "randomIDVal",
"attributes": ["eduPersonPrincipalName", "surname", "givenName"]
}
"""
AttributeBundle bundle = objectMapper.readValue(json, AttributeBundle.class)
attributeBundleRepository.saveAndFlush(bundle)

then: "bundle already exists"
try {
mockMvc.perform(post('/api/custom/entity/bundles').contentType(APPLICATION_JSON).content(json))
false
} catch (NestedServletException expected) {
expected.getCause() instanceof ObjectIdExistsException
}

when: "new bundle"
json = """
{
"name": "bundle2",
"resourceId": "differentResourceId",
"attributes": ["eduPersonPrincipalName", "surname", "givenName"]
}
"""

def result = mockMvc.perform(post('/api/custom/entity/bundles').contentType(APPLICATION_JSON).content(json))
then:
result.andExpect(status().isCreated())
.andExpect(content().contentType(APPLICATION_JSON))
.andExpect(jsonPath("\$.name").value("bundle2"))
.andExpect(jsonPath("\$.resourceId").value("differentResourceId"))
.andExpect(jsonPath("\$.attributes", containsInAnyOrder("eduPersonPrincipalName", "surname", "givenName")))
}

// can go away with merge to develop and this extends the base test class
@TestConfiguration
private static class ABCTConfig {
@Bean
Expand Down
Loading

0 comments on commit de71b30

Please sign in to comment.