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 914ae8d1b..f952b2a4d 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 @@ -9,9 +9,9 @@ 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.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.HttpHeaders; @@ -38,38 +38,39 @@ @RestController @RequestMapping("/api") +@Slf4j public class EntityDescriptorController { + private static URI getResourceUriFor(EntityDescriptor ed) { + return ServletUriComponentsBuilder + .fromCurrentServletMapping().path("/api/EntityDescriptor") + .pathSegment(ed.getResourceId()) + .build() + .toUri(); + } @Autowired private EntityDescriptorRepository entityDescriptorRepository; @Autowired - private OpenSamlObjects openSamlObjects; + private EntityDescriptorService entityDescriptorService; @Autowired - private EntityDescriptorService entityDescriptorService; + private OpenSamlObjects openSamlObjects; + + private RestTemplate restTemplate; @Autowired RestTemplateBuilder restTemplateBuilder; private UserService userService; - private RestTemplate restTemplate; - private EntityDescriptorVersionService versionService; - private static Logger LOGGER = LoggerFactory.getLogger(EntityDescriptorController.class); - public EntityDescriptorController(UserService userService, EntityDescriptorVersionService versionService) { this.userService = userService; this.versionService = versionService; } - @PostConstruct - public void initRestTemplate() { - this.restTemplate = restTemplateBuilder.build(); - } - @PostMapping("/EntityDescriptor") @Transactional public ResponseEntity create(@RequestBody EntityDescriptorRepresentation edRepresentation) { @@ -93,57 +94,44 @@ public ResponseEntity create(@RequestBody EntityDescriptorRepresentation edRe return ResponseEntity.created(getResourceUriFor(persistedEd)).body(entityDescriptorService.createRepresentationFromDescriptor(persistedEd)); } - @PostMapping(value = "/EntityDescriptor", consumes = "application/xml") + @Secured("ROLE_ADMIN") + @DeleteMapping(value = "/EntityDescriptor/{resourceId}") @Transactional - public ResponseEntity upload(@RequestBody byte[] entityDescriptorXml, @RequestParam String spName) throws Exception { - return handleUploadingEntityDescriptorXml(entityDescriptorXml, spName); + public ResponseEntity deleteOne(@PathVariable String resourceId) { + EntityDescriptor ed = entityDescriptorRepository.findByResourceId(resourceId); + if (ed == null) { + return ResponseEntity.notFound().build(); + } else if (ed.isServiceEnabled()) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ErrorResponse(HttpStatus.FORBIDDEN, "Deleting an enabled Metadata Source is not allowed. Disable the source and try again.")); + } else { + entityDescriptorRepository.delete(ed); + return ResponseEntity.noContent().build(); + } } - @PostMapping(value = "/EntityDescriptor", consumes = "application/x-www-form-urlencoded") - @Transactional - public ResponseEntity upload(@RequestParam String metadataUrl, @RequestParam String spName) throws Exception { - try { - byte[] xmlContents = this.restTemplate.getForObject(metadataUrl, byte[].class); - return handleUploadingEntityDescriptorXml(xmlContents, spName); - } catch (Throwable e) { - LOGGER.error("Error fetching XML metadata from the provided URL: [{}]. The error is: {}", metadataUrl, e); - LOGGER.error(e.getMessage(), e); - return ResponseEntity - .badRequest() - .body(String.format("Error fetching XML metadata from the provided URL. Error: %s", e.getMessage())); + private ResponseEntity entityDescriptorEnablePermissionsCheck(boolean serviceEnabled) { + User user = userService.getCurrentUser(); + if (user != null) { + if (serviceEnabled && !user.getRole().equals("ROLE_ADMIN")) { + return ResponseEntity.status(HttpStatus.FORBIDDEN) + .body(new ErrorResponse(HttpStatus.FORBIDDEN, "You do not have the permissions necessary to enable this service.")); + } } + return null; } - @PutMapping("/EntityDescriptor/{resourceId}") - @Transactional - public ResponseEntity update(@RequestBody EntityDescriptorRepresentation edRepresentation, @PathVariable String resourceId) { - User currentUser = userService.getCurrentUser(); - EntityDescriptor existingEd = entityDescriptorRepository.findByResourceId(resourceId); - if (existingEd == null) { - return ResponseEntity.notFound().build(); - } else { - if (currentUser != null && (currentUser.getRole().equals("ROLE_ADMIN") || currentUser.getUsername().equals(existingEd.getCreatedBy()))) { - if (!existingEd.isServiceEnabled()) { - ResponseEntity entityDescriptorEnablingDeniedResponse = entityDescriptorEnablePermissionsCheck(edRepresentation.isServiceEnabled()); - if (entityDescriptorEnablingDeniedResponse != null) { - return entityDescriptorEnablingDeniedResponse; - } - } - - // Verify we're the only one attempting to update the EntityDescriptor - if (edRepresentation.getVersion() != existingEd.hashCode()) { - return new ResponseEntity(HttpStatus.CONFLICT); - } - - entityDescriptorService.updateDescriptorFromRepresentation(existingEd, edRepresentation); - existingEd = entityDescriptorRepository.save(existingEd); - - return ResponseEntity.ok().body(entityDescriptorService.createRepresentationFromDescriptor(existingEd)); - } else { - return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ErrorResponse(HttpStatus.FORBIDDEN, - "You are not authorized to perform the requested operation.")); - } + private ResponseEntity existingEntityDescriptorCheck(String entityId) { + final EntityDescriptor ed = entityDescriptorRepository.findByEntityID(entityId); + if (ed != null) { + HttpHeaders headers = new HttpHeaders(); + headers.setLocation(getResourceUriFor(ed)); + 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.", entityId))); } + //No existing entity descriptor, which is an OK condition indicated by returning a null conflict response + return null; } @GetMapping("/EntityDescriptors") @@ -151,56 +139,31 @@ public ResponseEntity update(@RequestBody EntityDescriptorRepresentation edRe public ResponseEntity getAll() { User currentUser = userService.getCurrentUser(); if (currentUser != null) { - if (currentUser.getRole().equals("ROLE_ADMIN")) { - return ResponseEntity.ok(entityDescriptorRepository.findAllStreamByCustomQuery() - .map(ed -> entityDescriptorService.createRepresentationFromDescriptor(ed)) - .collect(Collectors.toList())); - } else { - return ResponseEntity.ok(entityDescriptorRepository.findAllStreamByCreatedBy(currentUser.getUsername()) - .map(ed -> entityDescriptorService.createRepresentationFromDescriptor(ed)) - .collect(Collectors.toList())); - } + return ResponseEntity.ok(entityDescriptorService.getAllRepresentationsBasedOnUserAccess()); } else { return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ErrorResponse(HttpStatus.FORBIDDEN, "You are not authorized to perform the requested operation.")); } } - @GetMapping("/EntityDescriptor/{resourceId}") + @GetMapping("/EntityDescriptor/{resourceId}/Versions") @Transactional(readOnly = true) - public ResponseEntity getOne(@PathVariable String resourceId) { - User currentUser = userService.getCurrentUser(); + public ResponseEntity getAllVersions(@PathVariable String resourceId) { EntityDescriptor ed = entityDescriptorRepository.findByResourceId(resourceId); if (ed == null) { return ResponseEntity.notFound().build(); - } else { - if (currentUser != null && (currentUser.getRole().equals("ROLE_ADMIN") || currentUser.getUsername().equals(ed.getCreatedBy()))) { - EntityDescriptorRepresentation edr = entityDescriptorService.createRepresentationFromDescriptor(ed); - return ResponseEntity.ok(edr); - } else { - return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ErrorResponse(HttpStatus.FORBIDDEN, - "You are not authorized to perform the requested operation.")); - } } - } - - @GetMapping(value = "/EntityDescriptor/{resourceId}", produces = "application/xml") - @Transactional(readOnly = true) - public ResponseEntity getOneXml(@PathVariable String resourceId) throws MarshallingException { - User currentUser = userService.getCurrentUser(); - EntityDescriptor ed = entityDescriptorRepository.findByResourceId(resourceId); - if (ed == null) { + List versions = versionService.findVersionsForEntityDescriptor(resourceId); + if (versions.isEmpty()) { return ResponseEntity.notFound().build(); - } else { - if (currentUser != null && (currentUser.getRole().equals("ROLE_ADMIN") || currentUser.getUsername().equals(ed.getCreatedBy()))) { - final String xml = this.openSamlObjects.marshalToXmlString(ed); - return ResponseEntity.ok(xml); - } else { - return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); - } } + if(userService.isAuthorizedFor(ed.getCreatedBy(), ed.getGroup() == null ? null : ed.getGroup().getResourceId())) { + return ResponseEntity.ok(versions); + } + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); } + @Secured("ROLE_ADMIN") @Transactional(readOnly = true) @GetMapping(value = "/EntityDescriptor/disabledNonAdmin") public Iterable getDisabledAndNotOwnedByAdmin() { @@ -209,90 +172,59 @@ public Iterable getDisabledAndNotOwnedByAdmin() .collect(Collectors.toList()); } - @Secured("ROLE_ADMIN") - @DeleteMapping(value = "/EntityDescriptor/{resourceId}") - @Transactional - public ResponseEntity deleteOne(@PathVariable String resourceId) { - EntityDescriptor ed = entityDescriptorRepository.findByResourceId(resourceId); - if (ed == null) { - return ResponseEntity.notFound().build(); - } else if (ed.isServiceEnabled()) { - return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ErrorResponse(HttpStatus.FORBIDDEN, "Deleting an enabled Metadata Source is not allowed. Disable the source and try again.")); - } else { - entityDescriptorRepository.delete(ed); - return ResponseEntity.noContent().build(); + @GetMapping("/EntityDescriptor/{resourceId}") + @Transactional(readOnly = true) + public ResponseEntity getOne(@PathVariable String resourceId) { + User currentUser = userService.getCurrentUser(); + if (currentUser != null) { + EntityDescriptor ed = entityDescriptorRepository.findByResourceId(resourceId); + if (ed == null) { + return ResponseEntity.notFound().build(); + } else { + if (userService.isAuthorizedFor(ed.getCreatedBy(), ed.getGroup() == null ? null : ed.getGroup().getResourceId())) { + EntityDescriptorRepresentation edr = entityDescriptorService.createRepresentationFromDescriptor(ed); + return ResponseEntity.ok(edr); + } + } } - } + return ResponseEntity.status(HttpStatus.FORBIDDEN).body( + new ErrorResponse(HttpStatus.FORBIDDEN, "You are not authorized to perform the requested operation.")); - //Versioning endpoints + } - @GetMapping("/EntityDescriptor/{resourceId}/Versions") + @GetMapping(value = "/EntityDescriptor/{resourceId}", produces = "application/xml") @Transactional(readOnly = true) - public ResponseEntity getAllVersions(@PathVariable String resourceId) { - EntityDescriptor ed = entityDescriptorRepository.findByResourceId(resourceId); - if (ed == null) { - return ResponseEntity.notFound().build(); - } - List versions = versionService.findVersionsForEntityDescriptor(resourceId); - if (versions.isEmpty()) { - return ResponseEntity.notFound().build(); - } - if(isAuthorizedFor(ed.getCreatedBy())) { - return ResponseEntity.ok(versions); + public ResponseEntity getOneXml(@PathVariable String resourceId) throws MarshallingException { + User currentUser = userService.getCurrentUser(); + if (currentUser != null) { + EntityDescriptor ed = entityDescriptorRepository.findByResourceId(resourceId); + if (ed == null) { + return ResponseEntity.notFound().build(); + } else { + if (userService.isAuthorizedFor(ed.getCreatedBy(), ed.getGroup() == null ? null : ed.getGroup().getResourceId())) { + final String xml = this.openSamlObjects.marshalToXmlString(ed); + return ResponseEntity.ok(xml); + } + } } return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } @GetMapping("/EntityDescriptor/{resourceId}/Versions/{versionId}") @Transactional(readOnly = true) public ResponseEntity getSpecificVersion(@PathVariable String resourceId, @PathVariable String versionId) { - EntityDescriptorRepresentation edRepresentation = - versionService.findSpecificVersionOfEntityDescriptor(resourceId, versionId); + EntityDescriptorRepresentation edRepresentation = versionService.findSpecificVersionOfEntityDescriptor(resourceId, versionId); if (edRepresentation == null) { return ResponseEntity.notFound().build(); } - if(isAuthorizedFor(edRepresentation.getCreatedBy())) { + if(userService.isAuthorizedFor(edRepresentation.getCreatedBy(), edRepresentation.getGroupId())) { return ResponseEntity.ok(edRepresentation); } return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); } - //Private methods - - private static URI getResourceUriFor(EntityDescriptor ed) { - return ServletUriComponentsBuilder - .fromCurrentServletMapping().path("/api/EntityDescriptor") - .pathSegment(ed.getResourceId()) - .build() - .toUri(); - } - - private ResponseEntity existingEntityDescriptorCheck(String entityId) { - final EntityDescriptor ed = entityDescriptorRepository.findByEntityID(entityId); - if (ed != null) { - HttpHeaders headers = new HttpHeaders(); - headers.setLocation(getResourceUriFor(ed)); - 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.", entityId))); - } - //No existing entity descriptor, which is an OK condition indicated by returning a null conflict response - return null; - } - - private ResponseEntity entityDescriptorEnablePermissionsCheck(boolean serviceEnabled) { - User user = userService.getCurrentUser(); - if (user != null) { - if (serviceEnabled && !user.getRole().equals("ROLE_ADMIN")) { - return ResponseEntity.status(HttpStatus.FORBIDDEN) - .body(new ErrorResponse(HttpStatus.FORBIDDEN, "You do not have the permissions necessary to enable this service.")); - } - } - return null; - } - private ResponseEntity handleUploadingEntityDescriptorXml(byte[] rawXmlBytes, String spName) throws Exception { final EntityDescriptor ed = EntityDescriptor.class.cast(openSamlObjects.unmarshalFromXml(rawXmlBytes)); @@ -307,11 +239,66 @@ private ResponseEntity handleUploadingEntityDescriptorXml(byte[] rawXmlBytes, .body(entityDescriptorService.createRepresentationFromDescriptor(persistedEd)); } - private boolean isAuthorizedFor(String username) { - User u = userService.getCurrentUser(); - return (u != null) && - (u.getRole().equals("ROLE_ADMIN") - || (u.getUsername().equals(username))); + @PostConstruct + public void initRestTemplate() { + this.restTemplate = restTemplateBuilder.build(); + } + + @PutMapping("/EntityDescriptor/{resourceId}") + @Transactional + public ResponseEntity update(@RequestBody EntityDescriptorRepresentation edRepresentation, @PathVariable String resourceId) { + User currentUser = userService.getCurrentUser(); + if (currentUser != null) { + EntityDescriptor existingEd = entityDescriptorRepository.findByResourceId(resourceId); + + if (existingEd == null) { + return ResponseEntity.notFound().build(); + } else { + if (userService.isAuthorizedFor(existingEd.getCreatedBy(), + existingEd.getGroup() == null ? null : existingEd.getGroup().getResourceId())) { + if (!existingEd.isServiceEnabled()) { + ResponseEntity entityDescriptorEnablingDeniedResponse = entityDescriptorEnablePermissionsCheck( + edRepresentation.isServiceEnabled()); + if (entityDescriptorEnablingDeniedResponse != null) { + return entityDescriptorEnablingDeniedResponse; + } + } + + // Verify we're the only one attempting to update the EntityDescriptor + if (edRepresentation.getVersion() != existingEd.hashCode()) { + return new ResponseEntity(HttpStatus.CONFLICT); + } + + entityDescriptorService.updateDescriptorFromRepresentation(existingEd, edRepresentation); + existingEd = entityDescriptorRepository.save(existingEd); + + return ResponseEntity.ok().body(entityDescriptorService.createRepresentationFromDescriptor(existingEd)); + } + } + } + return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ErrorResponse(HttpStatus.FORBIDDEN, + "You are not authorized to perform the requested operation.")); + } + + @PostMapping(value = "/EntityDescriptor", consumes = "application/xml") + @Transactional + public ResponseEntity upload(@RequestBody byte[] entityDescriptorXml, @RequestParam String spName) throws Exception { + return handleUploadingEntityDescriptorXml(entityDescriptorXml, spName); + } + + @PostMapping(value = "/EntityDescriptor", consumes = "application/x-www-form-urlencoded") + @Transactional + public ResponseEntity upload(@RequestParam String metadataUrl, @RequestParam String spName) throws Exception { + try { + byte[] xmlContents = this.restTemplate.getForObject(metadataUrl, byte[].class); + return handleUploadingEntityDescriptorXml(xmlContents, spName); + } catch (Throwable e) { + log.error("Error fetching XML metadata from the provided URL: [{}]. The error is: {}", metadataUrl, e); + log.error(e.getMessage(), e); + return ResponseEntity + .badRequest() + .body(String.format("Error fetching XML metadata from the provided URL. Error: %s", e.getMessage())); + } } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptor.java index dc036fa7f..99c0377a5 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptor.java @@ -1,10 +1,17 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.google.common.base.MoreObjects; import com.google.common.collect.Lists; + +import edu.internet2.tier.shibboleth.admin.ui.security.model.Group; import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + import org.hibernate.envers.Audited; import org.hibernate.envers.NotAudited; +import org.hibernate.envers.RelationTargetAuditMode; import org.opensaml.core.xml.XMLObject; import org.springframework.util.StringUtils; @@ -12,6 +19,7 @@ import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.OrderColumn; @@ -26,32 +34,9 @@ @Entity -@EqualsAndHashCode(callSuper = true, exclude={"versionModifiedTimestamp"}) +@EqualsAndHashCode(callSuper = true) @Audited public class EntityDescriptor extends AbstractDescriptor implements org.opensaml.saml.saml2.metadata.EntityDescriptor { - private String localId; - - private String entityID; - - private String serviceProviderName; - - private boolean serviceEnabled; - - private String resourceId; - - private Long versionModifiedTimestamp; - - @OneToOne(cascade = CascadeType.ALL) - private Organization organization; - - @OneToMany(cascade = CascadeType.ALL) - @OrderColumn - private List contactPersons = new ArrayList<>(); - - @OneToMany(cascade = CascadeType.ALL) - @OrderColumn - private List roleDescriptors; - @OneToMany(cascade = CascadeType.ALL) @JoinColumn(name = "entitydesc_addlmetdatlocations_id") @OrderColumn @@ -60,38 +45,85 @@ public class EntityDescriptor extends AbstractDescriptor implements org.opensaml @OneToOne(cascade = CascadeType.ALL) @NotAudited - private AuthnAuthorityDescriptor authnAuthorityDescriptor; - + private AffiliationDescriptor affiliationDescriptor; + @OneToOne(cascade = CascadeType.ALL) @NotAudited private AttributeAuthorityDescriptor attributeAuthorityDescriptor; @OneToOne(cascade = CascadeType.ALL) @NotAudited - private PDPDescriptor pdpDescriptor; + private AuthnAuthorityDescriptor authnAuthorityDescriptor; + + @OneToMany(cascade = CascadeType.ALL) + @OrderColumn + private List contactPersons = new ArrayList<>(); + + private String entityID; + + @ManyToOne + @JoinColumn(name = "group_resource_id") + @EqualsAndHashCode.Exclude + @Setter + @Getter + @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED) +// @JsonIgnore + private Group group; + + private String localId; + + @OneToOne(cascade = CascadeType.ALL) + private Organization organization; @OneToOne(cascade = CascadeType.ALL) @NotAudited - private AffiliationDescriptor affiliationDescriptor; + private PDPDescriptor pdpDescriptor; + + private String resourceId; + + @OneToMany(cascade = CascadeType.ALL) + @OrderColumn + private List roleDescriptors; + + private boolean serviceEnabled; + + private String serviceProviderName; + + @EqualsAndHashCode.Exclude + private Long versionModifiedTimestamp; public EntityDescriptor() { super(); this.resourceId = UUID.randomUUID().toString(); } - public void setVersionModifiedTimestamp(Long versionModifiedTimestamp) { - this.versionModifiedTimestamp = versionModifiedTimestamp; + public void addContactPerson(ContactPerson contactPerson) { + this.contactPersons.add(contactPerson); } - //getters and setters @Override - public String getID() { - return this.localId; + public List getAdditionalMetadataLocations() { + return Lists.newArrayList(additionalMetadataLocations); } @Override - public void setID(String id) { - this.localId = id; + public AffiliationDescriptor getAffiliationDescriptor() { + return affiliationDescriptor; + } + + @Override + public AttributeAuthorityDescriptor getAttributeAuthorityDescriptor(String s) { + return attributeAuthorityDescriptor; + } + + @Override + public AuthnAuthorityDescriptor getAuthnAuthorityDescriptor(String s) { + return authnAuthorityDescriptor; + } + + @Override + public List getContactPersons() { + return (List) (List) this.contactPersons; } @Override @@ -99,33 +131,62 @@ public String getEntityID() { return entityID; } + //getters and setters @Override - public void setEntityID(String entityID) { - this.entityID = entityID; + public String getID() { + return this.localId; } - public String getServiceProviderName() { - return serviceProviderName; + @Override + @Transient + public IDPSSODescriptor getIDPSSODescriptor(String s) { + return (IDPSSODescriptor) this.getRoleDescriptors() + .stream() + .filter(p -> p instanceof org.opensaml.saml.saml2.metadata.IDPSSODescriptor && (StringUtils.isEmpty(s) ? true :p.isSupportedProtocol(s))) + .findFirst() + .orElse(null); } - public void setServiceProviderName(String serviceProviderName) { - this.serviceProviderName = serviceProviderName; + @Transient + public Optional getOptionalSPSSODescriptor() { + return this.getOptionalSPSSODescriptor(""); } - public boolean isServiceEnabled() { - return serviceEnabled; + @Transient + public Optional getOptionalSPSSODescriptor(String s) { + return Optional.ofNullable(this.getSPSSODescriptor(s)); } - public void setServiceEnabled(boolean serviceEnabled) { - this.serviceEnabled = serviceEnabled; + @Nullable + @Override + public List getOrderedChildren() { + final ArrayList children = new ArrayList<>(); + + if (getSignature() != null) { + children.add(getSignature()); + } + children.add(getExtensions()); + children.addAll(this.getRoleDescriptors()); + children.add(getAffiliationDescriptor()); + children.add(getOrganization()); + children.addAll(this.getContactPersons()); + children.addAll(this.getAdditionalMetadataLocations()); + + return Collections.unmodifiableList(children); } - public String getResourceId() { - return resourceId; + @Override + public org.opensaml.saml.saml2.metadata.Organization getOrganization() { + return organization; } - public void setResourceId(String resourceId) { - this.resourceId = resourceId; + @Override + public PDPDescriptor getPDPDescriptor(String s) { + return pdpDescriptor; + } + + public String getResourceId() { + return resourceId; } @Override @@ -138,10 +199,6 @@ public List getRoleDescriptors( return (List) (List) this.roleDescriptors; } - public void setRoleDescriptors(List roleDescriptors) { - this.roleDescriptors = roleDescriptors; - } - @Override public List getRoleDescriptors(QName qName) { return this.getRoleDescriptors() @@ -158,14 +215,8 @@ public List getRoleDescriptors( .collect(Collectors.toList()); } - @Override - @Transient - public IDPSSODescriptor getIDPSSODescriptor(String s) { - return (IDPSSODescriptor) this.getRoleDescriptors() - .stream() - .filter(p -> p instanceof org.opensaml.saml.saml2.metadata.IDPSSODescriptor && (StringUtils.isEmpty(s) ? true :p.isSupportedProtocol(s))) - .findFirst() - .orElse(null); + public String getServiceProviderName() { + return serviceProviderName; } @Override @@ -178,56 +229,39 @@ public SPSSODescriptor getSPSSODescriptor(String s) { .orElse(null); } - @Transient - public Optional getOptionalSPSSODescriptor(String s) { - return Optional.ofNullable(this.getSPSSODescriptor(s)); - } - - @Transient - public Optional getOptionalSPSSODescriptor() { - return this.getOptionalSPSSODescriptor(""); - } - - @Override - public AuthnAuthorityDescriptor getAuthnAuthorityDescriptor(String s) { - return authnAuthorityDescriptor; + public boolean isServiceEnabled() { + return serviceEnabled; } - public void setAuthnAuthorityDescriptor(AuthnAuthorityDescriptor authnAuthorityDescriptor) { - this.authnAuthorityDescriptor = authnAuthorityDescriptor; + public void setAdditionalMetadataLocations(List additionalMetadataLocations) { + this.additionalMetadataLocations = additionalMetadataLocations; } @Override - public AttributeAuthorityDescriptor getAttributeAuthorityDescriptor(String s) { - return attributeAuthorityDescriptor; + public void setAffiliationDescriptor(org.opensaml.saml.saml2.metadata.AffiliationDescriptor affiliationDescriptor) { + this.affiliationDescriptor = (AffiliationDescriptor) affiliationDescriptor; } public void setAttributeAuthorityDescriptor(AttributeAuthorityDescriptor attributeAuthorityDescriptor) { this.attributeAuthorityDescriptor = attributeAuthorityDescriptor; } - @Override - public PDPDescriptor getPDPDescriptor(String s) { - return pdpDescriptor; - } - - public void setPdpDescriptor(PDPDescriptor pdpDescriptor) { - this.pdpDescriptor = pdpDescriptor; + public void setAuthnAuthorityDescriptor(AuthnAuthorityDescriptor authnAuthorityDescriptor) { + this.authnAuthorityDescriptor = authnAuthorityDescriptor; } - @Override - public AffiliationDescriptor getAffiliationDescriptor() { - return affiliationDescriptor; + public void setContactPersons(List contactPersons) { + this.contactPersons = contactPersons; } @Override - public void setAffiliationDescriptor(org.opensaml.saml.saml2.metadata.AffiliationDescriptor affiliationDescriptor) { - this.affiliationDescriptor = (AffiliationDescriptor) affiliationDescriptor; + public void setEntityID(String entityID) { + this.entityID = entityID; } @Override - public org.opensaml.saml.saml2.metadata.Organization getOrganization() { - return organization; + public void setID(String id) { + this.localId = id; } @Override @@ -235,26 +269,28 @@ public void setOrganization(org.opensaml.saml.saml2.metadata.Organization organi this.organization = (Organization) organization; } - @Override - public List getContactPersons() { - return (List) (List) this.contactPersons; + public void setPdpDescriptor(PDPDescriptor pdpDescriptor) { + this.pdpDescriptor = pdpDescriptor; } - public void addContactPerson(ContactPerson contactPerson) { - this.contactPersons.add(contactPerson); + public void setResourceId(String resourceId) { + this.resourceId = resourceId; } - public void setContactPersons(List contactPersons) { - this.contactPersons = contactPersons; + public void setRoleDescriptors(List roleDescriptors) { + this.roleDescriptors = roleDescriptors; } - @Override - public List getAdditionalMetadataLocations() { - return Lists.newArrayList(additionalMetadataLocations); + public void setServiceEnabled(boolean serviceEnabled) { + this.serviceEnabled = serviceEnabled; } - public void setAdditionalMetadataLocations(List additionalMetadataLocations) { - this.additionalMetadataLocations = additionalMetadataLocations; + public void setServiceProviderName(String serviceProviderName) { + this.serviceProviderName = serviceProviderName; + } + + public void setVersionModifiedTimestamp(Long versionModifiedTimestamp) { + this.versionModifiedTimestamp = versionModifiedTimestamp; } @Override @@ -265,22 +301,4 @@ public String toString() { .add("id", id) .toString(); } - - @Nullable - @Override - public List getOrderedChildren() { - final ArrayList children = new ArrayList<>(); - - if (getSignature() != null) { - children.add(getSignature()); - } - children.add(getExtensions()); - children.addAll(this.getRoleDescriptors()); - children.add(getAffiliationDescriptor()); - children.add(getOrganization()); - children.addAll(this.getContactPersons()); - children.addAll(this.getAdditionalMetadataLocations()); - - return Collections.unmodifiableList(children); - } } 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 32063271e..d2d4d8d6d 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 @@ -4,6 +4,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.Setter; + import javax.validation.constraints.NotNull; import java.io.Serializable; import java.time.LocalDateTime; @@ -12,231 +15,234 @@ import java.util.Map; public class EntityDescriptorRepresentation implements Serializable { + private static final long serialVersionUID = 7753435553892353966L; - public EntityDescriptorRepresentation() { - } + private List assertionConsumerServices; - public EntityDescriptorRepresentation(String id, - String entityId, - String serviceProviderName, - boolean serviceEnabled, - LocalDateTime createdDate, - LocalDateTime modifiedDate) { - this.id = id; - this.entityId = entityId; - this.serviceProviderName = serviceProviderName; - this.serviceEnabled = serviceEnabled; - this.createdDate = createdDate; - this.modifiedDate = modifiedDate; - } + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List attributeRelease; - private static final long serialVersionUID = 7753435553892353966L; + private List contacts; - private String id; + private String createdBy; - @NotNull - private String serviceProviderName; + private LocalDateTime createdDate; + + @JsonProperty + private boolean current; @NotNull private String entityId; - //TODO: review requirement - private OrganizationRepresentation organization = new OrganizationRepresentation(); + @Getter + @Setter + private String groupId; + + private String id; - private List contacts; + private List logoutEndpoints; @JsonInclude(JsonInclude.Include.NON_EMPTY) private MduiRepresentation mdui; - private ServiceProviderSsoDescriptorRepresentation serviceProviderSsoDescriptor; + private LocalDateTime modifiedDate; - private List logoutEndpoints; + //TODO: review requirement + private OrganizationRepresentation organization = new OrganizationRepresentation(); - private SecurityInfoRepresentation securityInfo; + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private Map relyingPartyOverrides; - private List assertionConsumerServices; + private SecurityInfoRepresentation securityInfo; private boolean serviceEnabled; - private LocalDateTime createdDate; - - private LocalDateTime modifiedDate; - - @JsonInclude(JsonInclude.Include.NON_EMPTY) - private Map relyingPartyOverrides; + @NotNull + private String serviceProviderName; - @JsonInclude(JsonInclude.Include.NON_EMPTY) - private List attributeRelease; + private ServiceProviderSsoDescriptorRepresentation serviceProviderSsoDescriptor; private int version; - private String createdBy; - - @JsonProperty - private boolean current; - - public String getId() { - return id; + public EntityDescriptorRepresentation() { } - public void setId(String id) { + public EntityDescriptorRepresentation(String id, + String entityId, + String serviceProviderName, + boolean serviceEnabled, + LocalDateTime createdDate, + LocalDateTime modifiedDate) { this.id = id; + this.entityId = entityId; + this.serviceProviderName = serviceProviderName; + this.serviceEnabled = serviceEnabled; + this.createdDate = createdDate; + this.modifiedDate = modifiedDate; } - public String getServiceProviderName() { - return serviceProviderName; + public List getAssertionConsumerServices() { + return assertionConsumerServices; } - public void setServiceProviderName(String serviceProviderName) { - this.serviceProviderName = serviceProviderName; + public List getAttributeRelease() { + return attributeRelease; } - public String getEntityId() { - return entityId; + public List getContacts() { + return contacts; } - public void setEntityId(String entityId) { - this.entityId = entityId; + public String getCreatedBy() { + return createdBy; } - public OrganizationRepresentation getOrganization() { - return organization; + public String getCreatedDate() { + return createdDate != null ? createdDate.toString() : null; } - public void setOrganization(OrganizationRepresentation organization) { - this.organization = organization; + public String getEntityId() { + return entityId; } - public List getContacts() { - return contacts; + public String getId() { + return id; } - public void setContacts(List contacts) { - this.contacts = contacts; + public List getLogoutEndpoints() { + return this.getLogoutEndpoints(false); + } + + public List getLogoutEndpoints(boolean create) { + if (create && this.logoutEndpoints == null) { + this.logoutEndpoints = new ArrayList<>(); + } + return logoutEndpoints; } public MduiRepresentation getMdui() { return mdui; } - public void setMdui(MduiRepresentation mdui) { - this.mdui = mdui; + public String getModifiedDate() { + return modifiedDate != null ? modifiedDate.toString() : null; } + @JsonIgnore + public LocalDateTime getModifiedDateAsDate() { + // we shouldn't have an ED without either modified or created date, so this is mostly for testing where data can be odd + return modifiedDate != null ? modifiedDate : createdDate != null ? createdDate : LocalDateTime.now(); + } - public ServiceProviderSsoDescriptorRepresentation getServiceProviderSsoDescriptor() { - return this.getServiceProviderSsoDescriptor(false); + + public OrganizationRepresentation getOrganization() { + return organization; } - public ServiceProviderSsoDescriptorRepresentation getServiceProviderSsoDescriptor(boolean create) { - if (create && this.serviceProviderSsoDescriptor == null) { - this.serviceProviderSsoDescriptor = new ServiceProviderSsoDescriptorRepresentation(); - } - return this.serviceProviderSsoDescriptor; + public Map getRelyingPartyOverrides() { + return relyingPartyOverrides; } - public void setServiceProviderSsoDescriptor(ServiceProviderSsoDescriptorRepresentation serviceProviderSsoDescriptor) { - this.serviceProviderSsoDescriptor = serviceProviderSsoDescriptor; + public SecurityInfoRepresentation getSecurityInfo() { + return securityInfo; } - public List getLogoutEndpoints() { - return this.getLogoutEndpoints(false); + public String getServiceProviderName() { + return serviceProviderName; } - public List getLogoutEndpoints(boolean create) { - if (create && this.logoutEndpoints == null) { - this.logoutEndpoints = new ArrayList<>(); - } - return logoutEndpoints; + public ServiceProviderSsoDescriptorRepresentation getServiceProviderSsoDescriptor() { + return this.getServiceProviderSsoDescriptor(false); } - public void setLogoutEndpoints(List logoutEndpoints) { - this.logoutEndpoints = logoutEndpoints; + public ServiceProviderSsoDescriptorRepresentation getServiceProviderSsoDescriptor(boolean create) { + if (create && this.serviceProviderSsoDescriptor == null) { + this.serviceProviderSsoDescriptor = new ServiceProviderSsoDescriptorRepresentation(); + } + return this.serviceProviderSsoDescriptor; } - public SecurityInfoRepresentation getSecurityInfo() { - return securityInfo; + public int getVersion() { + return version; } - public void setSecurityInfo(SecurityInfoRepresentation securityInfo) { - this.securityInfo = securityInfo; + public boolean isCurrent() { + return current; } - public List getAssertionConsumerServices() { - return assertionConsumerServices; + public boolean isServiceEnabled() { + return serviceEnabled; } public void setAssertionConsumerServices(List assertionConsumerServices) { this.assertionConsumerServices = assertionConsumerServices; } - public boolean isServiceEnabled() { - return serviceEnabled; + public void setAttributeRelease(List attributeRelease) { + this.attributeRelease = attributeRelease; } - public void setServiceEnabled(boolean serviceEnabled) { - this.serviceEnabled = serviceEnabled; + public void setContacts(List contacts) { + this.contacts = contacts; } - public String getCreatedDate() { - return createdDate != null ? createdDate.toString() : null; + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; } public void setCreatedDate(LocalDateTime createdDate) { this.createdDate = createdDate; } - public String getModifiedDate() { - return modifiedDate != null ? modifiedDate.toString() : null; + public void setCurrent(boolean current) { + this.current = current; } - @JsonIgnore - public LocalDateTime getModifiedDateAsDate() { - // we shouldn't have an ED without either modified or created date, so this is mostly for testing where data can be odd - return modifiedDate != null ? modifiedDate : createdDate != null ? createdDate : LocalDateTime.now(); + public void setEntityId(String entityId) { + this.entityId = entityId; } - public void setModifiedDate(LocalDateTime modifiedDate) { - this.modifiedDate = modifiedDate; + public void setId(String id) { + this.id = id; } - public Map getRelyingPartyOverrides() { - return relyingPartyOverrides; + public void setLogoutEndpoints(List logoutEndpoints) { + this.logoutEndpoints = logoutEndpoints; } - public void setRelyingPartyOverrides(Map relyingPartyOverrides) { - this.relyingPartyOverrides = relyingPartyOverrides; + public void setMdui(MduiRepresentation mdui) { + this.mdui = mdui; } - public List getAttributeRelease() { - return attributeRelease; + public void setModifiedDate(LocalDateTime modifiedDate) { + this.modifiedDate = modifiedDate; } - public void setAttributeRelease(List attributeRelease) { - this.attributeRelease = attributeRelease; + public void setOrganization(OrganizationRepresentation organization) { + this.organization = organization; } - public int getVersion() { - return version; + public void setRelyingPartyOverrides(Map relyingPartyOverrides) { + this.relyingPartyOverrides = relyingPartyOverrides; } - public void setVersion(int version) { - this.version = version; + public void setSecurityInfo(SecurityInfoRepresentation securityInfo) { + this.securityInfo = securityInfo; } - public String getCreatedBy() { - return createdBy; + public void setServiceEnabled(boolean serviceEnabled) { + this.serviceEnabled = serviceEnabled; } - public void setCreatedBy(String createdBy) { - this.createdBy = createdBy; + public void setServiceProviderName(String serviceProviderName) { + this.serviceProviderName = serviceProviderName; } - public boolean isCurrent() { - return current; + public void setServiceProviderSsoDescriptor(ServiceProviderSsoDescriptorRepresentation serviceProviderSsoDescriptor) { + this.serviceProviderSsoDescriptor = serviceProviderSsoDescriptor; } - public void setCurrent(boolean current) { - this.current = current; + public void setVersion(int version) { + this.version = version; } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepository.java index 8a6596594..4423d578d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepository.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepository.java @@ -26,4 +26,6 @@ public interface EntityDescriptorRepository extends JpaRepository findAllDisabledAndNotOwnedByAdmin(); Stream findAllStreamByCreatedBy(String createdBy); + + Stream findAllStreamByGroup_resourceIdOrCreatedBy(String resourceId, String username); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/controller/GroupController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/controller/GroupController.java index da6d5edae..4062ec080 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/controller/GroupController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/controller/GroupController.java @@ -52,7 +52,7 @@ public ResponseEntity update(@RequestBody Group group) { if (g == null) { HttpHeaders headers = new HttpHeaders(); - headers.setLocation(ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/admin/groups").build().toUri()); + headers.setLocation(ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/admin/groups/{resourceId}").build().toUri()); return ResponseEntity.status(HttpStatus.NOT_FOUND).headers(headers) .body(new ErrorResponse(String.valueOf(HttpStatus.NOT_FOUND.value()), @@ -99,13 +99,13 @@ public ResponseEntity delete(@PathVariable String resourceId) { .body(new ErrorResponse(String.valueOf(HttpStatus.NOT_FOUND.value()), String.format("Unable to find group with resource id: [%s]", resourceId))); } - if (!g.getUsers().isEmpty()) { + if (!g.getUsers().isEmpty() || !g.getEntityDescriptors().isEmpty()) { HttpHeaders headers = new HttpHeaders(); - headers.setLocation(ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/admin/groups").build().toUri()); + headers.setLocation(ServletUriComponentsBuilder.fromCurrentServletMapping().path("/api/admin/groups/{resourceId}").build().toUri()); return ResponseEntity.status(HttpStatus.CONFLICT).headers(headers) .body(new ErrorResponse(String.valueOf(HttpStatus.CONFLICT.value()), String.format( - "Unable to delete group with resource id: [%s] - remove all users from group first", + "Unable to delete group with resource id: [%s] - remove all users and entities from group first", resourceId))); } groupService.deleteDefinition(g); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/controller/UsersController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/controller/UsersController.java index db8924242..3b75b865c 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/controller/UsersController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/controller/UsersController.java @@ -1,13 +1,16 @@ package edu.internet2.tier.shibboleth.admin.ui.security.controller; import edu.internet2.tier.shibboleth.admin.ui.controller.ErrorResponse; +import edu.internet2.tier.shibboleth.admin.ui.security.model.Group; import edu.internet2.tier.shibboleth.admin.ui.security.model.User; +import edu.internet2.tier.shibboleth.admin.ui.security.repository.GroupsRepository; import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository; import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository; import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService; +import groovy.util.logging.Slf4j; + import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; @@ -31,25 +34,35 @@ /** * Implementation of the REST resource endpoints exposing system users. - * - * @author Dmitriy Kopylenko */ @RestController @RequestMapping("/api/admin/users") +@Slf4j public class UsersController { - - private static final Logger logger = LoggerFactory.getLogger(UsersController.class); - + @Autowired + private GroupsRepository groupRepo; private UserRepository userRepository; - private RoleRepository roleRepository; private UserService userService; public UsersController(UserRepository userRepository, RoleRepository roleRepository, UserService userService) { this.userRepository = userRepository; - this.roleRepository = roleRepository; this.userService = userService; } + @PreAuthorize("hasRole('ADMIN')") + @Transactional + @DeleteMapping("/{username}") + public ResponseEntity deleteOne(@PathVariable String username) { + User user = findUserOrThrowHttp404(username); + userRepository.delete(user); + return ResponseEntity.noContent().build(); + } + + private User findUserOrThrowHttp404(String username) { + return userRepository.findByUsername(username) + .orElseThrow(() -> new HttpClientErrorException(NOT_FOUND, String.format("User with username [%s] not found", username))); + } + @PreAuthorize("hasRole('ADMIN')") @Transactional(readOnly = true) @GetMapping @@ -78,15 +91,6 @@ public ResponseEntity getUsersWithRole(@PathVariable String rolename) { return ResponseEntity.ok(userRepository.findByRoles_Name(rolename)); } - @PreAuthorize("hasRole('ADMIN')") - @Transactional - @DeleteMapping("/{username}") - public ResponseEntity deleteOne(@PathVariable String username) { - User user = findUserOrThrowHttp404(username); - userRepository.delete(user); - return ResponseEntity.noContent().build(); - } - @PreAuthorize("hasRole('ADMIN')") @Transactional @PostMapping @@ -101,10 +105,22 @@ ResponseEntity saveOne(@RequestBody User user) { //TODO: modify this such that additional encoders can be used user.setPassword(BCrypt.hashpw(user.getPassword(), BCrypt.gensalt())); userService.updateUserRole(user); + findAndSetGroup(user); + User savedUser = userRepository.save(user); return ResponseEntity.ok(savedUser); } + private User findAndSetGroup(User user) { + // Ensure we have the full group detail from the db + if (user.getGroupId() != null || user.getGroup() != null) { + String resourceId = user.getGroupId() == null ? user.getGroup().getResourceId() : user.getGroupId(); + Group groupFromDb = groupRepo.findByResourceId(resourceId); + user.setGroup(groupFromDb); + } + return user; + } + @PreAuthorize("hasRole('ADMIN')") @Transactional @PatchMapping("/{username}") @@ -126,12 +142,8 @@ ResponseEntity updateOne(@PathVariable(value = "username") String username, @ persistedUser.setRole(user.getRole()); userService.updateUserRole(persistedUser); } + persistedUser.setGroup(findAndSetGroup(user).getGroup()); User savedUser = userRepository.save(persistedUser); return ResponseEntity.ok(savedUser); } - - private User findUserOrThrowHttp404(String username) { - return userRepository.findByUsername(username) - .orElseThrow(() -> new HttpClientErrorException(NOT_FOUND, String.format("User with username [%s] not found", username))); - } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Group.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Group.java index db903e988..adb172990 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Group.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Group.java @@ -10,8 +10,12 @@ import javax.persistence.Id; import javax.persistence.OneToMany; +import org.hibernate.envers.Audited; +import org.hibernate.envers.RelationTargetAuditMode; + import com.fasterxml.jackson.annotation.JsonIgnore; +import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor; import lombok.Data; import lombok.EqualsAndHashCode; @@ -32,4 +36,9 @@ public class Group { @JsonIgnore @EqualsAndHashCode.Exclude Set users; + + @OneToMany(mappedBy = "group", cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JsonIgnore + @EqualsAndHashCode.Exclude + Set entityDescriptors; } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/User.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/User.java index df4a96e13..8acb53a45 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/User.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/User.java @@ -43,10 +43,14 @@ public class User extends AbstractAuditable { private String firstName; @ManyToOne - @JoinColumn(name = "resource_id") + @JoinColumn(name = "group_resource_id") @EqualsAndHashCode.Exclude private Group group; + @Transient + @EqualsAndHashCode.Exclude + private String groupId; // simplifies the ui/api + private String lastName; @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserAccess.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserAccess.java new file mode 100644 index 000000000..d65849a44 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserAccess.java @@ -0,0 +1,8 @@ +package edu.internet2.tier.shibboleth.admin.ui.security.service; + +public enum UserAccess { + ADMIN, // Access to everything + GROUP, // Group users also should have owner access + OWNER, // Only access things this user created/owns + NONE // +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java index 3adca2b75..220bb090c 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java @@ -11,11 +11,7 @@ import java.util.Optional; import java.util.Set; -/** - * @author Bill Smith (wsmith@unicon.net) - */ public class UserService { - private RoleRepository roleRepository; private UserRepository userRepository; @@ -24,6 +20,53 @@ public UserService(RoleRepository roleRepository, UserRepository userRepository) this.userRepository = userRepository; } + public User getCurrentUser() { + //TODO: Consider returning an Optional here + User user = null; + if (SecurityContextHolder.getContext() != null && SecurityContextHolder.getContext().getAuthentication() != null) { + String principal = SecurityContextHolder.getContext().getAuthentication().getName(); + if (StringUtils.isNotBlank(principal)) { + Optional persistedUser = userRepository.findByUsername(principal); + if (persistedUser.isPresent()) { + user = persistedUser.get(); + } + } + } + return user; + } + + public UserAccess getCurrentUserAccess() { + User user = getCurrentUser(); + if (user == null) { + return UserAccess.NONE; + } + if (user.getRole().equals("ROLE_ADMIN")) { + return UserAccess.ADMIN; + } + if (user.getGroup() != null) { + return UserAccess.GROUP; + } + else { + return UserAccess.OWNER; + } + } + + public boolean isAuthorizedFor(String objectCreatedBy, String objectGroupResourceId) { + User currentUser = getCurrentUser(); + String groupId = objectGroupResourceId == null ? "" : objectGroupResourceId; + + switch (getCurrentUserAccess()) { + case ADMIN: + return true; + case GROUP: + return objectCreatedBy.equals(currentUser.getUsername()) || groupId.equals(currentUser.getGroupId()); + case OWNER: + return objectCreatedBy.equals(currentUser.getUsername()); + default: + return false; + } + } + /** * Given a user with a defined User.role, update the User.roles collection with that role. * @@ -46,19 +89,4 @@ public void updateUserRole(User user) { throw new RuntimeException(String.format("User with username [%s] has no role defined and therefor cannot be updated!", user.getUsername())); } } - - public User getCurrentUser() { - //TODO: Consider returning an Optional here - User user = null; - if (SecurityContextHolder.getContext() != null && SecurityContextHolder.getContext().getAuthentication() != null) { - String principal = SecurityContextHolder.getContext().getAuthentication().getName(); - if (StringUtils.isNotBlank(principal)) { - Optional persistedUser = userRepository.findByUsername(principal); - if (persistedUser.isPresent()) { - user = persistedUser.get(); - } - } - } - return user; - } -} +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityDescriptorService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityDescriptorService.java index ea57c4d06..cdcf80774 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityDescriptorService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityDescriptorService.java @@ -31,13 +31,10 @@ public interface EntityDescriptorService { EntityDescriptorRepresentation createRepresentationFromDescriptor(final EntityDescriptor entityDescriptor); /** - * Update an instance of entity descriptor with information from the front-end representation - * - * @param entityDescriptor opensaml model instance to update - * @param representation front end representation to use to update + * @return a list of EntityDescriptorRepresentations that a user has the rights to access */ - void updateDescriptorFromRepresentation(final EntityDescriptor entityDescriptor, final EntityDescriptorRepresentation representation); - + List getAllRepresentationsBasedOnUserAccess(); + /** * Given a list of attributes, generate an AttributeReleaseList * @@ -54,4 +51,12 @@ public interface EntityDescriptorService { */ Map getRelyingPartyOverridesRepresentationFromAttributeList(List attributeList); + /** + * Update an instance of entity descriptor with information from the front-end representation + * + * @param entityDescriptor opensaml model instance to update + * @param representation front end representation to use to update + */ + void updateDescriptorFromRepresentation(final EntityDescriptor entityDescriptor, final EntityDescriptorRepresentation representation); + } \ No newline at end of file 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 05477d616..5734f55ec 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 @@ -33,7 +33,6 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.XSBoolean; import edu.internet2.tier.shibboleth.admin.ui.domain.XSInteger; -import edu.internet2.tier.shibboleth.admin.ui.domain.XSString; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.AssertionConsumerServiceRepresentation; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.ContactRepresentation; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation; @@ -43,9 +42,15 @@ 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.opensaml.OpenSamlObjects; +import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository; +import edu.internet2.tier.shibboleth.admin.ui.security.model.Group; +import edu.internet2.tier.shibboleth.admin.ui.security.model.User; +import edu.internet2.tier.shibboleth.admin.ui.security.service.IGroupService; +import edu.internet2.tier.shibboleth.admin.ui.security.service.UserAccess; 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; +import groovy.util.logging.Slf4j; import org.opensaml.core.xml.XMLObject; import org.opensaml.core.xml.schema.XSBooleanValue; @@ -56,7 +61,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -67,7 +71,6 @@ import java.util.stream.Collectors; import static edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions.getStringListOfAttributeValues; -import static edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions.getValueFromXMLObject; /** @@ -75,16 +78,20 @@ * * @since 1.0 */ +@Slf4j public class JPAEntityDescriptorServiceImpl implements EntityDescriptorService { - - private static final Logger LOGGER = LoggerFactory.getLogger(JPAEntityDescriptorServiceImpl.class); - @Autowired - private OpenSamlObjects openSamlObjects; - + private EntityDescriptorRepository entityDescriptorRepository; + @Autowired private EntityService entityService; + @Autowired + private IGroupService groupService; + + @Autowired + private OpenSamlObjects openSamlObjects; + private UserService userService; public JPAEntityDescriptorServiceImpl(OpenSamlObjects openSamlObjects, EntityService entityService, UserService userService) { @@ -93,23 +100,9 @@ public JPAEntityDescriptorServiceImpl(OpenSamlObjects openSamlObjects, EntitySer this.userService = userService; } - @Override - public void updateDescriptorFromRepresentation(org.opensaml.saml.saml2.metadata.EntityDescriptor entityDescriptor, EntityDescriptorRepresentation representation) { - if (!(entityDescriptor instanceof EntityDescriptor)) { - throw new UnsupportedOperationException("not yet implemented"); - } - buildDescriptorFromRepresentation((EntityDescriptor) entityDescriptor, representation); - } - - @Override - public EntityDescriptor createDescriptorFromRepresentation(final EntityDescriptorRepresentation representation) { - EntityDescriptor ed = openSamlObjects.buildDefaultInstanceOfType(EntityDescriptor.class); - - return buildDescriptorFromRepresentation(ed, representation); - } - private EntityDescriptor buildDescriptorFromRepresentation(final EntityDescriptor ed, final EntityDescriptorRepresentation representation) { ed.setEntityID(representation.getEntityId()); + ed.setGroup(groupService.find(representation.getGroupId())); setupSPSSODescriptor(ed, representation); ed.setServiceProviderName(representation.getServiceProviderName()); @@ -129,381 +122,92 @@ private EntityDescriptor buildDescriptorFromRepresentation(final EntityDescripto return ed; } - void setupRelyingPartyOverrides(EntityDescriptor ed, EntityDescriptorRepresentation representation) { - if (representation.getRelyingPartyOverrides() != null || (representation.getAttributeRelease() != null && representation.getAttributeRelease().size() > 0)) { - // TODO: review if we need more than a naive implementation - getOptionalEntityAttributes(ed).ifPresent(entityAttributes -> entityAttributes.getAttributes().clear()); - getEntityAttributes(ed).getAttributes().addAll(entityService.getAttributeListFromEntityRepresentation(representation)); - } else { - getOptionalEntityAttributes(ed).ifPresent(entityAttributes -> entityAttributes.getAttributes().clear()); - } + private Attribute createAttributeWithArbitraryValues(String name, String friendlyName, List values) { + return createAttributeWithArbitraryValues(name, friendlyName, values.toArray(new String[]{})); } - void setupLogout(EntityDescriptor ed, EntityDescriptorRepresentation representation) { - // setup logout - if (representation.getLogoutEndpoints() != null && !representation.getLogoutEndpoints().isEmpty()) { - // TODO: review if we need more than a naive implementation - ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getSingleLogoutServices().clear()); - for (LogoutEndpointRepresentation logoutEndpointRepresentation : representation.getLogoutEndpoints()) { - SingleLogoutService singleLogoutService = openSamlObjects.buildDefaultInstanceOfType(SingleLogoutService.class); - singleLogoutService.setBinding(logoutEndpointRepresentation.getBindingType()); - singleLogoutService.setLocation(logoutEndpointRepresentation.getUrl()); + private Attribute createAttributeWithArbitraryValues(String name, String friendlyName, String... values) { + Attribute attribute = createBaseAttribute(name, friendlyName); - getSPSSODescriptorFromEntityDescriptor(ed).getSingleLogoutServices().add(singleLogoutService); - } - } else { - ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getSingleLogoutServices().clear()); + for (String value : values) { + XSAny xsAny = (XSAny) openSamlObjects.getBuilderFactory().getBuilder(XSAny.TYPE_NAME).buildObject(AttributeValue.DEFAULT_ELEMENT_NAME); + xsAny.setTextContent(value); + attribute.getAttributeValues().add(xsAny); } - } - void setupACSs(EntityDescriptor ed, EntityDescriptorRepresentation representation) { - // setup ACSs - if (representation.getAssertionConsumerServices() != null && representation.getAssertionConsumerServices().size() > 0) { - // TODO: review if we need more than a naive implementation - ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getAssertionConsumerServices().clear()); - for (AssertionConsumerServiceRepresentation acsRepresentation : representation.getAssertionConsumerServices()) { - AssertionConsumerService assertionConsumerService = openSamlObjects.buildDefaultInstanceOfType(AssertionConsumerService.class); - getSPSSODescriptorFromEntityDescriptor(ed).getAssertionConsumerServices().add(assertionConsumerService); - if (acsRepresentation.isMakeDefault()) { - assertionConsumerService.setIsDefault(true); - } - assertionConsumerService.setBinding(acsRepresentation.getBinding()); - assertionConsumerService.setLocation(acsRepresentation.getLocationUrl()); - assertionConsumerService.setIndex(acsRepresentation.getIndex()); - } - } else { - ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getAssertionConsumerServices().clear()); - } + return attribute; } - void setupSecurity(EntityDescriptor ed, EntityDescriptorRepresentation representation) { - // setup security - if (representation.getSecurityInfo() != null) { - SecurityInfoRepresentation securityInfoRepresentation = representation.getSecurityInfo(); - if (securityInfoRepresentation.isAuthenticationRequestsSigned()) { - getSPSSODescriptorFromEntityDescriptor(ed).setAuthnRequestsSigned(true); - } - if (securityInfoRepresentation.isWantAssertionsSigned()) { - getSPSSODescriptorFromEntityDescriptor(ed).setWantAssertionsSigned(true); - } - // TODO: review if we need more than a naive implementation - ed.getOptionalSPSSODescriptor().ifPresent( i -> i.getKeyDescriptors().clear()); - if (securityInfoRepresentation.isX509CertificateAvailable()) { - for (SecurityInfoRepresentation.X509CertificateRepresentation x509CertificateRepresentation : securityInfoRepresentation.getX509Certificates()) { - KeyDescriptor keyDescriptor = createKeyDescriptor(x509CertificateRepresentation.getName(), x509CertificateRepresentation.getType(), x509CertificateRepresentation.getValue()); - getSPSSODescriptorFromEntityDescriptor(ed).addKeyDescriptor(keyDescriptor); - } - } - } else { - ed.getOptionalSPSSODescriptor().ifPresent( spssoDescriptor -> { - spssoDescriptor.setAuthnRequestsSigned((Boolean) null); - spssoDescriptor.setWantAssertionsSigned((Boolean) null); - spssoDescriptor.getKeyDescriptors().clear(); - }); - } - } + private Attribute createAttributeWithBooleanValue(String name, String friendlyName, Boolean value) { + Attribute attribute = createBaseAttribute(name, friendlyName); - void setupUIInfo(EntityDescriptor ed, EntityDescriptorRepresentation representation) { - // set up mdui - if (representation.getMdui() != null) { - // TODO: check if we need more than a naive implementation - removeUIInfo(ed); - MduiRepresentation mduiRepresentation = representation.getMdui(); + XSBoolean xsBoolean = (XSBoolean) openSamlObjects.getBuilderFactory().getBuilder(XSBoolean.TYPE_NAME).buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSBoolean.TYPE_NAME); + xsBoolean.setValue(XSBooleanValue.valueOf(value.toString())); - if (!Strings.isNullOrEmpty(mduiRepresentation.getDisplayName())) { - DisplayName displayName = openSamlObjects.buildDefaultInstanceOfType(DisplayName.class); - getUIInfo(ed).addDisplayName(displayName); - displayName.setValue(mduiRepresentation.getDisplayName()); - displayName.setXMLLang("en"); - } else { - ed.getOptionalSPSSODescriptor() - .flatMap(SPSSODescriptor::getOptionalExtensions) - .flatMap(Extensions::getOptionalUIInfo) - .ifPresent(u -> u.getXMLObjects().removeAll(u.getDisplayNames())); - } + attribute.getAttributeValues().add(xsBoolean); + return attribute; + } - if (!Strings.isNullOrEmpty(mduiRepresentation.getInformationUrl())) { - InformationURL informationURL = openSamlObjects.buildDefaultInstanceOfType(InformationURL.class); - getUIInfo(ed).addInformationURL(informationURL); - informationURL.setValue(mduiRepresentation.getInformationUrl()); - informationURL.setXMLLang("en"); - } else { - ed.getOptionalSPSSODescriptor() - .flatMap(SPSSODescriptor::getOptionalExtensions) - .flatMap(Extensions::getOptionalUIInfo) - .ifPresent(u -> u.getXMLObjects().removeAll(u.getInformationURLs())); - } + private Attribute createBaseAttribute(String name, String friendlyName) { + Attribute attribute = ((AttributeBuilder) openSamlObjects.getBuilderFactory().getBuilder(Attribute.DEFAULT_ELEMENT_NAME)).buildObject(); + attribute.setName(name); + attribute.setFriendlyName(friendlyName); + attribute.setNameFormat("urn:oasis:names:tc:SAML:2.0:attrname-format:uri"); - if (!Strings.isNullOrEmpty(mduiRepresentation.getPrivacyStatementUrl())) { - PrivacyStatementURL privacyStatementURL = openSamlObjects.buildDefaultInstanceOfType(PrivacyStatementURL.class); - getUIInfo(ed).addPrivacyStatementURL(privacyStatementURL); - privacyStatementURL.setValue(mduiRepresentation.getPrivacyStatementUrl()); - privacyStatementURL.setXMLLang("en"); - } else { - ed.getOptionalSPSSODescriptor() - .flatMap(SPSSODescriptor::getOptionalExtensions) - .flatMap(Extensions::getOptionalUIInfo) - .ifPresent(u -> u.getXMLObjects().removeAll(u.getPrivacyStatementURLs())); - } + return attribute; + } - if (!Strings.isNullOrEmpty(mduiRepresentation.getDescription())) { - Description description = openSamlObjects.buildDefaultInstanceOfType(Description.class); - getUIInfo(ed).addDescription(description); - description.setValue(mduiRepresentation.getDescription()); - description.setXMLLang("en"); - } else { - ed.getOptionalSPSSODescriptor() - .flatMap(SPSSODescriptor::getOptionalExtensions) - .flatMap(Extensions::getOptionalUIInfo) - .ifPresent(u -> u.getXMLObjects().removeAll(u.getDescriptions())); - } + @Override + public EntityDescriptor createDescriptorFromRepresentation(final EntityDescriptorRepresentation representation) { + EntityDescriptor ed = openSamlObjects.buildDefaultInstanceOfType(EntityDescriptor.class); - if (!Strings.isNullOrEmpty(mduiRepresentation.getLogoUrl())) { - Logo logo = openSamlObjects.buildDefaultInstanceOfType(Logo.class); - getUIInfo(ed).addLogo(logo); - logo.setURL(mduiRepresentation.getLogoUrl()); - logo.setHeight(mduiRepresentation.getLogoHeight()); - logo.setWidth(mduiRepresentation.getLogoWidth()); - logo.setXMLLang("en"); - } else { - ed.getOptionalSPSSODescriptor() - .flatMap(SPSSODescriptor::getOptionalExtensions) - .flatMap(Extensions::getOptionalUIInfo) - .ifPresent(u -> u.getXMLObjects().removeAll(u.getLogos())); - } - } else { - removeUIInfo(ed); - } + return buildDescriptorFromRepresentation(ed, representation); } - void setupContacts(EntityDescriptor ed, EntityDescriptorRepresentation representation) { - // set up contacts - if (representation.getContacts() != null && representation.getContacts().size() > 0) { - ed.getContactPersons().clear(); - for (ContactRepresentation contactRepresentation : representation.getContacts()) { - ContactPerson contactPerson = ((ContactPersonBuilder) openSamlObjects.getBuilderFactory().getBuilder(ContactPerson.DEFAULT_ELEMENT_NAME)).buildObject(); + KeyDescriptor createKeyDescriptor(String name, String type, String value) { + KeyDescriptor keyDescriptor = openSamlObjects.buildDefaultInstanceOfType(KeyDescriptor.class); - contactPerson.setType(contactRepresentation.getType()); + if (!Strings.isNullOrEmpty(name)) { + keyDescriptor.setName(name); + } - GivenName givenName = openSamlObjects.buildDefaultInstanceOfType(GivenName.class); - givenName.setName(contactRepresentation.getName()); - contactPerson.setGivenName(givenName); + if (!"both".equals(type)) { + keyDescriptor.setUsageType(type); + } - EmailAddress emailAddress = openSamlObjects.buildDefaultInstanceOfType(EmailAddress.class); - emailAddress.setAddress(contactRepresentation.getEmailAddress()); - contactPerson.addEmailAddress(emailAddress); + KeyInfo keyInfo = openSamlObjects.buildDefaultInstanceOfType(KeyInfo.class); + keyDescriptor.setKeyInfo(keyInfo); - ed.addContactPerson(contactPerson); - } - } else { - ed.getContactPersons().clear(); - } - } + X509Data x509Data = openSamlObjects.buildDefaultInstanceOfType(X509Data.class); + keyInfo.getXMLObjects().add(x509Data); - void setupOrganization(EntityDescriptor ed, EntityDescriptorRepresentation representation) { - // set up organization - if (representation.getOrganization() != null && representation.getOrganization().getName() != null && representation.getOrganization().getDisplayName() != null && representation.getOrganization().getUrl() != null) { - OrganizationRepresentation organizationRepresentation = representation.getOrganization(); - Organization organization = openSamlObjects.buildDefaultInstanceOfType(Organization.class); + X509Certificate x509Certificate = openSamlObjects.buildDefaultInstanceOfType(X509Certificate.class); + x509Data.getXMLObjects().add(x509Certificate); + x509Certificate.setValue(value); - OrganizationName organizationName = openSamlObjects.buildDefaultInstanceOfType(OrganizationName.class); - organizationName.setXMLLang("en"); - organizationName.setValue(organizationRepresentation.getName()); - organization.getOrganizationNames().add(organizationName); + return keyDescriptor; + } - OrganizationDisplayName organizationDisplayName = openSamlObjects.buildDefaultInstanceOfType(OrganizationDisplayName.class); - organizationDisplayName.setXMLLang("en"); - organizationDisplayName.setValue(organizationRepresentation.getDisplayName()); - organization.getDisplayNames().add(organizationDisplayName); + //TODO: implement + @Override + public EntityDescriptorRepresentation createRepresentationFromDescriptor(org.opensaml.saml.saml2.metadata.EntityDescriptor entityDescriptor) { + EntityDescriptor ed = (EntityDescriptor) entityDescriptor; + EntityDescriptorRepresentation representation = new EntityDescriptorRepresentation(); - OrganizationURL organizationURL = openSamlObjects.buildDefaultInstanceOfType(OrganizationURL.class); - organizationURL.setXMLLang("en"); - organizationURL.setValue(organizationRepresentation.getUrl()); - organization.getURLs().add(organizationURL); + representation.setId(ed.getResourceId()); + representation.setEntityId(ed.getEntityID()); + representation.setServiceProviderName(ed.getServiceProviderName()); + representation.setServiceEnabled(ed.isServiceEnabled()); + representation.setCreatedDate(ed.getCreatedDate()); + representation.setModifiedDate(ed.getModifiedDate()); + representation.setVersion(ed.hashCode()); + representation.setCreatedBy(ed.getCreatedBy()); + representation.setCurrent(ed.isCurrent()); + representation.setGroupId(ed.getGroup() != null ? ed.getGroup().getResourceId() : null); - ed.setOrganization(organization); - } else { - ed.setOrganization(null); - } - } - - void setupSPSSODescriptor(EntityDescriptor ed, EntityDescriptorRepresentation representation) { - // setup SPSSODescriptor - if (representation.getServiceProviderSsoDescriptor() != null) { - SPSSODescriptor spssoDescriptor = getSPSSODescriptorFromEntityDescriptor(ed); - - spssoDescriptor.setSupportedProtocols(Collections.EMPTY_LIST); - if (!Strings.isNullOrEmpty(representation.getServiceProviderSsoDescriptor().getProtocolSupportEnum())) { - spssoDescriptor.setSupportedProtocols( - Arrays.stream(representation.getServiceProviderSsoDescriptor().getProtocolSupportEnum().split(",")).map(p -> MDDCConstants.PROTOCOL_BINDINGS.get(p.trim())).collect(Collectors.toList()) - ); - } - - spssoDescriptor.getNameIDFormats().clear(); - if (representation.getServiceProviderSsoDescriptor() != null && representation.getServiceProviderSsoDescriptor().getNameIdFormats() != null && representation.getServiceProviderSsoDescriptor().getNameIdFormats().size() > 0) { - for (String nameidFormat : representation.getServiceProviderSsoDescriptor().getNameIdFormats()) { - NameIDFormat nameIDFormat = openSamlObjects.buildDefaultInstanceOfType(NameIDFormat.class); - - nameIDFormat.setFormat(nameidFormat); - - spssoDescriptor.getNameIDFormats().add(nameIDFormat); - } - } - } else { - ed.setRoleDescriptors(null); - } - } - - private SPSSODescriptor getSPSSODescriptorFromEntityDescriptor(EntityDescriptor entityDescriptor) { - return getSPSSODescriptorFromEntityDescriptor(entityDescriptor, true); - } - - private SPSSODescriptor getSPSSODescriptorFromEntityDescriptor(EntityDescriptor entityDescriptor, boolean create) { - if (entityDescriptor.getSPSSODescriptor("") == null && create) { - SPSSODescriptor spssoDescriptor = openSamlObjects.buildDefaultInstanceOfType(SPSSODescriptor.class); - entityDescriptor.getRoleDescriptors().add(spssoDescriptor); - } - return entityDescriptor.getSPSSODescriptor(""); - } - - private Attribute createBaseAttribute(String name, String friendlyName) { - Attribute attribute = ((AttributeBuilder) openSamlObjects.getBuilderFactory().getBuilder(Attribute.DEFAULT_ELEMENT_NAME)).buildObject(); - attribute.setName(name); - attribute.setFriendlyName(friendlyName); - attribute.setNameFormat("urn:oasis:names:tc:SAML:2.0:attrname-format:uri"); - - return attribute; - } - - private Attribute createAttributeWithBooleanValue(String name, String friendlyName, Boolean value) { - Attribute attribute = createBaseAttribute(name, friendlyName); - - XSBoolean xsBoolean = (XSBoolean) openSamlObjects.getBuilderFactory().getBuilder(XSBoolean.TYPE_NAME).buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSBoolean.TYPE_NAME); - xsBoolean.setValue(XSBooleanValue.valueOf(value.toString())); - - attribute.getAttributeValues().add(xsBoolean); - return attribute; - } - - private Attribute createAttributeWithArbitraryValues(String name, String friendlyName, String... values) { - Attribute attribute = createBaseAttribute(name, friendlyName); - - for (String value : values) { - XSAny xsAny = (XSAny) openSamlObjects.getBuilderFactory().getBuilder(XSAny.TYPE_NAME).buildObject(AttributeValue.DEFAULT_ELEMENT_NAME); - xsAny.setTextContent(value); - attribute.getAttributeValues().add(xsAny); - } - - return attribute; - } - - private Attribute createAttributeWithArbitraryValues(String name, String friendlyName, List values) { - return createAttributeWithArbitraryValues(name, friendlyName, values.toArray(new String[]{})); - } - - KeyDescriptor createKeyDescriptor(String name, String type, String value) { - KeyDescriptor keyDescriptor = openSamlObjects.buildDefaultInstanceOfType(KeyDescriptor.class); - - if (!Strings.isNullOrEmpty(name)) { - keyDescriptor.setName(name); - } - - if (!"both".equals(type)) { - keyDescriptor.setUsageType(type); - } - - KeyInfo keyInfo = openSamlObjects.buildDefaultInstanceOfType(KeyInfo.class); - keyDescriptor.setKeyInfo(keyInfo); - - X509Data x509Data = openSamlObjects.buildDefaultInstanceOfType(X509Data.class); - keyInfo.getXMLObjects().add(x509Data); - - X509Certificate x509Certificate = openSamlObjects.buildDefaultInstanceOfType(X509Certificate.class); - x509Data.getXMLObjects().add(x509Certificate); - x509Certificate.setValue(value); - - return keyDescriptor; - } - - private EntityAttributes getEntityAttributes(EntityDescriptor ed) { - return getEntityAttributes(ed, true); - } - - private Optional getOptionalEntityAttributes(EntityDescriptor ed) { - return Optional.ofNullable(getEntityAttributes(ed, false)); - } - - private EntityAttributes getEntityAttributes(EntityDescriptor ed, boolean create) { - Extensions extensions = ed.getExtensions(); - if (extensions == null && !create) { - return null; - } - if (extensions == null) { - extensions = openSamlObjects.buildDefaultInstanceOfType(Extensions.class); - ed.setExtensions(extensions); - } - - EntityAttributes entityAttributes = null; - if (extensions.getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME).size() > 0) { - entityAttributes = (EntityAttributes) extensions.getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME).get(0); - } else { - if (create) { - entityAttributes = ((EntityAttributesBuilder) openSamlObjects.getBuilderFactory().getBuilder(EntityAttributes.DEFAULT_ELEMENT_NAME)).buildObject(); - extensions.getUnknownXMLObjects().add(entityAttributes); - } - } - return entityAttributes; - } - - private UIInfo getUIInfo(EntityDescriptor ed) { - Extensions extensions = getSPSSODescriptorFromEntityDescriptor(ed).getExtensions(); - if (extensions == null) { - extensions = openSamlObjects.buildDefaultInstanceOfType(Extensions.class); - ed.getSPSSODescriptor("").setExtensions(extensions); - } - - UIInfo uiInfo; - if (extensions.getUnknownXMLObjects(UIInfo.DEFAULT_ELEMENT_NAME).size() > 0) { - uiInfo = (UIInfo) extensions.getUnknownXMLObjects(UIInfo.DEFAULT_ELEMENT_NAME).get(0); - } else { - uiInfo = openSamlObjects.buildDefaultInstanceOfType(UIInfo.class); - extensions.getUnknownXMLObjects().add(uiInfo); - } - return uiInfo; - } - - private void removeUIInfo(EntityDescriptor ed) { - SPSSODescriptor spssoDescriptor = getSPSSODescriptorFromEntityDescriptor(ed, false); - if (spssoDescriptor != null) { - Extensions extensions = spssoDescriptor.getExtensions(); - if (extensions == null) { - return; - } - if (extensions.getUnknownXMLObjects(UIInfo.DEFAULT_ELEMENT_NAME).size() > 0) { - extensions.getUnknownXMLObjects().remove(extensions.getUnknownXMLObjects(UIInfo.DEFAULT_ELEMENT_NAME).get(0)); - } - } - } - - //TODO: implement - @Override - public EntityDescriptorRepresentation createRepresentationFromDescriptor(org.opensaml.saml.saml2.metadata.EntityDescriptor entityDescriptor) { - EntityDescriptor ed = (EntityDescriptor) entityDescriptor; - EntityDescriptorRepresentation representation = new EntityDescriptorRepresentation(); - - representation.setId(ed.getResourceId()); - representation.setEntityId(ed.getEntityID()); - representation.setServiceProviderName(ed.getServiceProviderName()); - representation.setServiceEnabled(ed.isServiceEnabled()); - representation.setCreatedDate(ed.getCreatedDate()); - representation.setModifiedDate(ed.getModifiedDate()); - representation.setVersion(ed.hashCode()); - representation.setCreatedBy(ed.getCreatedBy()); - representation.setCurrent(ed.isCurrent()); - - if (ed.getSPSSODescriptor("") != null && ed.getSPSSODescriptor("").getSupportedProtocols().size() > 0) { - ServiceProviderSsoDescriptorRepresentation serviceProviderSsoDescriptorRepresentation = representation.getServiceProviderSsoDescriptor(true); - serviceProviderSsoDescriptorRepresentation.setProtocolSupportEnum(String.join(",", ed.getSPSSODescriptor("").getSupportedProtocols().stream().map(p -> MDDCConstants.PROTOCOL_BINDINGS.get(p)).collect(Collectors.toList()))); + if (ed.getSPSSODescriptor("") != null && ed.getSPSSODescriptor("").getSupportedProtocols().size() > 0) { + ServiceProviderSsoDescriptorRepresentation serviceProviderSsoDescriptorRepresentation = representation.getServiceProviderSsoDescriptor(true); + serviceProviderSsoDescriptorRepresentation.setProtocolSupportEnum(String.join(",", ed.getSPSSODescriptor("").getSupportedProtocols().stream().map(p -> MDDCConstants.PROTOCOL_BINDINGS.get(p)).collect(Collectors.toList()))); } if (ed.getSPSSODescriptor("") != null && ed.getSPSSODescriptor("").getNameIDFormats().size() > 0) { @@ -695,18 +399,343 @@ public EntityDescriptorRepresentation createRepresentationFromDescriptor(org.ope return representation; } - // TODO: remove - private String getValueFromXMLObject(XMLObject xmlObject) { - return ModelRepresentationConversions.getValueFromXMLObject(xmlObject); - } + @Override + public List getAllRepresentationsBasedOnUserAccess() { + switch (userService.getCurrentUserAccess()) { + case ADMIN: + return entityDescriptorRepository.findAllStreamByCustomQuery().map(ed -> createRepresentationFromDescriptor(ed)) + .collect(Collectors.toList()); + case GROUP: + User user = userService.getCurrentUser(); + Group group = user.getGroup(); + return entityDescriptorRepository + .findAllStreamByGroup_resourceIdOrCreatedBy(group == null ? null : group.getResourceId(), user.getUsername()) + .map(ed -> createRepresentationFromDescriptor(ed)).collect(Collectors.toList()); + case OWNER: + default: + return entityDescriptorRepository.findAllStreamByCreatedBy(userService.getCurrentUser().getUsername()) + .map(ed -> createRepresentationFromDescriptor(ed)).collect(Collectors.toList()); + } + } + @Override public List getAttributeReleaseListFromAttributeList(List attributeList) { return ModelRepresentationConversions.getAttributeReleaseListFromAttributeList(attributeList); } + private EntityAttributes getEntityAttributes(EntityDescriptor ed) { + return getEntityAttributes(ed, true); + } + + private EntityAttributes getEntityAttributes(EntityDescriptor ed, boolean create) { + Extensions extensions = ed.getExtensions(); + if (extensions == null && !create) { + return null; + } + if (extensions == null) { + extensions = openSamlObjects.buildDefaultInstanceOfType(Extensions.class); + ed.setExtensions(extensions); + } + + EntityAttributes entityAttributes = null; + if (extensions.getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME).size() > 0) { + entityAttributes = (EntityAttributes) extensions.getUnknownXMLObjects(EntityAttributes.DEFAULT_ELEMENT_NAME).get(0); + } else { + if (create) { + entityAttributes = ((EntityAttributesBuilder) openSamlObjects.getBuilderFactory().getBuilder(EntityAttributes.DEFAULT_ELEMENT_NAME)).buildObject(); + extensions.getUnknownXMLObjects().add(entityAttributes); + } + } + return entityAttributes; + } + + private Optional getOptionalEntityAttributes(EntityDescriptor ed) { + return Optional.ofNullable(getEntityAttributes(ed, false)); + } + @Override public Map getRelyingPartyOverridesRepresentationFromAttributeList(List attributeList) { return ModelRepresentationConversions.getRelyingPartyOverridesRepresentationFromAttributeList(attributeList); } + + private SPSSODescriptor getSPSSODescriptorFromEntityDescriptor(EntityDescriptor entityDescriptor) { + return getSPSSODescriptorFromEntityDescriptor(entityDescriptor, true); + } + + private SPSSODescriptor getSPSSODescriptorFromEntityDescriptor(EntityDescriptor entityDescriptor, boolean create) { + if (entityDescriptor.getSPSSODescriptor("") == null && create) { + SPSSODescriptor spssoDescriptor = openSamlObjects.buildDefaultInstanceOfType(SPSSODescriptor.class); + entityDescriptor.getRoleDescriptors().add(spssoDescriptor); + } + return entityDescriptor.getSPSSODescriptor(""); + } + + private UIInfo getUIInfo(EntityDescriptor ed) { + Extensions extensions = getSPSSODescriptorFromEntityDescriptor(ed).getExtensions(); + if (extensions == null) { + extensions = openSamlObjects.buildDefaultInstanceOfType(Extensions.class); + ed.getSPSSODescriptor("").setExtensions(extensions); + } + + UIInfo uiInfo; + if (extensions.getUnknownXMLObjects(UIInfo.DEFAULT_ELEMENT_NAME).size() > 0) { + uiInfo = (UIInfo) extensions.getUnknownXMLObjects(UIInfo.DEFAULT_ELEMENT_NAME).get(0); + } else { + uiInfo = openSamlObjects.buildDefaultInstanceOfType(UIInfo.class); + extensions.getUnknownXMLObjects().add(uiInfo); + } + return uiInfo; + } + + // TODO: remove + private String getValueFromXMLObject(XMLObject xmlObject) { + return ModelRepresentationConversions.getValueFromXMLObject(xmlObject); + } + + private void removeUIInfo(EntityDescriptor ed) { + SPSSODescriptor spssoDescriptor = getSPSSODescriptorFromEntityDescriptor(ed, false); + if (spssoDescriptor != null) { + Extensions extensions = spssoDescriptor.getExtensions(); + if (extensions == null) { + return; + } + if (extensions.getUnknownXMLObjects(UIInfo.DEFAULT_ELEMENT_NAME).size() > 0) { + extensions.getUnknownXMLObjects().remove(extensions.getUnknownXMLObjects(UIInfo.DEFAULT_ELEMENT_NAME).get(0)); + } + } + } + + void setupACSs(EntityDescriptor ed, EntityDescriptorRepresentation representation) { + // setup ACSs + if (representation.getAssertionConsumerServices() != null && representation.getAssertionConsumerServices().size() > 0) { + // TODO: review if we need more than a naive implementation + ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getAssertionConsumerServices().clear()); + for (AssertionConsumerServiceRepresentation acsRepresentation : representation.getAssertionConsumerServices()) { + AssertionConsumerService assertionConsumerService = openSamlObjects.buildDefaultInstanceOfType(AssertionConsumerService.class); + getSPSSODescriptorFromEntityDescriptor(ed).getAssertionConsumerServices().add(assertionConsumerService); + if (acsRepresentation.isMakeDefault()) { + assertionConsumerService.setIsDefault(true); + } + assertionConsumerService.setBinding(acsRepresentation.getBinding()); + assertionConsumerService.setLocation(acsRepresentation.getLocationUrl()); + assertionConsumerService.setIndex(acsRepresentation.getIndex()); + } + } else { + ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getAssertionConsumerServices().clear()); + } + } + + void setupContacts(EntityDescriptor ed, EntityDescriptorRepresentation representation) { + // set up contacts + if (representation.getContacts() != null && representation.getContacts().size() > 0) { + ed.getContactPersons().clear(); + for (ContactRepresentation contactRepresentation : representation.getContacts()) { + ContactPerson contactPerson = ((ContactPersonBuilder) openSamlObjects.getBuilderFactory().getBuilder(ContactPerson.DEFAULT_ELEMENT_NAME)).buildObject(); + + contactPerson.setType(contactRepresentation.getType()); + + GivenName givenName = openSamlObjects.buildDefaultInstanceOfType(GivenName.class); + givenName.setName(contactRepresentation.getName()); + contactPerson.setGivenName(givenName); + + EmailAddress emailAddress = openSamlObjects.buildDefaultInstanceOfType(EmailAddress.class); + emailAddress.setAddress(contactRepresentation.getEmailAddress()); + contactPerson.addEmailAddress(emailAddress); + + ed.addContactPerson(contactPerson); + } + } else { + ed.getContactPersons().clear(); + } + } + + void setupLogout(EntityDescriptor ed, EntityDescriptorRepresentation representation) { + // setup logout + if (representation.getLogoutEndpoints() != null && !representation.getLogoutEndpoints().isEmpty()) { + // TODO: review if we need more than a naive implementation + ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getSingleLogoutServices().clear()); + for (LogoutEndpointRepresentation logoutEndpointRepresentation : representation.getLogoutEndpoints()) { + SingleLogoutService singleLogoutService = openSamlObjects.buildDefaultInstanceOfType(SingleLogoutService.class); + singleLogoutService.setBinding(logoutEndpointRepresentation.getBindingType()); + singleLogoutService.setLocation(logoutEndpointRepresentation.getUrl()); + + getSPSSODescriptorFromEntityDescriptor(ed).getSingleLogoutServices().add(singleLogoutService); + } + } else { + ed.getOptionalSPSSODescriptor().ifPresent(spssoDescriptor -> spssoDescriptor.getSingleLogoutServices().clear()); + } + } + + void setupOrganization(EntityDescriptor ed, EntityDescriptorRepresentation representation) { + // set up organization + if (representation.getOrganization() != null && representation.getOrganization().getName() != null && representation.getOrganization().getDisplayName() != null && representation.getOrganization().getUrl() != null) { + OrganizationRepresentation organizationRepresentation = representation.getOrganization(); + Organization organization = openSamlObjects.buildDefaultInstanceOfType(Organization.class); + + OrganizationName organizationName = openSamlObjects.buildDefaultInstanceOfType(OrganizationName.class); + organizationName.setXMLLang("en"); + organizationName.setValue(organizationRepresentation.getName()); + organization.getOrganizationNames().add(organizationName); + + OrganizationDisplayName organizationDisplayName = openSamlObjects.buildDefaultInstanceOfType(OrganizationDisplayName.class); + organizationDisplayName.setXMLLang("en"); + organizationDisplayName.setValue(organizationRepresentation.getDisplayName()); + organization.getDisplayNames().add(organizationDisplayName); + + OrganizationURL organizationURL = openSamlObjects.buildDefaultInstanceOfType(OrganizationURL.class); + organizationURL.setXMLLang("en"); + organizationURL.setValue(organizationRepresentation.getUrl()); + organization.getURLs().add(organizationURL); + + ed.setOrganization(organization); + } else { + ed.setOrganization(null); + } + } + + void setupRelyingPartyOverrides(EntityDescriptor ed, EntityDescriptorRepresentation representation) { + if (representation.getRelyingPartyOverrides() != null || (representation.getAttributeRelease() != null && representation.getAttributeRelease().size() > 0)) { + // TODO: review if we need more than a naive implementation + getOptionalEntityAttributes(ed).ifPresent(entityAttributes -> entityAttributes.getAttributes().clear()); + getEntityAttributes(ed).getAttributes().addAll(entityService.getAttributeListFromEntityRepresentation(representation)); + } else { + getOptionalEntityAttributes(ed).ifPresent(entityAttributes -> entityAttributes.getAttributes().clear()); + } + } + + void setupSecurity(EntityDescriptor ed, EntityDescriptorRepresentation representation) { + // setup security + if (representation.getSecurityInfo() != null) { + SecurityInfoRepresentation securityInfoRepresentation = representation.getSecurityInfo(); + if (securityInfoRepresentation.isAuthenticationRequestsSigned()) { + getSPSSODescriptorFromEntityDescriptor(ed).setAuthnRequestsSigned(true); + } + if (securityInfoRepresentation.isWantAssertionsSigned()) { + getSPSSODescriptorFromEntityDescriptor(ed).setWantAssertionsSigned(true); + } + // TODO: review if we need more than a naive implementation + ed.getOptionalSPSSODescriptor().ifPresent( i -> i.getKeyDescriptors().clear()); + if (securityInfoRepresentation.isX509CertificateAvailable()) { + for (SecurityInfoRepresentation.X509CertificateRepresentation x509CertificateRepresentation : securityInfoRepresentation.getX509Certificates()) { + KeyDescriptor keyDescriptor = createKeyDescriptor(x509CertificateRepresentation.getName(), x509CertificateRepresentation.getType(), x509CertificateRepresentation.getValue()); + getSPSSODescriptorFromEntityDescriptor(ed).addKeyDescriptor(keyDescriptor); + } + } + } else { + ed.getOptionalSPSSODescriptor().ifPresent( spssoDescriptor -> { + spssoDescriptor.setAuthnRequestsSigned((Boolean) null); + spssoDescriptor.setWantAssertionsSigned((Boolean) null); + spssoDescriptor.getKeyDescriptors().clear(); + }); + } + } + + void setupSPSSODescriptor(EntityDescriptor ed, EntityDescriptorRepresentation representation) { + // setup SPSSODescriptor + if (representation.getServiceProviderSsoDescriptor() != null) { + SPSSODescriptor spssoDescriptor = getSPSSODescriptorFromEntityDescriptor(ed); + + spssoDescriptor.setSupportedProtocols(Collections.EMPTY_LIST); + if (!Strings.isNullOrEmpty(representation.getServiceProviderSsoDescriptor().getProtocolSupportEnum())) { + spssoDescriptor.setSupportedProtocols( + Arrays.stream(representation.getServiceProviderSsoDescriptor().getProtocolSupportEnum().split(",")).map(p -> MDDCConstants.PROTOCOL_BINDINGS.get(p.trim())).collect(Collectors.toList()) + ); + } + + spssoDescriptor.getNameIDFormats().clear(); + if (representation.getServiceProviderSsoDescriptor() != null && representation.getServiceProviderSsoDescriptor().getNameIdFormats() != null && representation.getServiceProviderSsoDescriptor().getNameIdFormats().size() > 0) { + for (String nameidFormat : representation.getServiceProviderSsoDescriptor().getNameIdFormats()) { + NameIDFormat nameIDFormat = openSamlObjects.buildDefaultInstanceOfType(NameIDFormat.class); + + nameIDFormat.setFormat(nameidFormat); + + spssoDescriptor.getNameIDFormats().add(nameIDFormat); + } + } + } else { + ed.setRoleDescriptors(null); + } + } + + void setupUIInfo(EntityDescriptor ed, EntityDescriptorRepresentation representation) { + // set up mdui + if (representation.getMdui() != null) { + // TODO: check if we need more than a naive implementation + removeUIInfo(ed); + MduiRepresentation mduiRepresentation = representation.getMdui(); + + if (!Strings.isNullOrEmpty(mduiRepresentation.getDisplayName())) { + DisplayName displayName = openSamlObjects.buildDefaultInstanceOfType(DisplayName.class); + getUIInfo(ed).addDisplayName(displayName); + displayName.setValue(mduiRepresentation.getDisplayName()); + displayName.setXMLLang("en"); + } else { + ed.getOptionalSPSSODescriptor() + .flatMap(SPSSODescriptor::getOptionalExtensions) + .flatMap(Extensions::getOptionalUIInfo) + .ifPresent(u -> u.getXMLObjects().removeAll(u.getDisplayNames())); + } + + if (!Strings.isNullOrEmpty(mduiRepresentation.getInformationUrl())) { + InformationURL informationURL = openSamlObjects.buildDefaultInstanceOfType(InformationURL.class); + getUIInfo(ed).addInformationURL(informationURL); + informationURL.setValue(mduiRepresentation.getInformationUrl()); + informationURL.setXMLLang("en"); + } else { + ed.getOptionalSPSSODescriptor() + .flatMap(SPSSODescriptor::getOptionalExtensions) + .flatMap(Extensions::getOptionalUIInfo) + .ifPresent(u -> u.getXMLObjects().removeAll(u.getInformationURLs())); + } + + if (!Strings.isNullOrEmpty(mduiRepresentation.getPrivacyStatementUrl())) { + PrivacyStatementURL privacyStatementURL = openSamlObjects.buildDefaultInstanceOfType(PrivacyStatementURL.class); + getUIInfo(ed).addPrivacyStatementURL(privacyStatementURL); + privacyStatementURL.setValue(mduiRepresentation.getPrivacyStatementUrl()); + privacyStatementURL.setXMLLang("en"); + } else { + ed.getOptionalSPSSODescriptor() + .flatMap(SPSSODescriptor::getOptionalExtensions) + .flatMap(Extensions::getOptionalUIInfo) + .ifPresent(u -> u.getXMLObjects().removeAll(u.getPrivacyStatementURLs())); + } + + if (!Strings.isNullOrEmpty(mduiRepresentation.getDescription())) { + Description description = openSamlObjects.buildDefaultInstanceOfType(Description.class); + getUIInfo(ed).addDescription(description); + description.setValue(mduiRepresentation.getDescription()); + description.setXMLLang("en"); + } else { + ed.getOptionalSPSSODescriptor() + .flatMap(SPSSODescriptor::getOptionalExtensions) + .flatMap(Extensions::getOptionalUIInfo) + .ifPresent(u -> u.getXMLObjects().removeAll(u.getDescriptions())); + } + + if (!Strings.isNullOrEmpty(mduiRepresentation.getLogoUrl())) { + Logo logo = openSamlObjects.buildDefaultInstanceOfType(Logo.class); + getUIInfo(ed).addLogo(logo); + logo.setURL(mduiRepresentation.getLogoUrl()); + logo.setHeight(mduiRepresentation.getLogoHeight()); + logo.setWidth(mduiRepresentation.getLogoWidth()); + logo.setXMLLang("en"); + } else { + ed.getOptionalSPSSODescriptor() + .flatMap(SPSSODescriptor::getOptionalExtensions) + .flatMap(Extensions::getOptionalUIInfo) + .ifPresent(u -> u.getXMLObjects().removeAll(u.getLogos())); + } + } else { + removeUIInfo(ed); + } + } + + @Override + public void updateDescriptorFromRepresentation(org.opensaml.saml.saml2.metadata.EntityDescriptor entityDescriptor, EntityDescriptorRepresentation representation) { + if (!(entityDescriptor instanceof EntityDescriptor)) { + throw new UnsupportedOperationException("not yet implemented"); + } + buildDescriptorFromRepresentation((EntityDescriptor) entityDescriptor, representation); + } } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy index 7f12a050d..ba73cb0d4 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy @@ -5,6 +5,8 @@ import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects import edu.internet2.tier.shibboleth.admin.ui.repository.CustomEntityAttributeDefinitionRepository import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository import edu.internet2.tier.shibboleth.admin.ui.security.DefaultAuditorAware +import edu.internet2.tier.shibboleth.admin.ui.security.repository.GroupsRepository +import edu.internet2.tier.shibboleth.admin.ui.security.service.GroupServiceImpl import edu.internet2.tier.shibboleth.admin.ui.service.CustomEntityAttributesDefinitionServiceImpl import edu.internet2.tier.shibboleth.admin.ui.service.IndexWriterService import net.shibboleth.ext.spring.resource.ResourceHelper @@ -118,4 +120,12 @@ class TestConfiguration { AuditorAware defaultAuditorAware() { return new DefaultAuditorAware() } + + @Bean + GroupServiceImpl groupService(GroupsRepository repo) { + new GroupServiceImpl().with { + it.repo = repo + return it + } + } } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntitiesControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntitiesControllerTests.groovy index 8f262b6a1..0a0c4806e 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntitiesControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntitiesControllerTests.groovy @@ -124,9 +124,10 @@ class EntitiesControllerTests extends Specification { "createdDate":null, "modifiedDate":null, "attributeRelease":["givenName","employeeNumber"], - "version":-1891841119, + "version":1445248649, "createdBy":null, - "current":false + "current":false, + "groupId":null } ''' @@ -168,9 +169,10 @@ class EntitiesControllerTests extends Specification { "createdDate":null, "modifiedDate":null, "attributeRelease":["givenName","employeeNumber"], - "version":-1891841119, + "version":1445248649, "createdBy":null, - "current":false + "current":false, + "groupId":null } ''' 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 75ef8dd3e..08f42f537 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 @@ -10,6 +10,7 @@ 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.repository.RoleRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository +import edu.internet2.tier.shibboleth.admin.ui.security.service.IGroupService import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorVersionService import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityDescriptorServiceImpl @@ -71,6 +72,8 @@ class EntityDescriptorControllerTests extends Specification { UserService userService EntityDescriptorVersionService versionService = Mock() + + IGroupService groupService = Mock() def setup() { generator = new TestObjectGenerator() @@ -79,6 +82,8 @@ class EntityDescriptorControllerTests extends Specification { userService = new UserService(roleRepository, userRepository) service = new JPAEntityDescriptorServiceImpl(openSamlObjects, new JPAEntityServiceImpl(openSamlObjects), userService) + service.entityDescriptorRepository = entityDescriptorRepository + service.groupService = groupService controller = new EntityDescriptorController(userService, versionService) controller.entityDescriptorRepository = entityDescriptorRepository @@ -124,6 +129,7 @@ class EntityDescriptorControllerTests extends Specification { def role = 'ROLE_ADMIN' authentication.getName() >> username userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) + groupService.find(null) >> null //"groupId": null def expectedCreationDate = '2017-10-23T11:11:11' def entityDescriptor = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true, createdDate: LocalDateTime.parse(expectedCreationDate)) @@ -146,7 +152,8 @@ class EntityDescriptorControllerTests extends Specification { "assertionConsumerServices": null, "version": $version, "createdBy": null, - "current": false + "current": false, + "groupId": null } ] """ @@ -173,6 +180,7 @@ class EntityDescriptorControllerTests extends Specification { def role = 'ROLE_ADMIN' authentication.getName() >> username userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) + groupService.find(null) >> null //"groupId": null def expectedCreationDate = '2017-10-23T11:11:11' def entityDescriptorOne = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true, @@ -200,7 +208,8 @@ class EntityDescriptorControllerTests extends Specification { "assertionConsumerServices": null, "version": $versionOne, "createdBy": null, - "current": false + "current": false, + "groupId": null }, { "id": "uuid-2", @@ -217,7 +226,8 @@ class EntityDescriptorControllerTests extends Specification { "assertionConsumerServices": null, "version": $versionTwo, "createdBy": null, - "current": false + "current": false, + "groupId": null } ] """ @@ -245,6 +255,7 @@ class EntityDescriptorControllerTests extends Specification { def role = 'ROLE_USER' authentication.getName() >> username userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) + groupService.find(null) >> null def expectedCreationDate = '2017-10-23T11:11:11' def entityDescriptorOne = new EntityDescriptor(resourceId: 'uuid-1', entityID: 'eid1', serviceProviderName: 'sp1', serviceEnabled: true, @@ -269,7 +280,8 @@ class EntityDescriptorControllerTests extends Specification { "assertionConsumerServices": null, "version": $versionOne, "createdBy": "someUser", - "current": false + "current": false, + "groupId": null } ] """ @@ -295,6 +307,7 @@ class EntityDescriptorControllerTests extends Specification { def role = 'ROLE_ADMIN' authentication.getName() >> username userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) + groupService.find(null) >> null def expectedCreationDate = '2017-10-23T11:11:11' def expectedEntityId = 'https://shib' def expectedSpName = 'sp1' @@ -339,7 +352,8 @@ class EntityDescriptorControllerTests extends Specification { "assertionConsumerServices": null, "version": $version, "createdBy": null, - "current": false + "current": false, + "groupId": null } """ @@ -468,6 +482,7 @@ class EntityDescriptorControllerTests extends Specification { def role = 'ROLE_ADMIN' authentication.getName() >> username userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) + groupService.find(null) >> null def expectedCreationDate = '2017-10-23T11:11:11' def providedResourceId = 'uuid-1' def expectedSpName = 'sp1' @@ -494,7 +509,8 @@ class EntityDescriptorControllerTests extends Specification { "assertionConsumerServices": null, "version": $version, "createdBy": null, - "current": false + "current": false, + "groupId": null } """ @@ -518,6 +534,7 @@ class EntityDescriptorControllerTests extends Specification { def role = 'ROLE_USER' authentication.getName() >> username userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) + groupService.find(null) >> null def expectedCreationDate = '2017-10-23T11:11:11' def providedResourceId = 'uuid-1' def expectedSpName = 'sp1' @@ -545,7 +562,8 @@ class EntityDescriptorControllerTests extends Specification { "assertionConsumerServices": null, "version": $version, "createdBy": "someUser", - "current": false + "current": false, + "groupId": null } """ @@ -899,6 +917,7 @@ class EntityDescriptorControllerTests extends Specification { def role = 'ROLE_ADMIN' authentication.getName() >> username userRepository.findByUsername(username) >> TestHelpers.generateOptionalUser(username, role) + groupService.find(null) >> null def entityDescriptor = generator.buildEntityDescriptor() def updatedEntityDescriptor = generator.buildEntityDescriptor() updatedEntityDescriptor.resourceId = entityDescriptor.resourceId diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptorTest.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptorTest.groovy index b0906a68c..ffc92431a 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptorTest.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptorTest.groovy @@ -10,6 +10,8 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ReloadableMetadat import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.opensaml.OpenSamlChainingMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.opensaml.OpenSamlFileBackedHTTPMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects +import edu.internet2.tier.shibboleth.admin.ui.security.repository.GroupsRepository +import edu.internet2.tier.shibboleth.admin.ui.security.service.GroupServiceImpl import edu.internet2.tier.shibboleth.admin.ui.service.IndexWriterService import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator @@ -92,5 +94,13 @@ class EntityDescriptorTest extends Specification { metadataResolver.initialize() return metadataResolver } + + @Bean + GroupServiceImpl groupService(GroupsRepository repo) { + new GroupServiceImpl().with { + it.repo = repo + return it + } + } } } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepositoryTest.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepositoryTest.groovy index 70331ffe6..d72a0705f 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepositoryTest.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepositoryTest.groovy @@ -5,8 +5,11 @@ import edu.internet2.tier.shibboleth.admin.ui.configuration.Internationalization import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.opensaml.OpenSamlChainingMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects +import edu.internet2.tier.shibboleth.admin.ui.security.model.Group +import edu.internet2.tier.shibboleth.admin.ui.security.repository.GroupsRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository +import edu.internet2.tier.shibboleth.admin.ui.security.service.GroupServiceImpl import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService import edu.internet2.tier.shibboleth.admin.ui.service.CustomEntityAttributesDefinitionServiceImpl import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityDescriptorServiceImpl @@ -24,6 +27,8 @@ import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ContextConfiguration import spock.lang.Specification +import java.util.stream.Stream + import javax.persistence.EntityManager /** @@ -49,6 +54,9 @@ class EntityDescriptorRepositoryTest extends Specification { @Autowired UserRepository userRepository + + @Autowired + GroupsRepository groupRepository OpenSamlObjects openSamlObjects = new OpenSamlObjects().with { it.init() @@ -60,7 +68,6 @@ class EntityDescriptorRepositoryTest extends Specification { def "SHIBUI-553.2"() { when: def input = openSamlObjects.unmarshalFromXml(this.class.getResource('/metadata/SHIBUI-553.2.xml').bytes) as EntityDescriptor - entityDescriptorRepository.save(input) def item1 = entityDescriptorRepository.findByResourceId(input.resourceId) @@ -88,6 +95,46 @@ class EntityDescriptorRepositoryTest extends Specification { then: noExceptionThrown() } + + def "SHIBUI-1849 - extend data model for group ownership"() { + given: + def group = new Group().with { + it.name = "group-name" + it.description = "some description" + it + } + groupRepository.save(group) + entityManager.flush() + entityManager.clear() + def gList = groupRepository.findAll() + def groupFromDb = gList.get(0).asType(Group) + + def ed = openSamlObjects.unmarshalFromXml(this.class.getResource('/metadata/SHIBUI-553.2.xml').bytes) as EntityDescriptor + ed.with { + it.group = groupFromDb + } + entityDescriptorRepository.save(ed) + entityManager.flush() + entityManager.clear() + + when: + def edStreamFromDb = entityDescriptorRepository.findAllStreamByGroup_resourceIdOrCreatedBy(null, "whocares"); + + then: + ((Stream)edStreamFromDb).count() == 0 + + when: + def edStreamFromDb2 = entityDescriptorRepository.findAllStreamByGroup_resourceIdOrCreatedBy("random value", "whocares"); + + then: + ((Stream)edStreamFromDb2).count() == 0 + + when: + def edStreamFromDb3 = entityDescriptorRepository.findAllStreamByGroup_resourceIdOrCreatedBy(groupFromDb.resourceId, "whocares"); + + then: + ((Stream)edStreamFromDb3).count() == 1 + } @TestConfiguration static class LocalConfig { @@ -105,6 +152,14 @@ class EntityDescriptorRepositoryTest extends Specification { return new EnglishAnalyzer() } + @Bean + GroupServiceImpl groupService(GroupsRepository repo) { + new GroupServiceImpl().with { + it.repo = repo + return it + } + } + @Bean CustomEntityAttributesDefinitionServiceImpl customEntityAttributesDefinitionServiceImpl() { new CustomEntityAttributesDefinitionServiceImpl().with { diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy index f47928082..7402cb098 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/scheduled/EntityDescriptorFilesScheduledTasksTests.groovy @@ -10,6 +10,7 @@ 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.repository.RoleRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository +import edu.internet2.tier.shibboleth.admin.ui.security.service.IGroupService import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService import edu.internet2.tier.shibboleth.admin.ui.service.FileCheckingFileWritingService import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityDescriptorServiceImpl @@ -54,10 +55,14 @@ class EntityDescriptorFilesScheduledTasksTests extends Specification { @Autowired UserRepository userRepository + @Autowired + IGroupService groupService + def setup() { randomGenerator = new RandomGenerator() tempPath = tempPath + randomGenerator.randomRangeInt(10000, 20000) service = new JPAEntityDescriptorServiceImpl(openSamlObjects, new JPAEntityServiceImpl(openSamlObjects), new UserService(roleRepository, userRepository)) + service.groupService = groupService entityDescriptorFilesScheduledTasks = new EntityDescriptorFilesScheduledTasks(tempPath, entityDescriptorRepository, openSamlObjects, new FileCheckingFileWritingService()) directory = new File(tempPath) directory.mkdir() diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/controller/GroupsControllerIntegrationTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/controller/GroupsControllerIntegrationTests.groovy index 3b514c065..a1199bf21 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/controller/GroupsControllerIntegrationTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/controller/GroupsControllerIntegrationTests.groovy @@ -24,6 +24,8 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import javax.persistence.EntityManager + @SpringBootTest @AutoConfigureMockMvc @ActiveProfiles(["no-auth", "dev"]) @@ -32,6 +34,9 @@ class GroupsControllerIntegrationTests extends Specification { @Autowired private MockMvc mockMvc + @Autowired + EntityManager entityManager + static RESOURCE_URI = '/api/admin/groups' static USERS_RESOURCE_URI = '/api/admin/users' @@ -147,35 +152,7 @@ class GroupsControllerIntegrationTests extends Specification { then: 'The group not found' nonexistentGroupRequest.andExpect(status().isNotFound()) } - -// @Rollback -// @WithMockUser(value = "admin", roles = ["ADMIN"]) -// def 'DELETE ONE existing group'() { -// when: 'GET request for a single specific group in a system that has groups' -// def result = mockMvc.perform(get("$RESOURCE_URI/BBB")) -// -// then: 'GET request for a single specific group completed with HTTP 200' -// result.andExpect(status().isOk()) -// -// when: 'DELETE request is made' -// result = mockMvc.perform(delete("$RESOURCE_URI/BBB")) -// -// then: 'DELETE was successful' -// result.andExpect(status().isNoContent()) -// -// when: 'GET request for a single specific group just deleted' -// result = mockMvc.perform(get("$RESOURCE_URI/BBB")) -// -// then: 'The group not found' -// result.andExpect(status().isNotFound()) -// -// when: 'DELETE request for a single specific group that does not exist' -// result = mockMvc.perform(delete("$RESOURCE_URI/CCCC")) -// -// then: 'The group not found' -// result.andExpect(status().isNotFound()) -// } - + @Rollback @WithMockUser(value = "admin", roles = ["ADMIN"]) def 'DELETE performs correctly when group attached to a user'() { @@ -218,6 +195,8 @@ class GroupsControllerIntegrationTests extends Specification { when: 'DELETE request is made' + entityManager.flush() + entityManager.clear() result = mockMvc.perform(delete("$RESOURCE_URI/$group.resourceId")) then: 'DELETE was not successful' diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/repository/GroupsRepositoryTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/repository/GroupsRepositoryTests.groovy index 20fcc4f8a..a5c8f07f5 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/repository/GroupsRepositoryTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/repository/GroupsRepositoryTests.groovy @@ -139,5 +139,11 @@ class GroupsRepositoryTests extends Specification { then: repo.findAll().size() == 0 + + when: + def nothingThere = repo.findByResourceId(null); + + then: + nothingThere == null } } \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy index 31cec75b2..4ee9db41e 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IncommonJPAMetadataResolverServiceImplTests.groovy @@ -11,6 +11,8 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.filters.RequiredValidUntilF import edu.internet2.tier.shibboleth.admin.ui.domain.filters.SignatureValidationFilter 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.repository.GroupsRepository +import edu.internet2.tier.shibboleth.admin.ui.security.service.GroupServiceImpl import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator import edu.internet2.tier.shibboleth.admin.util.AttributeUtility import groovy.xml.XmlUtil @@ -147,5 +149,13 @@ class IncommonJPAMetadataResolverServiceImplTests extends Specification { return resolver } + + @Bean + GroupServiceImpl groupService(GroupsRepository repo) { + new GroupServiceImpl().with { + it.repo = repo + return it + } + } } } \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy index 3d05ae598..f00625e1a 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImplTests.groovy @@ -22,6 +22,7 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.ServiceProviderSso import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository +import edu.internet2.tier.shibboleth.admin.ui.security.service.IGroupService import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator @@ -68,9 +69,13 @@ class JPAEntityDescriptorServiceImplTests extends Specification { @Autowired UserRepository userRepository + @Autowired + IGroupService groupService + def setup() { service = new JPAEntityDescriptorServiceImpl(openSamlObjects, new JPAEntityServiceImpl(openSamlObjects, new AttributeUtility(openSamlObjects), customPropertiesConfiguration), new UserService(roleRepository, userRepository)) + service.groupService = groupService mapper = new ObjectMapper() JacksonTester.initFields(this, mapper) generator = new RandomGenerator() diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy index 3ab2154ea..717d0432f 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImplTests.groovy @@ -21,6 +21,8 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.TemplateScheme import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.opensaml.OpenSamlChainingMetadataResolver 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.repository.GroupsRepository +import edu.internet2.tier.shibboleth.admin.ui.security.service.GroupServiceImpl import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator import edu.internet2.tier.shibboleth.admin.util.AttributeUtility import edu.internet2.tier.shibboleth.admin.util.TokenPlaceholderResolvers @@ -490,5 +492,13 @@ class JPAMetadataResolverServiceImplTests extends Specification { it } } + + @Bean + GroupServiceImpl groupService(GroupsRepository repo) { + new GroupServiceImpl().with { + it.repo = repo + return it + } + } } }