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..c4e677c57 --- /dev/null +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/service/envers/EnversVersioningMetadataTests.groovy @@ -0,0 +1,84 @@ +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.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() + } +} 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..24731b6f3 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 @@ -17,7 +17,12 @@ import javax.persistence.Id; import javax.persistence.MappedSuperclass; 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 @@ -94,4 +99,20 @@ 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); + } + + 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/envers/EnversVersionServiceSupport.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/envers/EnversVersionServiceSupport.java index 964ede86a..b12d943fc 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,11 @@ public List findVersionsForPersistentEntity(String resourceId, Class .getResultList(); Object listOfVersions = revs.stream() - .map(it -> ((Object[]) it)[1]) + //.map(it -> ((Object[]) it)) .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()); 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/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/repository/envers/EnversTestsSupport.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/EnversTestsSupport.groovy index 2052cf9d1..cd1bb58b5 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/EnversTestsSupport.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/EnversTestsSupport.groovy @@ -13,6 +13,10 @@ import org.springframework.transaction.PlatformTransactionManager import org.springframework.transaction.support.DefaultTransactionDefinition import javax.persistence.EntityManager +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.ZoneOffset +import java.time.ZonedDateTime import static org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRES_NEW 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()); }