From 2795f70a7b5c2f14f3acc6ecebb0bfd69c64d765 Mon Sep 17 00:00:00 2001 From: Bill Smith Date: Mon, 21 Jan 2019 15:11:38 -0700 Subject: [PATCH] [SHIBUI-1058] Updated unit tests to make use of createdBy. --- .../EntityDescriptorController.java | 3 +- .../EntityDescriptorRepresentation.java | 15 +- .../JPAEntityDescriptorServiceImpl.java | 1 + .../EntityDescriptorControllerTests.groovy | 243 +++++++++++++++++- 4 files changed, 243 insertions(+), 19 deletions(-) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorController.java index 11f5b4877..23d89849c 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorController.java @@ -176,8 +176,7 @@ public ResponseEntity getOneXml(@PathVariable String resourceId) throws Marsh final String xml = this.openSamlObjects.marshalToXmlString(ed); return ResponseEntity.ok(xml); } else { - return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ErrorResponse(HttpStatus.FORBIDDEN, - "You are not authorized to perform the requested operation.")); + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); } } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/EntityDescriptorRepresentation.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/EntityDescriptorRepresentation.java index b879e76d5..e378f5fbb 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/EntityDescriptorRepresentation.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/frontend/EntityDescriptorRepresentation.java @@ -9,9 +9,6 @@ public class EntityDescriptorRepresentation implements Serializable { - - private int version; - public EntityDescriptorRepresentation() { } @@ -63,6 +60,10 @@ public EntityDescriptorRepresentation(String id, private List attributeRelease; + private int version; + + private String createdBy; + public String getId() { return id; } @@ -204,4 +205,12 @@ public int getVersion() { public void setVersion(int version) { this.version = version; } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java index adb8e4cb8..231ac7ab3 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java @@ -357,6 +357,7 @@ public EntityDescriptorRepresentation createRepresentationFromDescriptor(org.ope representation.setCreatedDate(ed.getCreatedDate()); representation.setModifiedDate(ed.getModifiedDate()); representation.setVersion(ed.hashCode()); + representation.setCreatedBy(ed.getCreatedBy()); if (ed.getSPSSODescriptor("") != null && ed.getSPSSODescriptor("").getSupportedProtocols().size() > 0) { ServiceProviderSsoDescriptorRepresentation serviceProviderSsoDescriptorRepresentation = representation.getServiceProviderSsoDescriptor(true); diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerTests.groovy index faa187465..5396477d9 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerTests.groovy @@ -24,6 +24,7 @@ import org.springframework.security.core.context.SecurityContext import org.springframework.security.core.context.SecurityContextHolder import org.springframework.security.web.context.HttpSessionSecurityContextRepository import org.springframework.test.context.ContextConfiguration +import org.springframework.test.web.servlet.result.MockMvcResultHandlers import org.springframework.test.web.servlet.setup.MockMvcBuilders import org.springframework.web.client.RestTemplate import spock.lang.Specification @@ -85,7 +86,7 @@ class EntityDescriptorControllerTests extends Specification { securityContext.getAuthentication() >> authentication } - def 'GET /EntityDescriptors with empty repository'() { + def 'GET /EntityDescriptors with empty repository as admin'() { given: prepareAdminUser() def emptyRecordsFromRepository = [].stream() @@ -105,7 +106,7 @@ class EntityDescriptorControllerTests extends Specification { } - def 'GET /EntityDescriptors with 1 record in repository'() { + def 'GET /EntityDescriptors with 1 record in repository as admin'() { given: prepareAdminUser() def expectedCreationDate = '2017-10-23T11:11:11' @@ -131,7 +132,8 @@ class EntityDescriptorControllerTests extends Specification { "assertionConsumerServices": null, "relyingPartyOverrides": null, "attributeRelease": null, - "version": $version + "version": $version, + "createdBy": null } ] """ @@ -151,7 +153,7 @@ class EntityDescriptorControllerTests extends Specification { } - def 'GET /EntityDescriptors with 2 records in repository'() { + def 'GET /EntityDescriptors with 2 records in repository as admin'() { given: prepareAdminUser() def expectedCreationDate = '2017-10-23T11:11:11' @@ -182,7 +184,8 @@ class EntityDescriptorControllerTests extends Specification { "assertionConsumerServices": null, "relyingPartyOverrides": null, "attributeRelease": null, - "version": $versionOne + "version": $versionOne, + "createdBy": null }, { "id": "uuid-2", @@ -200,7 +203,8 @@ class EntityDescriptorControllerTests extends Specification { "assertionConsumerServices": null, "relyingPartyOverrides": null, "attributeRelease": null, - "version": $versionTwo + "version": $versionTwo, + "createdBy": null } ] """ @@ -220,6 +224,54 @@ class EntityDescriptorControllerTests extends Specification { } + def 'GET /EntityDescriptors with 1 record in repository as user returns only that user\'s records'() { + given: + prepareUser('someUser', 'ROLE_USER') + def expectedCreationDate = '2017-10-23T11:11:11' + def entityDescriptorOne = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', + serviceEnabled: true, + createdDate: LocalDateTime.parse(expectedCreationDate), + createdBy: 'someUser') + def versionOne = entityDescriptorOne.hashCode() + def oneRecordFromRepository = [entityDescriptorOne].stream() + def expectedOneRecordListResponseBody = """ + [ + { + "id": "uuid-1", + "serviceProviderName": "sp1", + "entityId": "eid1", + "serviceEnabled": true, + "createdDate": "$expectedCreationDate", + "modifiedDate": null, + "organization": null, + "contacts": null, + "mdui": null, + "serviceProviderSsoDescriptor": null, + "logoutEndpoints": null, + "securityInfo": null, + "assertionConsumerServices": null, + "relyingPartyOverrides": null, + "attributeRelease": null, + "version": $versionOne, + "createdBy": "someUser" + } + ] + """ + + def expectedResponseContentType = APPLICATION_JSON_UTF8 + def expectedHttpResponseStatus = status().isOk() + + when: + def result = mockMvc.perform(get('/api/EntityDescriptors')) + + then: + //One call to the repo expected + 1 * entityDescriptorRepository.findAllByCreatedBy('someUser') >> oneRecordFromRepository + result.andExpect(expectedHttpResponseStatus) + .andExpect(content().contentType(expectedResponseContentType)) + .andExpect(content().json(expectedOneRecordListResponseBody, true)) + } + def 'POST /EntityDescriptor and successfully create new record'() { given: def expectedCreationDate = '2017-10-23T11:11:11' @@ -271,7 +323,8 @@ class EntityDescriptorControllerTests extends Specification { "assertionConsumerServices": null, "relyingPartyOverrides": null, "attributeRelease": null, - "version": $version + "version": $version, + "createdBy": null } """ @@ -377,7 +430,57 @@ class EntityDescriptorControllerTests extends Specification { "assertionConsumerServices": null, "relyingPartyOverrides": null, "attributeRelease": null, - "version": $version + "version": $version, + "createdBy": null + } + """ + + when: + def result = mockMvc.perform(get("/api/EntityDescriptor/$providedResourceId")) + + then: + //EntityDescriptor found + 1 * entityDescriptorRepository.findByResourceId(providedResourceId) >> entityDescriptor + + + result.andExpect(status().isOk()) + .andExpect(content().json(expectedJsonBody, true)) + } + + def 'GET /EntityDescriptor/{resourceId} existing, owned by non-admin'() { + given: + prepareUser('someUser', 'ROLE_USER') + def expectedCreationDate = '2017-10-23T11:11:11' + def providedResourceId = 'uuid-1' + def expectedSpName = 'sp1' + def expectedEntityId = 'eid1' + + def entityDescriptor = new EntityDescriptor(resourceId: providedResourceId, entityID: expectedEntityId, serviceProviderName: expectedSpName, + serviceEnabled: true, + createdDate: LocalDateTime.parse(expectedCreationDate), + createdBy: 'someUser') + def version = entityDescriptor.hashCode() + + def expectedJsonBody = """ + { + "id": "${providedResourceId}", + "serviceProviderName": "$expectedSpName", + "entityId": "$expectedEntityId", + "organization": null, + "serviceEnabled": true, + "createdDate": "$expectedCreationDate", + "modifiedDate": null, + "organization": null, + "contacts": null, + "mdui": null, + "serviceProviderSsoDescriptor": null, + "logoutEndpoints": null, + "securityInfo": null, + "assertionConsumerServices": null, + "relyingPartyOverrides": null, + "attributeRelease": null, + "version": $version, + "createdBy": "someUser" } """ @@ -393,6 +496,29 @@ class EntityDescriptorControllerTests extends Specification { .andExpect(content().json(expectedJsonBody, true)) } + def 'GET /EntityDescriptor/{resourceId} existing, owned by some other user'() { + given: + prepareUser('someUser', 'ROLE_USER') + def expectedCreationDate = '2017-10-23T11:11:11' + def providedResourceId = 'uuid-1' + def expectedSpName = 'sp1' + def expectedEntityId = 'eid1' + + def entityDescriptor = new EntityDescriptor(resourceId: providedResourceId, entityID: expectedEntityId, serviceProviderName: expectedSpName, + serviceEnabled: true, + createdDate: LocalDateTime.parse(expectedCreationDate), + createdBy: 'someOtherUser') + + when: + def result = mockMvc.perform(get("/api/EntityDescriptor/$providedResourceId")) + + then: + //EntityDescriptor found + 1 * entityDescriptorRepository.findByResourceId(providedResourceId) >> entityDescriptor + + result.andExpect(status().is(403)) + } + def 'GET /EntityDescriptor/{resourceId} existing (xml)'() { given: prepareAdminUser() @@ -425,6 +551,66 @@ class EntityDescriptorControllerTests extends Specification { .andExpect(content().xml(expectedXML)) } + def 'GET /EntityDescriptor/{resourceId} existing (xml), user-owned'() { + given: + prepareUser('someUser', 'ROLE_USER') + def expectedCreationDate = '2017-10-23T11:11:11' + def providedResourceId = 'uuid-1' + def expectedSpName = 'sp1' + def expectedEntityId = 'eid1' + + def entityDescriptor = new EntityDescriptor(resourceId: providedResourceId, entityID: expectedEntityId, serviceProviderName: expectedSpName, + serviceEnabled: true, + createdDate: LocalDateTime.parse(expectedCreationDate), + createdBy: 'someUser') + entityDescriptor.setElementLocalName("EntityDescriptor") + entityDescriptor.setNamespacePrefix("md") + entityDescriptor.setNamespaceURI("urn:oasis:names:tc:SAML:2.0:metadata") + + def expectedXML = """ +""" + + when: + def result = mockMvc.perform(get("/api/EntityDescriptor/$providedResourceId") + .accept(APPLICATION_XML)) + + then: + //EntityDescriptor found + 1 * entityDescriptorRepository.findByResourceId(providedResourceId) >> entityDescriptor + + + result.andExpect(status().isOk()) + .andExpect(content().xml(expectedXML)) + } + + def 'GET /EntityDescriptor/{resourceId} existing (xml), other user-owned'() { + given: + prepareUser('someUser', 'ROLE_USER') + def expectedCreationDate = '2017-10-23T11:11:11' + def providedResourceId = 'uuid-1' + def expectedSpName = 'sp1' + def expectedEntityId = 'eid1' + + def entityDescriptor = new EntityDescriptor(resourceId: providedResourceId, entityID: expectedEntityId, serviceProviderName: expectedSpName, + serviceEnabled: true, + createdDate: LocalDateTime.parse(expectedCreationDate), + createdBy: 'someOtherUser') + entityDescriptor.setElementLocalName("EntityDescriptor") + entityDescriptor.setNamespacePrefix("md") + entityDescriptor.setNamespaceURI("urn:oasis:names:tc:SAML:2.0:metadata") + + when: + def result = mockMvc.perform(get("/api/EntityDescriptor/$providedResourceId") + .accept(APPLICATION_XML)) + + then: + //EntityDescriptor found + 1 * entityDescriptorRepository.findByResourceId(providedResourceId) >> entityDescriptor + + result.andExpect(status().is(403)) + } + def "POST /EntityDescriptor handles XML happily"() { given: prepareAdminUser() @@ -485,7 +671,8 @@ class EntityDescriptorControllerTests extends Specification { "attributeRelease": [ "givenName", "employeeNumber" - ] + ], + "createdBy": null } """ @@ -604,7 +791,8 @@ class EntityDescriptorControllerTests extends Specification { "attributeRelease": [ "givenName", "employeeNumber" - ] + ], + "createdBy": null } """ @@ -620,7 +808,7 @@ class EntityDescriptorControllerTests extends Specification { .andExpect(content().json(expectedJson, true)) } - def "PUT /EntityDescriptor updates entity descriptors properly"() { + def "PUT /EntityDescriptor updates entity descriptors properly as admin"() { given: prepareAdminUser() def entityDescriptor = generator.buildEntityDescriptor() @@ -648,6 +836,29 @@ class EntityDescriptorControllerTests extends Specification { .andExpect(content().json(JsonOutput.toJson(expectedJson), true)) } + def "PUT /EntityDescriptor denies the request if the PUTing user is not an ADMIN and not the createdBy user"() { + given: + prepareUser('randomUser', 'ROLE_USER') + def entityDescriptor = generator.buildEntityDescriptor() + entityDescriptor.createdBy = 'someoneElse' + def updatedEntityDescriptor = generator.buildEntityDescriptor() + updatedEntityDescriptor.createdBy = 'someoneElse' + updatedEntityDescriptor.resourceId = entityDescriptor.resourceId + def updatedEntityDescriptorRepresentation = service.createRepresentationFromDescriptor(updatedEntityDescriptor) + def postedJsonBody = mapper.writeValueAsString(updatedEntityDescriptorRepresentation) + def resourceId = entityDescriptor.resourceId + 1 * entityDescriptorRepository.findByResourceId(resourceId) >> entityDescriptor + + when: + def result = mockMvc.perform( + put("/api/EntityDescriptor/$resourceId") + .contentType(APPLICATION_JSON_UTF8) + .content(postedJsonBody)) + + then: + result.andExpect(status().is(403)) + } + def "PUT /EntityDescriptor 409's if the version numbers don't match"() { given: prepareAdminUser() @@ -672,10 +883,14 @@ class EntityDescriptorControllerTests extends Specification { } def prepareAdminUser() { - authentication.getPrincipal() >> "foo" + prepareUser('foo', 'ROLE_ADMIN') + } + + def prepareUser(String username, String rolename) { + authentication.getPrincipal() >> username SecurityContextHolder.setContext(securityContext) - def user = new User(username: "foo", role: "ROLE_ADMIN") + def user = new User(username: username, role: rolename) Optional currentUser = Optional.of(user) - userRepository.findByUsername("foo") >> currentUser + userRepository.findByUsername(username) >> currentUser } }