diff --git a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerVersionEndpointsIntegrationTests.groovy b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerVersionEndpointsIntegrationTests.groovy index 705f50021..99b5810ce 100644 --- a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerVersionEndpointsIntegrationTests.groovy +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerVersionEndpointsIntegrationTests.groovy @@ -36,7 +36,7 @@ class EntityDescriptorControllerVersionEndpointsIntegrationTests extends Specifi result.statusCodeValue == 404 } - def "GET /api/EntityDescriptor{resourceId}/Versions with 1 entity descriptor version"() { + def "GET /api/EntityDescriptor/{resourceId}/Versions with 1 entity descriptor version"() { given: EntityDescriptor ed = new EntityDescriptor(entityID: 'http://test/controller', createdBy: 'anonymousUser') entityDescriptorRepository.save(ed) @@ -50,7 +50,7 @@ class EntityDescriptorControllerVersionEndpointsIntegrationTests extends Specifi result.body[0].id && result.body[0].creator && result.body[0].date } - def "GET /api/EntityDescriptor{resourceId}/Versions with 2 entity descriptor versions"() { + def "GET /api/EntityDescriptor/{resourceId}/Versions with 2 entity descriptor versions"() { given: EntityDescriptor ed = new EntityDescriptor(entityID: 'http://test/controller', createdBy: 'anonymousUser') ed = entityDescriptorRepository.save(ed) @@ -68,7 +68,7 @@ class EntityDescriptorControllerVersionEndpointsIntegrationTests extends Specifi result.body[0].date < result.body[1].date } - def "GET /api/EntityDescriptor{resourceId}/Versions/{version} for non existent version"() { + def "GET /api/EntityDescriptor/{resourceId}/Versions/{version} for non existent version"() { given: EntityDescriptor ed = new EntityDescriptor(entityID: 'http://test/controller', createdBy: 'anonymousUser') ed = entityDescriptorRepository.save(ed) diff --git a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/service/envers/EnversEntityDescriptorVersionServiceTests.groovy b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/service/envers/EnversEntityDescriptorVersionServiceTests.groovy index ac5a4d129..0289be502 100644 --- a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/service/envers/EnversEntityDescriptorVersionServiceTests.groovy +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/service/envers/EnversEntityDescriptorVersionServiceTests.groovy @@ -13,12 +13,12 @@ import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorVersionSer import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import org.springframework.data.jpa.repository.config.EnableJpaAuditing import org.springframework.data.jpa.repository.config.EnableJpaRepositories import org.springframework.test.context.ContextConfiguration import org.springframework.transaction.PlatformTransactionManager import spock.lang.Specification -import java.time.LocalDateTime import java.time.ZonedDateTime @@ -26,6 +26,7 @@ import java.time.ZonedDateTime @ContextConfiguration(classes = [CoreShibUiConfiguration, InternationalizationConfiguration, TestConfiguration, SearchConfiguration, EntitiesVersioningConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") +@EnableJpaAuditing class EnversEntityDescriptorVersionServiceTests extends Specification { @Autowired diff --git a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/service/envers/EnversMetadataResolverVersionServiceTests.groovy b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/service/envers/EnversMetadataResolverVersionServiceTests.groovy index 8f68922ed..97b37f10e 100644 --- a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/service/envers/EnversMetadataResolverVersionServiceTests.groovy +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/service/envers/EnversMetadataResolverVersionServiceTests.groovy @@ -6,7 +6,6 @@ import edu.internet2.tier.shibboleth.admin.ui.configuration.EntitiesVersioningCo import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration -import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FilesystemMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver @@ -18,12 +17,12 @@ import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverVersionSer import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import org.springframework.data.jpa.repository.config.EnableJpaAuditing import org.springframework.data.jpa.repository.config.EnableJpaRepositories import org.springframework.test.context.ContextConfiguration import org.springframework.transaction.PlatformTransactionManager import spock.lang.Specification -import java.time.LocalDateTime import java.time.ZonedDateTime @@ -31,6 +30,7 @@ import java.time.ZonedDateTime @ContextConfiguration(classes = [CoreShibUiConfiguration, InternationalizationConfiguration, TestConfiguration, SearchConfiguration, EntitiesVersioningConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") +@EnableJpaAuditing class EnversMetadataResolverVersionServiceTests extends Specification { @Autowired diff --git a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/service/envers/EnversVersioningMetadataTests.groovy b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/service/envers/EnversVersioningMetadataTests.groovy new file mode 100644 index 000000000..9eed8e2bc --- /dev/null +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/service/envers/EnversVersioningMetadataTests.groovy @@ -0,0 +1,127 @@ +package edu.internet2.tier.shibboleth.admin.ui.service.envers + +import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.EntitiesVersioningConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration +import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository +import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository +import edu.internet2.tier.shibboleth.admin.ui.repository.envers.EnversTestsSupport +import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorVersionService +import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverVersionService +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.autoconfigure.domain.EntityScan +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import org.springframework.data.jpa.repository.config.EnableJpaAuditing +import org.springframework.data.jpa.repository.config.EnableJpaRepositories +import org.springframework.test.context.ContextConfiguration +import org.springframework.transaction.PlatformTransactionManager +import spock.lang.Specification + +@DataJpaTest +@ContextConfiguration(classes = [CoreShibUiConfiguration, InternationalizationConfiguration, TestConfiguration, SearchConfiguration, EntitiesVersioningConfiguration]) +@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) +@EntityScan("edu.internet2.tier.shibboleth.admin.ui") +@EnableJpaAuditing +class EnversVersioningMetadataTests extends Specification { + + @Autowired + MetadataResolverVersionService metadataResolverVersionService + + @Autowired + MetadataResolverRepository metadataResolverRepository + + @Autowired + EntityDescriptorVersionService entityDescriptorVersionService + + @Autowired + EntityDescriptorRepository entityDescriptorRepository + + @Autowired + PlatformTransactionManager txMgr + + def "versioning service uses versioning metadata from target entities enhanced by boot auditing facility"() { + when: 'Initial versions' + MetadataResolver mr = new LocalDynamicMetadataResolver(name: 'resolver') + EntityDescriptor ed = new EntityDescriptor(serviceProviderName: 'descriptor') + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + ed = EnversTestsSupport.doInExplicitTransaction(txMgr) { + entityDescriptorRepository.save(ed) + } + def mrVersions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + def edVersions = entityDescriptorVersionService.findVersionsForEntityDescriptor(ed.resourceId) + + then: + mrVersions[0].creator == mr.createdBy + mrVersions[0].date == mr.createdDateAsZonedDateTime() + edVersions[0].creator == ed.createdBy + edVersions[0].date == ed.createdDateAsZonedDateTime() + + when: 'new version due to update' + mr.name = 'UPDATED' + ed.serviceProviderName = 'UPDATED' + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + ed = EnversTestsSupport.doInExplicitTransaction(txMgr) { + entityDescriptorRepository.save(ed) + } + mrVersions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + edVersions = entityDescriptorVersionService.findVersionsForEntityDescriptor(ed.resourceId) + + then: + mrVersions[1].creator == mr.modifiedBy + mrVersions[1].date == mr.modifiedDateAsZonedDateTime() + edVersions[1].creator == ed.modifiedBy + edVersions[1].date == ed.modifiedDateAsZonedDateTime() + } + + def "test current version correct logic"() { + when: 'Initial versions' + MetadataResolver mr = new DynamicHttpMetadataResolver(name: 'resolver') + EntityDescriptor ed = new EntityDescriptor(serviceProviderName: 'descriptor') + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + ed = EnversTestsSupport.doInExplicitTransaction(txMgr) { + entityDescriptorRepository.save(ed) + } + def mrVersions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + def edVersions = entityDescriptorVersionService.findVersionsForEntityDescriptor(ed.resourceId) + def mrV1 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, mrVersions[0].id) + def edV1 = entityDescriptorVersionService.findSpecificVersionOfEntityDescriptor(ed.resourceId, edVersions[0].id) + + then: + mrV1.isCurrent() + edV1.isCurrent() + + when: 'new version due to update' + mr.name = 'UPDATED' + ed.serviceProviderName = 'UPDATED' + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + ed = EnversTestsSupport.doInExplicitTransaction(txMgr) { + entityDescriptorRepository.save(ed) + } + mrVersions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + edVersions = entityDescriptorVersionService.findVersionsForEntityDescriptor(ed.resourceId) + mrV1 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, mrVersions[0].id) + edV1 = entityDescriptorVersionService.findSpecificVersionOfEntityDescriptor(ed.resourceId, edVersions[0].id) + def mrV2 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, mrVersions[1].id) + def edV2 = entityDescriptorVersionService.findSpecificVersionOfEntityDescriptor(ed.resourceId, edVersions[1].id) + + then: + !mrV1.isCurrent() + !edV1.isCurrent() + mrV2.isCurrent() + edV2.isCurrent() + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/EntitiesVersioningConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/EntitiesVersioningConfiguration.java index ec21d42c4..9341a45c2 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/EntitiesVersioningConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/EntitiesVersioningConfiguration.java @@ -20,7 +20,7 @@ public class EntitiesVersioningConfiguration { @Bean public EntityDescriptorVersionService entityDescriptorVersionService(EntityDescriptorService entityDescriptorService) { - return new EnversEntityDescriptorVersionService(entityManager, entityDescriptorService); + return new EnversEntityDescriptorVersionService(enversVersionServiceSupport(), entityDescriptorService); } @Bean diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractAuditable.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractAuditable.java index 567a6637e..3d2ed1391 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractAuditable.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractAuditable.java @@ -1,5 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.EqualsAndHashCode; import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; @@ -16,13 +18,19 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.MappedSuperclass; +import javax.persistence.Transient; import javax.validation.constraints.NotNull; +import java.time.Instant; import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; @MappedSuperclass @EntityListeners(AuditingEntityListener.class) -@EqualsAndHashCode +@EqualsAndHashCode(exclude = {"current"}) @Audited public abstract class AbstractAuditable implements Auditable { @@ -48,6 +56,9 @@ public abstract class AbstractAuditable implements Auditable { @LastModifiedBy private String modifiedBy; + @Transient + @JsonProperty + private boolean current; @Override public Long getAudId() { @@ -94,4 +105,28 @@ public String getModifiedBy() { public void setModifiedBy(String modifiedBy) { this.modifiedBy = modifiedBy; } + + public ZonedDateTime createdDateAsZonedDateTime() { + return toZonedDateTime(this.createdDate); + } + + public ZonedDateTime modifiedDateAsZonedDateTime() { + return toZonedDateTime(this.modifiedDate); + } + + public boolean isCurrent() { + return this.current; + } + + public void markAsCurrent() { + this.current = true; + } + + private static ZonedDateTime toZonedDateTime(LocalDateTime localDateTime) { + return localDateTime + .atZone(ZoneId.systemDefault()) + .toInstant() + .atOffset(ZoneOffset.UTC) + .toZonedDateTime(); + } } 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 e378f5fbb..44f1463c5 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 @@ -1,5 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain.frontend; +import com.fasterxml.jackson.annotation.JsonProperty; + import javax.validation.constraints.NotNull; import java.io.Serializable; import java.time.LocalDateTime; @@ -64,6 +66,9 @@ public EntityDescriptorRepresentation(String id, private String createdBy; + @JsonProperty + private boolean current; + public String getId() { return id; } @@ -112,6 +117,7 @@ public void setMdui(MduiRepresentation mdui) { this.mdui = mdui; } + public ServiceProviderSsoDescriptorRepresentation getServiceProviderSsoDescriptor() { return this.getServiceProviderSsoDescriptor(false); } @@ -213,4 +219,12 @@ public String getCreatedBy() { public void setCreatedBy(String createdBy) { this.createdBy = createdBy; } + + public boolean isCurrent() { + return current; + } + + public void setCurrent(boolean current) { + this.current = current; + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/envers/EnversVersionServiceSupport.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/envers/EnversVersionServiceSupport.java index 964ede86a..bbf399227 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/envers/EnversVersionServiceSupport.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/envers/EnversVersionServiceSupport.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.envers; +import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; import edu.internet2.tier.shibboleth.admin.ui.domain.versioning.Version; import org.hibernate.envers.AuditReaderFactory; import org.hibernate.envers.query.AuditEntity; @@ -31,14 +32,10 @@ public List findVersionsForPersistentEntity(String resourceId, Class .getResultList(); Object listOfVersions = revs.stream() - .map(it -> ((Object[]) it)[1]) .map(it -> { - return new Version(((PrincipalAwareRevisionEntity) it).idAsString(), - ((PrincipalAwareRevisionEntity) it).getPrincipalUserName(), - ((PrincipalAwareRevisionEntity) it).getRevisionDate() - .toInstant() - .atOffset(ZoneOffset.UTC) - .toZonedDateTime()); + return new Version(((PrincipalAwareRevisionEntity) ((Object[]) it)[1]).idAsString(), + ((AbstractAuditable) ((Object[]) it)[0]).getModifiedBy(), + ((AbstractAuditable) ((Object[]) it)[0]).modifiedDateAsZonedDateTime()); }) .sorted(comparing(Version::getDate)) .collect(toList()); @@ -48,14 +45,30 @@ public List findVersionsForPersistentEntity(String resourceId, Class public Object findSpecificVersionOfPersistentEntity(String resourceId, String versionId, Class entityClass) { try { - return AuditReaderFactory.get(entityManager).createQuery() + AbstractAuditable abstractAuditable = + (AbstractAuditable) AuditReaderFactory.get(entityManager).createQuery() .forEntitiesAtRevision(entityClass, Integer.valueOf(versionId)) .add(AuditEntity.property("resourceId").eq(resourceId)) .add(AuditEntity.revisionNumber().eq(Integer.valueOf(versionId))) .getSingleResult(); - } - catch (NoResultException e) { + if(isCurrentRevision(resourceId, versionId, entityClass)) { + abstractAuditable.markAsCurrent(); + } + return abstractAuditable; + } catch (NoResultException e) { return null; } } + + private boolean isCurrentRevision(String resourceId, String versionId, Class entityClass) { + Number revision = (Number) AuditReaderFactory + .get(entityManager) + .createQuery() + .forRevisionsOfEntity(entityClass, false, false) + .addProjection(AuditEntity.revisionNumber().max()) + .add(AuditEntity.property("resourceId").eq(resourceId)) + .getSingleResult(); + + return Integer.valueOf(versionId) == revision.intValue(); + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/envers/PrincipalAwareRevisionEntity.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/envers/PrincipalAwareRevisionEntity.java index 6a2847b38..e0aef8807 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/envers/PrincipalAwareRevisionEntity.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/envers/PrincipalAwareRevisionEntity.java @@ -2,7 +2,6 @@ import lombok.Getter; import lombok.Setter; -import org.hibernate.envers.DefaultRevisionEntity; import org.hibernate.envers.DefaultTrackingModifiedEntitiesRevisionEntity; import org.hibernate.envers.RevisionEntity; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/DefaultAuditorAware.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/DefaultAuditorAware.java index 080b4312d..9ec1a7eb7 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/DefaultAuditorAware.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/DefaultAuditorAware.java @@ -3,7 +3,6 @@ import org.springframework.data.domain.AuditorAware; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.User; import java.util.Optional; @@ -16,12 +15,14 @@ */ public class DefaultAuditorAware implements AuditorAware { + private static final String ANONYMOUS = "anonymousUser"; + @Override public Optional getCurrentAuditor() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null || !authentication.isAuthenticated()) { - return Optional.empty(); + return Optional.of(ANONYMOUS); } - return Optional.of(User.class.cast(authentication.getPrincipal()).getUsername()); + return Optional.of(authentication.getName()); } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EnversEntityDescriptorVersionService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EnversEntityDescriptorVersionService.java index f21d9d227..2bfced24d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EnversEntityDescriptorVersionService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EnversEntityDescriptorVersionService.java @@ -3,77 +3,32 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation; import edu.internet2.tier.shibboleth.admin.ui.domain.versioning.Version; -import edu.internet2.tier.shibboleth.admin.ui.envers.PrincipalAwareRevisionEntity; -import org.hibernate.envers.AuditReader; -import org.hibernate.envers.AuditReaderFactory; -import org.hibernate.envers.RevisionEntity; -import org.hibernate.envers.query.AuditEntity; -import org.springframework.data.jpa.repository.JpaContext; +import edu.internet2.tier.shibboleth.admin.ui.envers.EnversVersionServiceSupport; -import javax.persistence.EntityManager; -import javax.persistence.NoResultException; -import javax.persistence.PersistenceContext; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.util.Arrays; -import java.util.Comparator; import java.util.List; -import java.util.stream.Collectors; - -import static java.util.Comparator.comparing; -import static java.util.stream.Collectors.toList; /** - * Hibernate Envers based implementation of {@link EntityDescriptorVersionService}. - * TODO: refactor this implementation using EnversVersionServiceSupport class + * Hibernate Envers based implementation of {@link EntityDescriptorVersionService}.* */ public class EnversEntityDescriptorVersionService implements EntityDescriptorVersionService { - private EntityManager entityManager; + private EnversVersionServiceSupport enversVersionServiceSupport; private EntityDescriptorService entityDescriptorService; - public EnversEntityDescriptorVersionService(EntityManager entityManager, EntityDescriptorService entityDescriptorService) { - this.entityManager = entityManager; + public EnversEntityDescriptorVersionService(EnversVersionServiceSupport enversVersionServiceSupport, EntityDescriptorService entityDescriptorService) { + this.enversVersionServiceSupport = enversVersionServiceSupport; this.entityDescriptorService = entityDescriptorService; } @Override public List findVersionsForEntityDescriptor(String resourceId) { - List revs = AuditReaderFactory.get(entityManager).createQuery() - .forRevisionsOfEntity(EntityDescriptor.class, false, false) - .add(AuditEntity.property("resourceId").eq(resourceId)) - .getResultList(); - - Object listOfVersions = revs.stream() - .map(it -> ((Object[])it)[1]) - .map(it -> { - return new Version(((PrincipalAwareRevisionEntity) it).idAsString(), - ((PrincipalAwareRevisionEntity) it).getPrincipalUserName(), - ((PrincipalAwareRevisionEntity) it).getRevisionDate() - .toInstant() - .atOffset(ZoneOffset.UTC) - .toZonedDateTime()); - }) - .sorted(comparing(Version::getDate)) - .collect(toList()); - - return (List)listOfVersions; + return enversVersionServiceSupport.findVersionsForPersistentEntity(resourceId, EntityDescriptor.class); } @Override public EntityDescriptorRepresentation findSpecificVersionOfEntityDescriptor(String resourceId, String versionId) { - try { - Object revision = AuditReaderFactory.get(entityManager).createQuery() - .forEntitiesAtRevision(EntityDescriptor.class, Integer.valueOf(versionId)) - .add(AuditEntity.property("resourceId").eq(resourceId)) - .add(AuditEntity.revisionNumber().eq(Integer.valueOf(versionId))) - .getSingleResult(); - return entityDescriptorService.createRepresentationFromDescriptor((EntityDescriptor) revision); - } - catch (NoResultException e) { - return null; - } + Object edObject = enversVersionServiceSupport.findSpecificVersionOfPersistentEntity(resourceId, versionId, EntityDescriptor.class); + return edObject == null ? null : entityDescriptorService.createRepresentationFromDescriptor((EntityDescriptor) edObject); } } 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 ca7a8659e..7be97f308 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 @@ -497,6 +497,7 @@ public EntityDescriptorRepresentation createRepresentationFromDescriptor(org.ope 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); 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 a01645a09..676547ffd 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 @@ -3,6 +3,7 @@ package edu.internet2.tier.shibboleth.admin.ui.configuration 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.DefaultAuditorAware import edu.internet2.tier.shibboleth.admin.ui.service.IndexWriterService import net.shibboleth.ext.spring.resource.ResourceHelper import net.shibboleth.utilities.java.support.component.ComponentInitializationException @@ -20,6 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.core.io.ClassPathResource +import org.springframework.data.domain.AuditorAware import org.springframework.mail.javamail.JavaMailSender import org.springframework.mail.javamail.JavaMailSenderImpl @@ -87,4 +89,9 @@ class TestConfiguration { metadataResolver.initialize() return metadataResolver } + + @Bean + AuditorAware defaultAuditorAware() { + return new DefaultAuditorAware() + } } 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 89cb89d54..bf3778fbb 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 @@ -147,7 +147,8 @@ class EntityDescriptorControllerTests extends Specification { "relyingPartyOverrides": null, "attributeRelease": null, "version": $version, - "createdBy": null + "createdBy": null, + "current": false } ] """ @@ -202,7 +203,8 @@ class EntityDescriptorControllerTests extends Specification { "relyingPartyOverrides": null, "attributeRelease": null, "version": $versionOne, - "createdBy": null + "createdBy": null, + "current": false }, { "id": "uuid-2", @@ -221,7 +223,8 @@ class EntityDescriptorControllerTests extends Specification { "relyingPartyOverrides": null, "attributeRelease": null, "version": $versionTwo, - "createdBy": null + "createdBy": null, + "current": false } ] """ @@ -273,7 +276,8 @@ class EntityDescriptorControllerTests extends Specification { "relyingPartyOverrides": null, "attributeRelease": null, "version": $versionOne, - "createdBy": "someUser" + "createdBy": "someUser", + "current": false } ] """ @@ -325,7 +329,8 @@ class EntityDescriptorControllerTests extends Specification { "securityInfo": null, "assertionConsumerServices": null, "relyingPartyOverrides": null, - "attributeRelease": null + "attributeRelease": null, + "current": false } """ @@ -348,7 +353,8 @@ class EntityDescriptorControllerTests extends Specification { "relyingPartyOverrides": null, "attributeRelease": null, "version": $version, - "createdBy": null + "createdBy": null, + "current": false } """ @@ -504,7 +510,8 @@ class EntityDescriptorControllerTests extends Specification { "relyingPartyOverrides": null, "attributeRelease": null, "version": $version, - "createdBy": null + "createdBy": null, + "current": false } """ @@ -556,7 +563,8 @@ class EntityDescriptorControllerTests extends Specification { "relyingPartyOverrides": null, "attributeRelease": null, "version": $version, - "createdBy": "someUser" + "createdBy": "someUser", + "current": false } """ diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/versioning/VersionJsonSerializationBasicTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/versioning/VersionJsonSerializationBasicTests.groovy index 011fe7033..b7fecdf4d 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/versioning/VersionJsonSerializationBasicTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/versioning/VersionJsonSerializationBasicTests.groovy @@ -27,7 +27,7 @@ class VersionJsonSerializationBasicTests extends Specification { { "id": "2", "creator": "kramer", - "date": "2019-05-20T15:00:00.574Z" + "date": "2019-05-20T15:00:00.574Z" } """ def expectedJsonMap = jsonSlurper.parseText(expectedJson) @@ -37,5 +37,7 @@ class VersionJsonSerializationBasicTests extends Specification { then: deSerializedJsonMap.date == expectedJsonMap.date + deSerializedJsonMap.id == expectedJsonMap.id + deSerializedJsonMap.creator == expectedJsonMap.creator } } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/controller/UsersControllerIntegrationTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/controller/UsersControllerIntegrationTests.groovy index 0e96fc1b7..ab60461a0 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/controller/UsersControllerIntegrationTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/controller/UsersControllerIntegrationTests.groovy @@ -39,30 +39,30 @@ class UsersControllerIntegrationTests extends Specification { def expectedJson = """ [ { - "modifiedBy" : null, + "modifiedBy" : anonymousUser, "firstName" : "Joe", "emailAddress" : "joe@institution.edu", "role" : "ROLE_ADMIN", "username" : "admin", - "createdBy" : null, + "createdBy" : anonymousUser, "lastName" : "Doe" }, { - "modifiedBy" : null, + "modifiedBy" : anonymousUser, "firstName" : "Peter", "emailAddress" : "peter@institution.edu", "role" : "ROLE_USER", "username" : "nonadmin", - "createdBy" : null, + "createdBy" : anonymousUser, "lastName" : "Vandelay" }, { - "modifiedBy" : null, + "modifiedBy" : anonymousUser, "firstName" : "Anon", "emailAddress" : "anon@institution.edu", "role" : "ROLE_ADMIN", "username" : "anonymousUser", - "createdBy" : null, + "createdBy" : anonymousUser, "lastName" : "Ymous" } ]""" @@ -83,12 +83,12 @@ class UsersControllerIntegrationTests extends Specification { given: def expectedJson = """ { - "modifiedBy" : null, + "modifiedBy" : anonymousUser, "firstName" : "Joe", "emailAddress" : "joe@institution.edu", "role" : "ROLE_ADMIN", "username" : "admin", - "createdBy" : null, + "createdBy" : anonymousUser, "lastName" : "Doe" }""" when: 'GET request is made for one existing user' @@ -229,4 +229,4 @@ class UsersControllerIntegrationTests extends Specification { then: result.andExpect(status().isNotFound()) } -} \ No newline at end of file +} diff --git a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/Pac4jAuditorAware.java b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/Pac4jAuditorAware.java index 0fe287c7e..47332d43b 100644 --- a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/Pac4jAuditorAware.java +++ b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/Pac4jAuditorAware.java @@ -7,11 +7,14 @@ import java.util.Optional; public class Pac4jAuditorAware implements AuditorAware { + + private static final String ANONYMOUS = "anonymousUser"; + @Override public Optional getCurrentAuditor() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null) { - return Optional.empty(); + return Optional.of(ANONYMOUS); } return Optional.of(authentication.getName()); }