From b0f5a899bd02701d3663978b505e2fe3a482330f Mon Sep 17 00:00:00 2001 From: chasegawa Date: Fri, 13 Aug 2021 08:52:16 -0700 Subject: [PATCH] SHIBUI-1742 merged from 1740 code (master) --- .../JPAMetadataResolverServiceImpl.groovy | 4 +- .../admin/ui/domain/ActivatableType.java | 5 +++ .../admin/ui/domain/EntityDescriptor.java | 23 +++++++--- .../admin/ui/domain/IActivatable.java | 7 +++ .../ui/domain/filters/MetadataFilter.java | 24 +++++++--- .../ui/domain/resolvers/MetadataResolver.java | 44 +++++++++++-------- .../ui/security/service/UserService.java | 37 +++++++++++----- .../JPAEntityDescriptorServiceImpl.java | 4 +- .../ui/service/JPAFilterServiceImpl.java | 13 +++--- 9 files changed, 110 insertions(+), 51 deletions(-) create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ActivatableType.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/IActivatable.java diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy index 365125395..abd4ec060 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy @@ -559,8 +559,8 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { } public edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver updateMetadataResolverEnabledStatus(edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver updatedResolver) throws ForbiddenException, MetadataFileNotFoundException, InitializationException { - // @TODO: when merged with groups, this should maybe be merged with group check as they have to have the role in the right group - if (!userService.currentUserHasExpectedRole(["ROLE_ADMIN", "ROLE_ENABLE"])) { + if (!userService.currentUserCanEnable(updatedResolver)) { +// if (!userService.currentUserHasExpectedRole(["ROLE_ADMIN", "ROLE_ENABLE"])) { throw new ForbiddenException("You do not have the permissions necessary to change the enable status of this filter.") } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ActivatableType.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ActivatableType.java new file mode 100644 index 000000000..8042d56d4 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ActivatableType.java @@ -0,0 +1,5 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain; + +public enum ActivatableType { + ENTITY_DESCRIPTOR, METADATA_RESOLVER, FILTER +} \ No newline at end of file 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 a3acff06b..185b43918 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 @@ -33,11 +33,12 @@ import java.util.UUID; import java.util.stream.Collectors; +import static edu.internet2.tier.shibboleth.admin.ui.domain.ActivatableType.ENTITY_DESCRIPTOR; @Entity @EqualsAndHashCode(callSuper = true) @Audited -public class EntityDescriptor extends AbstractDescriptor implements org.opensaml.saml.saml2.metadata.EntityDescriptor, Ownable { +public class EntityDescriptor extends AbstractDescriptor implements org.opensaml.saml.saml2.metadata.EntityDescriptor, Ownable, IActivatable { @OneToMany(cascade = CascadeType.ALL) @JoinColumn(name = "entitydesc_addlmetdatlocations_id") @OrderColumn @@ -47,7 +48,7 @@ public class EntityDescriptor extends AbstractDescriptor implements org.opensaml @OneToOne(cascade = CascadeType.ALL) @NotAudited private AffiliationDescriptor affiliationDescriptor; - + @OneToOne(cascade = CascadeType.ALL) @NotAudited private AttributeAuthorityDescriptor attributeAuthorityDescriptor; @@ -61,7 +62,7 @@ public class EntityDescriptor extends AbstractDescriptor implements org.opensaml private List contactPersons = new ArrayList<>(); private String entityID; - + private String localId; @OneToOne(cascade = CascadeType.ALL) @@ -70,7 +71,7 @@ public class EntityDescriptor extends AbstractDescriptor implements org.opensaml @Getter @Setter private String idOfOwner; - + @OneToOne(cascade = CascadeType.ALL) @NotAudited private PDPDescriptor pdpDescriptor; @@ -254,6 +255,10 @@ public void setEntityID(String entityID) { this.entityID = entityID; } + public void setEnabled(Boolean serviceEnabled) { + this.serviceEnabled = (serviceEnabled == null) ? false : serviceEnabled; + } + @Override public void setID(String id) { this.localId = id; @@ -296,12 +301,16 @@ public String toString() { .add("id", id) .toString(); } - + public String getObjectId() { return entityID; } - + public OwnableType getOwnableType() { return OwnableType.ENTITY_DESCRIPTOR; } -} + + @Override public ActivatableType getActivatableType() { + return ENTITY_DESCRIPTOR; + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/IActivatable.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/IActivatable.java new file mode 100644 index 000000000..84a31078a --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/IActivatable.java @@ -0,0 +1,7 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain; + +public interface IActivatable { + ActivatableType getActivatableType(); + + void setEnabled(Boolean enabled); +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java index 9363f5711..33b763231 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/MetadataFilter.java @@ -5,6 +5,8 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; +import edu.internet2.tier.shibboleth.admin.ui.domain.ActivatableType; +import edu.internet2.tier.shibboleth.admin.ui.domain.IActivatable; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; @@ -20,6 +22,8 @@ import javax.persistence.Transient; import java.util.UUID; +import static edu.internet2.tier.shibboleth.admin.ui.domain.ActivatableType.*; + /** * Domain class to store information about {@link org.opensaml.saml.metadata.resolver.filter.MetadataFilter} */ @@ -38,22 +42,26 @@ @JsonSubTypes.Type(value=NameIdFormatFilter.class, name="NameIDFormat")}) @Audited @AuditOverride(forClass = AbstractAuditable.class) -public abstract class MetadataFilter extends AbstractAuditable implements IConcreteMetadataFilterType { +public abstract class MetadataFilter extends AbstractAuditable implements IConcreteMetadataFilterType, IActivatable { - @JsonProperty("@type") - @Transient - String type; + private boolean filterEnabled; private String name; @Column(unique=true) private String resourceId = UUID.randomUUID().toString(); - private boolean filterEnabled; + @JsonProperty("@type") + @Transient + String type; @Transient private transient Integer version; + public ActivatableType getActivatableType() { + return FILTER; + } + @JsonGetter("version") public int getVersion() { if (version != null && version != 0) { @@ -61,4 +69,8 @@ public int getVersion() { } return this.hashCode(); } -} + + public void setEnabled(Boolean serviceEnabled) { + this.filterEnabled = (serviceEnabled == null) ? false : serviceEnabled; + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java index 13d573b56..a777aa3ca 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java @@ -6,6 +6,8 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; +import edu.internet2.tier.shibboleth.admin.ui.domain.ActivatableType; +import edu.internet2.tier.shibboleth.admin.ui.domain.IActivatable; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter; import lombok.EqualsAndHashCode; @@ -28,6 +30,8 @@ import java.util.List; import java.util.UUID; +import static edu.internet2.tier.shibboleth.admin.ui.domain.ActivatableType.*; + @Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) @EqualsAndHashCode(callSuper = true, exclude = {"version", "versionModifiedTimestamp"}) @@ -43,7 +47,7 @@ @JsonSubTypes.Type(value = ResourceBackedMetadataResolver.class, name = "ResourceBackedMetadataResolver")}) @Audited @AuditOverride(forClass = AbstractAuditable.class) -public class MetadataResolver extends AbstractAuditable { +public class MetadataResolver extends AbstractAuditable implements IActivatable { @JsonProperty("@type") @Transient @@ -84,31 +88,35 @@ public class MetadataResolver extends AbstractAuditable { @Transient private Integer version; - @JsonGetter("version") - public int getVersion() { - if (this.version != null && this.version != 0 ) { - return this.version; - } - return this.hashCode(); - } - public void addFilter(MetadataFilter metadataFilter) { //To make sure that Spring Data auditing infrastructure recognizes update and "touched" modifiedDate markAsModified(); this.metadataFilters.add(metadataFilter); } - public void markAsModified() { - this.versionModifiedTimestamp = System.currentTimeMillis(); - } - public void entityAttributesFilterIntoTransientRepresentation() { //expose explicit API to call to convert into transient representation //used in unit/integration tests where JPA's @PostLoad callback execution engine is not available this.metadataFilters - .stream() - .filter(EntityAttributesFilter.class::isInstance) - .map(EntityAttributesFilter.class::cast) - .forEach(EntityAttributesFilter::intoTransientRepresentation); + .stream() + .filter(EntityAttributesFilter.class::isInstance) + .map(EntityAttributesFilter.class::cast) + .forEach(EntityAttributesFilter::intoTransientRepresentation); + } + + @Override public ActivatableType getActivatableType() { + return METADATA_RESOLVER; + } + + @JsonGetter("version") + public int getVersion() { + if (this.version != null && this.version != 0 ) { + return this.version; + } + return this.hashCode(); + } + + public void markAsModified() { + this.versionModifiedTimestamp = System.currentTimeMillis(); } -} +} \ No newline at end of file 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 2081f02f5..4c1bdc4c2 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 @@ -1,10 +1,10 @@ package edu.internet2.tier.shibboleth.admin.ui.security.service; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; +import java.util.*; +import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor; +import edu.internet2.tier.shibboleth.admin.ui.domain.IActivatable; +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.context.SecurityContextHolder; @@ -25,6 +25,8 @@ import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository; import lombok.NoArgsConstructor; +import static edu.internet2.tier.shibboleth.admin.ui.security.service.UserAccess.*; + @Service @NoArgsConstructor public class UserService { @@ -47,10 +49,25 @@ public UserService(IGroupService groupService, OwnershipRepository ownershipRepo this.userRepository = userRepository; } + public boolean currentUserCanEnable(IActivatable activatableObject) { + switch (activatableObject.getActivatableType()) { + case ENTITY_DESCRIPTOR: { + if (getCurrentUserAccess() == ADMIN) { return true; } + if (currentUserHasExpectedRole(Arrays.asList("ROLE_ENABLE" )) && getCurrentUserGroup().getOwnerId().equals(((EntityDescriptor) activatableObject).getIdOfOwner())) { + return true; + } + } + case FILTER: + case METADATA_RESOLVER: + return currentUserHasExpectedRole(Arrays.asList("ROLE_ADMIN", "ROLE_ENABLE" )); + } + return false; + } + /** - * Current logic is pretty dumb, this will need to change/expand once a user can have more than one role. + * This basic logic assumes users only have a single role (despite users having a list of roles, we assume only 1 currently) */ - public boolean currentUserHasExpectedRole(List acceptedRoles) { + private boolean currentUserHasExpectedRole(List acceptedRoles) { User user = getCurrentUser(); return acceptedRoles.contains(user.getRole()); } @@ -91,15 +108,15 @@ public User getCurrentUser() { public UserAccess getCurrentUserAccess() { User user = getCurrentUser(); if (user == null) { - return UserAccess.NONE; + return NONE; } if (user.getRole().equals("ROLE_ADMIN")) { - return UserAccess.ADMIN; + return ADMIN; } if (user.getRole().equals("ROLE_USER")) { - return UserAccess.GROUP; + return GROUP; } - return UserAccess.NONE; + return NONE; } public Group getCurrentUserGroup() { 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 7752eafa2..668f2636b 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 @@ -391,8 +391,8 @@ public EntityDescriptorRepresentation updateEntityDescriptorEnabledStatus(String if (ed == null) { throw new EntityNotFoundException("Entity with resourceid[" + resourceId + "] was not found for update"); } - // @TODO: when merged with groups, this should maybe be merged with group check as they have to have the role in the right group - if (!userService.currentUserHasExpectedRole(Arrays.asList(new String[] { "ROLE_ADMIN", "ROLE_ENABLE" }))) { + if (!userService.currentUserCanEnable(ed)) { +// if (!userService.currentUserHasExpectedRole(Arrays.asList(new String[] { "ROLE_ADMIN", "ROLE_ENABLE" }))) { throw new ForbiddenException("You do not have the permissions necessary to change the enable status of this entity descriptor."); } ed.setServiceEnabled(status); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImpl.java index 23a70268a..c42bd7cad 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImpl.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.service; +import edu.internet2.tier.shibboleth.admin.ui.domain.IActivatable; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.FilterRepresentation; @@ -117,13 +118,13 @@ public MetadataFilter updateFilterEnabledStatus(String metadataResolverId, Strin if (filterTobeUpdatedOptional.isEmpty()) { throw new EntityNotFoundException("Filter with resource id[" + resourceId + "] not found"); } - - // @TODO: when merged with groups, this should maybe be merged with group check as they have to have the role in the right group - if (!userService.currentUserHasExpectedRole(Arrays.asList(new String[] { "ROLE_ADMIN", "ROLE_ENABLE" }))) { + + MetadataFilter filterTobeUpdated = filterTobeUpdatedOptional.get(); + + if (!userService.currentUserCanEnable(filterTobeUpdated)) { throw new ForbiddenException("You do not have the permissions necessary to change the enable status of this filter."); } - - MetadataFilter filterTobeUpdated = filterTobeUpdatedOptional.get(); + filterTobeUpdated.setFilterEnabled(status); MetadataFilter persistedFilter = filterRepository.save(filterTobeUpdated); @@ -136,4 +137,4 @@ public MetadataFilter updateFilterEnabledStatus(String metadataResolverId, Strin return persistedFilter; } -} +} \ No newline at end of file