diff --git a/Jenkinsfile b/Jenkinsfile index bc032671c..8c6befc98 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -48,5 +48,8 @@ pipeline { success { emailext body: '''${SCRIPT, template="groovy-text.template"}''', recipientProviders: [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']], subject: '[SHIBUI] Build Success' } + always { + cleanWs() + } } -} \ No newline at end of file +} diff --git a/backend/build.gradle b/backend/build.gradle index 970b23f4c..84f6666bf 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -46,6 +46,36 @@ configurations { } } +def generatedSrcDir = new File(buildDir, 'generated/src/main/java') + +sourceSets { + main { + groovy { + srcDirs = ['src/main/groovy', 'src/main/java', generatedSrcDir] + } + java { + srcDirs = [] + } + } + integrationTest { + groovy { + srcDirs = ['src/integration/groovy'] + compileClasspath += main.output + test.output + runtimeClasspath += main.output + test.output + } + resources { + srcDir 'src/integration/resources' + } + } + + enversTest { + groovy { + srcDir 'src/enversTest/groovy' + } + resources.srcDir 'src/enversTest/resources' + } +} + processResources.dependsOn(':ui:npm_run_buildProd') jar { @@ -174,29 +204,14 @@ dependencies { // Envers for persistent entities versioning compile 'org.hibernate:hibernate-envers' -} -def generatedSrcDir = new File(buildDir, 'generated/src/main/java') + enversTestCompile sourceSets.main.output + enversTestCompile sourceSets.test.output + enversTestCompile configurations.compile + enversTestCompile configurations.testCompile -sourceSets { - main { - groovy { - srcDirs = ['src/main/groovy', 'src/main/java', generatedSrcDir] - } - java { - srcDirs = [] - } - } - integrationTest { - groovy { - srcDirs = ['src/integration/groovy'] - compileClasspath += main.output + test.output - runtimeClasspath += main.output + test.output - } - resources { - srcDir 'src/integration/resources' - } - } + enversTestRuntime configurations.runtime + enversTestRuntime configurations.testRuntime } task integrationTest(type: Test) { @@ -210,6 +225,19 @@ task integrationTest(type: Test) { systemProperties['user.dir'] = workingDir } +task enversTest(type: Test) { + group = 'verification' + description = 'Run tests pertaing to envers versioning engine' + testClassesDirs = sourceSets.enversTest.output.classesDirs + classpath = sourceSets.enversTest.runtimeClasspath + systemProperties = System.properties + systemProperties['user.dir'] = workingDir +} + +check { + dependsOn enversTest +} + task generateSources { inputs.dir('src/main/templates') inputs.files fileTree('src/main/resources') { 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 new file mode 100644 index 000000000..705f50021 --- /dev/null +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityDescriptorControllerVersionEndpointsIntegrationTests.groovy @@ -0,0 +1,118 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller + +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.repository.EntityDescriptorRepository +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.web.client.TestRestTemplate +import org.springframework.test.context.ActiveProfiles +import spock.lang.Specification + +/** + * @author Dmitriy Kopylenko + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ActiveProfiles(['no-auth', 'dev']) +class EntityDescriptorControllerVersionEndpointsIntegrationTests extends Specification { + + @Autowired + private TestRestTemplate restTemplate + + @Autowired + EntityDescriptorRepository entityDescriptorRepository + + static BASE_URI = '/api/EntityDescriptor' + + static ALL_VERSIONS_URI = "$BASE_URI/%s/Versions" + + static SPECIFIC_VERSION_URI = "$BASE_URI/%s/Versions/%s" + + def "GET /api/EntityDescriptor/{resourceId}/Versions with non-existent entity descriptor"() { + when: + def result = getAllEntityDescriptorVersions('non-existent-ed-id', String) + + then: + result.statusCodeValue == 404 + } + + 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) + + when: + def result = getAllEntityDescriptorVersions(ed.resourceId, List) + + then: + result.statusCodeValue == 200 + result.body.size == 1 + result.body[0].id && result.body[0].creator && result.body[0].date + } + + 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) + //Will created a second version for UPDATE revision + ed.serviceEnabled = true + entityDescriptorRepository.save(ed) + + when: + def result = getAllEntityDescriptorVersions(ed.resourceId, List) + + then: + result.statusCodeValue == 200 + result.body.size == 2 + result.body[0].id < result.body[1].id + result.body[0].date < result.body[1].date + } + + 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) + + when: + def result = getEntityDescriptorForVersion(ed.resourceId, '1000', EntityDescriptorRepresentation) + + then: + result.statusCodeValue == 404 + } + + def "GET /api/EntityDescriptor{resourceId}/Versions/{version} with 2 entity descriptor versions returns correct ED for specific versions"() { + given: + EntityDescriptor ed = new EntityDescriptor(entityID: 'http://test/controller', createdBy: 'anonymousUser', serviceProviderName: 'SP1') + ed = entityDescriptorRepository.save(ed) + //Will created a second version for UPDATE revision + ed.serviceProviderName = 'SP2' + entityDescriptorRepository.save(ed) + + when: + def allVersions = getAllEntityDescriptorVersions(ed.resourceId, List) + def edv1 = getEntityDescriptorForVersion(ed.resourceId, allVersions.body[0].id, EntityDescriptorRepresentation) + def edv2 = getEntityDescriptorForVersion(ed.resourceId, allVersions.body[1].id, EntityDescriptorRepresentation) + + then: + edv1.statusCodeValue == 200 + edv1.body.serviceProviderName == 'SP1' + edv2.statusCodeValue == 200 + edv2.body.serviceProviderName == 'SP2' + } + + private getAllEntityDescriptorVersions(String resourceId, responseType) { + this.restTemplate.getForEntity(resourceUriFor(ALL_VERSIONS_URI, resourceId), responseType) + } + + private getEntityDescriptorForVersion(String resourceId, String version, responseType) { + this.restTemplate.getForEntity(resourceUriFor(SPECIFIC_VERSION_URI, resourceId, version), responseType) + } + + private static resourceUriFor(String uriTemplate, String resourceId, String version) { + String.format(uriTemplate, resourceId, version) + } + + private static resourceUriFor(String uriTemplate, String resourceId) { + String.format(uriTemplate, resourceId) + } +} diff --git a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy new file mode 100644 index 000000000..f23ea333d --- /dev/null +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy @@ -0,0 +1,121 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller + +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FilesystemMetadataResolver +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.MetadataResolverRepository +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.web.client.TestRestTemplate +import org.springframework.test.context.ActiveProfiles +import spock.lang.Specification + +/** + * @author Dmitriy Kopylenko + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ActiveProfiles(['no-auth', 'dev']) +class MetadataResolverControllerVersionEndpointsIntegrationTests extends Specification { + + @Autowired + private TestRestTemplate restTemplate + + @Autowired + MetadataResolverRepository repository + + static BASE_URI = '/api/MetadataResolvers' + + static ALL_VERSIONS_URI = "$BASE_URI/%s/Versions" + + static SPECIFIC_VERSION_URI = "$BASE_URI/%s/Versions/%s" + + def "GET /api/MetadataResolvers/{resourceId}/Versions with non-existent resolver"() { + when: + def result = getAllMetadataResolverVersions('non-existent-resolver-id', String) + + then: + result.statusCodeValue == 404 + } + + def "GET /api/MetadataResolvers/{resourceId}/Versions with 1 resolver version"() { + given: + MetadataResolver mr = new LocalDynamicMetadataResolver(name: 'resolver') + repository.save(mr) + + when: + def result = getAllMetadataResolverVersions(mr.resourceId, List) + + then: + result.statusCodeValue == 200 + result.body.size == 1 + result.body[0].id && result.body[0].creator && result.body[0].date + } + + def "GET /api/MetadataResolvers/{resourceId}/Versions with 2 resolver versions"() { + given: + MetadataResolver mr = new FileBackedHttpMetadataResolver(name: 'resolver') + mr = repository.save(mr) + //Will create a second version for UPDATE revision + mr.name = 'resolverUPDATED' + repository.save(mr) + + when: + def result = getAllMetadataResolverVersions(mr.resourceId, List) + + then: + result.statusCodeValue == 200 + result.body.size == 2 + result.body[0].id < result.body[1].id + result.body[0].date < result.body[1].date + } + + def "GET /api/MetadataResolvers/{resourceId}/Versions/{version} for non existent version"() { + given: + MetadataResolver mr = new DynamicHttpMetadataResolver(name: 'resolver') + mr = repository.save(mr) + + when: + def result = getMetadataResolverForVersion(mr.resourceId, '1000', MetadataResolver) + + then: + result.statusCodeValue == 404 + } + + def "GET /api/MetadataResolvers/{resourceId}/Versions/{version} with 2 resolver versions returns correct resolver for specific version"() { + given: + MetadataResolver mr = new FilesystemMetadataResolver(name: 'resolver') + mr = repository.save(mr) + //Will create a second version for UPDATE revision + mr.name = 'resolverUPDATED' + repository.save(mr) + + when: + def allVersions = getAllMetadataResolverVersions(mr.resourceId, List) + def mrv1 = getMetadataResolverForVersion(mr.resourceId, allVersions.body[0].id, MetadataResolver) + def mrv2 = getMetadataResolverForVersion(mr.resourceId, allVersions.body[1].id, MetadataResolver) + + then: + mrv1.statusCodeValue == 200 + mrv1.body.name == 'resolver' + mrv2.statusCodeValue == 200 + mrv2.body.name == 'resolverUPDATED' + } + + private getAllMetadataResolverVersions(String resourceId, responseType) { + this.restTemplate.getForEntity(resourceUriFor(ALL_VERSIONS_URI, resourceId), responseType) + } + + private getMetadataResolverForVersion(String resourceId, String version, responseType) { + this.restTemplate.getForEntity(resourceUriFor(SPECIFIC_VERSION_URI, resourceId, version), responseType) + } + + private static resourceUriFor(String uriTemplate, String resourceId, String version) { + String.format(uriTemplate, resourceId, version) + } + + private static resourceUriFor(String uriTemplate, String resourceId) { + String.format(uriTemplate, resourceId) + } +} diff --git a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/EntityDescriptorEnversVersioningTests.groovy b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/EntityDescriptorEnversVersioningTests.groovy new file mode 100644 index 000000000..b7f31270a --- /dev/null +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/EntityDescriptorEnversVersioningTests.groovy @@ -0,0 +1,651 @@ +package edu.internet2.tier.shibboleth.admin.ui.repository.envers + +import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration +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.AssertionConsumerService +import edu.internet2.tier.shibboleth.admin.ui.domain.Attribute +import edu.internet2.tier.shibboleth.admin.ui.domain.ContactPerson +import edu.internet2.tier.shibboleth.admin.ui.domain.Description +import edu.internet2.tier.shibboleth.admin.ui.domain.DisplayName +import edu.internet2.tier.shibboleth.admin.ui.domain.EmailAddress +import edu.internet2.tier.shibboleth.admin.ui.domain.EntityAttributes +import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor +import edu.internet2.tier.shibboleth.admin.ui.domain.Extensions +import edu.internet2.tier.shibboleth.admin.ui.domain.GivenName +import edu.internet2.tier.shibboleth.admin.ui.domain.InformationURL +import edu.internet2.tier.shibboleth.admin.ui.domain.KeyDescriptor +import edu.internet2.tier.shibboleth.admin.ui.domain.KeyInfo +import edu.internet2.tier.shibboleth.admin.ui.domain.Logo +import edu.internet2.tier.shibboleth.admin.ui.domain.NameIDFormat +import edu.internet2.tier.shibboleth.admin.ui.domain.Organization +import edu.internet2.tier.shibboleth.admin.ui.domain.OrganizationDisplayName +import edu.internet2.tier.shibboleth.admin.ui.domain.OrganizationName +import edu.internet2.tier.shibboleth.admin.ui.domain.OrganizationURL +import edu.internet2.tier.shibboleth.admin.ui.domain.PrivacyStatementURL +import edu.internet2.tier.shibboleth.admin.ui.domain.SPSSODescriptor +import edu.internet2.tier.shibboleth.admin.ui.domain.SingleLogoutService +import edu.internet2.tier.shibboleth.admin.ui.domain.UIInfo +import edu.internet2.tier.shibboleth.admin.ui.domain.X509Certificate +import edu.internet2.tier.shibboleth.admin.ui.domain.X509Data +import edu.internet2.tier.shibboleth.admin.ui.domain.XSBoolean +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 +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.LogoutEndpointRepresentation +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.MduiRepresentation +import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.OrganizationRepresentation +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.service.EntityDescriptorService +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.EnableJpaRepositories +import org.springframework.test.context.ContextConfiguration +import org.springframework.transaction.PlatformTransactionManager +import spock.lang.Specification + +import javax.persistence.EntityManager + +import static edu.internet2.tier.shibboleth.admin.ui.repository.envers.EnversTestsSupport.getModifiedEntityNames +import static edu.internet2.tier.shibboleth.admin.ui.repository.envers.EnversTestsSupport.getRevisionEntityForRevisionIndex +import static edu.internet2.tier.shibboleth.admin.ui.repository.envers.EnversTestsSupport.getTargetEntityForRevisionIndex +import static edu.internet2.tier.shibboleth.admin.ui.repository.envers.EnversTestsSupport.updateAndGetRevisionHistoryOfEntityDescriptor + +/** + * Testing entity descriptor envers versioning + */ +@DataJpaTest +@ContextConfiguration(classes = [CoreShibUiConfiguration, InternationalizationConfiguration, SearchConfiguration, TestConfiguration]) +@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) +@EntityScan("edu.internet2.tier.shibboleth.admin.ui") +class EntityDescriptorEnversVersioningTests extends Specification { + + @Autowired + EntityDescriptorRepository entityDescriptorRepository + + @Autowired + EntityDescriptorService entityDescriptorService + + @Autowired + EntityManager entityManager + + @Autowired + PlatformTransactionManager txMgr + + @Autowired + OpenSamlObjects openSamlObjects + + def "test versioning with contact persons"() { + setup: + def expectedModifiedPersistentEntities = [EntityDescriptor.name, ContactPerson.name, GivenName.name, EmailAddress.name] + + when: + def ed = new EntityDescriptor() + def representation = new EntityDescriptorRepresentation().with { + it.contacts = [new ContactRepresentation(type: 'administrative', name: 'name', emailAddress: 'test@test')] + it + } + def entityDescriptorHistory = updateAndGetRevisionHistoryOfEntityDescriptor(ed, representation, entityDescriptorService, + entityDescriptorRepository, + txMgr, + entityManager) + + then: + entityDescriptorHistory.size() == 1 + getTargetEntityForRevisionIndex(entityDescriptorHistory, 0).contactPersons[0].givenName.name == 'name' + getTargetEntityForRevisionIndex(entityDescriptorHistory, 0).contactPersons[0].type == org.opensaml.saml.saml2.metadata.ContactPersonTypeEnumeration.ADMINISTRATIVE + getTargetEntityForRevisionIndex(entityDescriptorHistory, 0).contactPersons[0].emailAddresses[0].address == 'test@test' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 0).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 0).timestamp > 0L + getModifiedEntityNames(entityDescriptorHistory, 0).sort() == expectedModifiedPersistentEntities.sort() + + when: + representation = new EntityDescriptorRepresentation().with { + it.contacts = [new ContactRepresentation(type: 'administrative', name: 'nameUPDATED', emailAddress: 'test@test')] + it + } + entityDescriptorHistory = updateAndGetRevisionHistoryOfEntityDescriptor(ed, representation, entityDescriptorService, + entityDescriptorRepository, + txMgr, + entityManager) + then: + entityDescriptorHistory.size() == 2 + getTargetEntityForRevisionIndex(entityDescriptorHistory, 1).contactPersons[0].givenName.name == 'nameUPDATED' + getTargetEntityForRevisionIndex(entityDescriptorHistory, 1).contactPersons[0].type == org.opensaml.saml.saml2.metadata.ContactPersonTypeEnumeration.ADMINISTRATIVE + getTargetEntityForRevisionIndex(entityDescriptorHistory, 1).contactPersons[0].emailAddresses[0].address == 'test@test' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 1).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 1).timestamp > 0L + getModifiedEntityNames(entityDescriptorHistory, 1).sort() == expectedModifiedPersistentEntities.sort() + + when: + representation = new EntityDescriptorRepresentation().with { + it.contacts = [new ContactRepresentation(type: 'other', name: 'nameUPDATED2', emailAddress: 'test@test.com')] + it + } + entityDescriptorHistory = updateAndGetRevisionHistoryOfEntityDescriptor(ed, representation, + entityDescriptorService, + entityDescriptorRepository, + txMgr, + entityManager) + + then: + entityDescriptorHistory.size() == 3 + getTargetEntityForRevisionIndex(entityDescriptorHistory, 2).contactPersons[0].givenName.name == 'nameUPDATED2' + getTargetEntityForRevisionIndex(entityDescriptorHistory, 2).contactPersons[0].type == org.opensaml.saml.saml2.metadata.ContactPersonTypeEnumeration.OTHER + getTargetEntityForRevisionIndex(entityDescriptorHistory, 2).contactPersons[0].emailAddresses[0].address == 'test@test.com' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 2).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 2).timestamp > 0L + getModifiedEntityNames(entityDescriptorHistory, 2).sort() == expectedModifiedPersistentEntities.sort() + + //Also make sure we have our original revision + getTargetEntityForRevisionIndex(entityDescriptorHistory, 1).contactPersons[0].givenName.name == 'nameUPDATED' + getTargetEntityForRevisionIndex(entityDescriptorHistory, 1).contactPersons[0].type == org.opensaml.saml.saml2.metadata.ContactPersonTypeEnumeration.ADMINISTRATIVE + getTargetEntityForRevisionIndex(entityDescriptorHistory, 1).contactPersons[0].emailAddresses[0].address == 'test@test' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 1).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 1).timestamp > 0L + + } + + def "test versioning with organization"() { + setup: + def expectedModifiedPersistentEntities = [EntityDescriptor.name, + Organization.name, + OrganizationDisplayName.name, + OrganizationName.name, + OrganizationURL.name] + + when: + EntityDescriptor ed = new EntityDescriptor() + def representation = new EntityDescriptorRepresentation().with { + it.organization = new OrganizationRepresentation(name: 'org', displayName: 'display org', url: 'http://org.edu') + it + } + def entityDescriptorHistory = updateAndGetRevisionHistoryOfEntityDescriptor(ed, representation, entityDescriptorService, + entityDescriptorRepository, + txMgr, + entityManager) + then: + entityDescriptorHistory.size() == 1 + getTargetEntityForRevisionIndex(entityDescriptorHistory, 0).organization.organizationNames[0].value == 'org' + getTargetEntityForRevisionIndex(entityDescriptorHistory, 0).organization.displayNames[0].value == 'display org' + getTargetEntityForRevisionIndex(entityDescriptorHistory, 0).organization.URLs[0].value == 'http://org.edu' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 0).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 0).timestamp > 0L + getModifiedEntityNames(entityDescriptorHistory, 0).sort() == expectedModifiedPersistentEntities.sort() + + + + + + when: + representation = new EntityDescriptorRepresentation().with { + it.organization = new OrganizationRepresentation(name: 'orgUpdated', displayName: 'display org Updated', url: 'http://org2.edu') + it + } + entityDescriptorHistory = updateAndGetRevisionHistoryOfEntityDescriptor(ed, representation, entityDescriptorService, + entityDescriptorRepository, + txMgr, + entityManager) + then: + entityDescriptorHistory.size() == 2 + getTargetEntityForRevisionIndex(entityDescriptorHistory, 1).organization.organizationNames[0].value == 'orgUpdated' + getTargetEntityForRevisionIndex(entityDescriptorHistory, 1).organization.displayNames[0].value == 'display org Updated' + getTargetEntityForRevisionIndex(entityDescriptorHistory, 1).organization.URLs[0].value == 'http://org2.edu' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 0).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 0).timestamp > 0L + getModifiedEntityNames(entityDescriptorHistory, 1).sort() == expectedModifiedPersistentEntities.sort() + + //Check the original revision is intact + getTargetEntityForRevisionIndex(entityDescriptorHistory, 0).organization.organizationNames[0].value == 'org' + getTargetEntityForRevisionIndex(entityDescriptorHistory, 0).organization.displayNames[0].value == 'display org' + getTargetEntityForRevisionIndex(entityDescriptorHistory, 0).organization.URLs[0].value == 'http://org.edu' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 1).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 1).timestamp > 0L + } + + def "test versioning with sp sso descriptor"() { + setup: + def expectedModifiedPersistentEntities = [EntityDescriptor.name, + NameIDFormat.name, + SPSSODescriptor.name] + when: + EntityDescriptor ed = new EntityDescriptor() + def representation = new EntityDescriptorRepresentation().with { + it.serviceProviderSsoDescriptor = new ServiceProviderSsoDescriptorRepresentation().with { + it.protocolSupportEnum = 'SAML 1.1' + it.nameIdFormats = ['format'] + it + } + it + } + def entityDescriptorHistory = updateAndGetRevisionHistoryOfEntityDescriptor(ed, representation, entityDescriptorService, + entityDescriptorRepository, + txMgr, + entityManager) + + then: + entityDescriptorHistory.size() == 1 + getTargetEntityForRevisionIndex(entityDescriptorHistory, 0).roleDescriptors[0].nameIDFormats[0].format == 'format' + getTargetEntityForRevisionIndex(entityDescriptorHistory, 0).roleDescriptors[0].supportedProtocols[0] == 'urn:oasis:names:tc:SAML:1.1:protocol' + getTargetEntityForRevisionIndex(entityDescriptorHistory, 0).roleDescriptors[0].supportedProtocols[1] == null + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 0).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 0).timestamp > 0L + getModifiedEntityNames(entityDescriptorHistory, 0).sort() == expectedModifiedPersistentEntities.sort() + + when: + representation = new EntityDescriptorRepresentation().with { + it.serviceProviderSsoDescriptor = new ServiceProviderSsoDescriptorRepresentation().with { + it.protocolSupportEnum = 'SAML 1.1, SAML 2' + it.nameIdFormats = ['formatUPDATED'] + it + } + it + } + + entityDescriptorHistory = updateAndGetRevisionHistoryOfEntityDescriptor(ed, representation, entityDescriptorService, + entityDescriptorRepository, + txMgr, + entityManager) + + then: + entityDescriptorHistory.size() == 2 + getTargetEntityForRevisionIndex(entityDescriptorHistory, 1).roleDescriptors[0].nameIDFormats[0].format == 'formatUPDATED' + getTargetEntityForRevisionIndex(entityDescriptorHistory, 1).roleDescriptors[0].supportedProtocols[0] == 'urn:oasis:names:tc:SAML:1.1:protocol' + getTargetEntityForRevisionIndex(entityDescriptorHistory, 1).roleDescriptors[0].supportedProtocols[1] == 'urn:oasis:names:tc:SAML:2.0:protocol' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 1).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 1).timestamp > 0L + getModifiedEntityNames(entityDescriptorHistory, 1).sort() == expectedModifiedPersistentEntities.sort() + + //Check the original revision is intact + getTargetEntityForRevisionIndex(entityDescriptorHistory, 0).roleDescriptors[0].nameIDFormats[0].format == 'format' + getTargetEntityForRevisionIndex(entityDescriptorHistory, 0).roleDescriptors[0].supportedProtocols[0] == 'urn:oasis:names:tc:SAML:1.1:protocol' + getTargetEntityForRevisionIndex(entityDescriptorHistory, 0).roleDescriptors[0].supportedProtocols[1] == null + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 0).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(entityDescriptorHistory, 0).timestamp > 0L + } + + def "test versioning with uiInfo"() { + setup: + def expectedModifiedPersistentEntities = [EntityDescriptor.name, + Description.name, + DisplayName.name, + SPSSODescriptor.name, + Extensions.name, + InformationURL.name, + Logo.name, + PrivacyStatementURL.name, + UIInfo.name] + + when: + EntityDescriptor ed = new EntityDescriptor() + def representation = new EntityDescriptorRepresentation().with { + it.mdui = new MduiRepresentation().with { + it.displayName = 'Initial display name' + it.informationUrl = 'http://info' + it.privacyStatementUrl = 'http://privacy' + it.description = 'Initial desc' + it.logoUrl = 'http://logo' + it.logoHeight = 20 + it.logoWidth = 30 + it + } + it + } + def entityDescriptorHistory = updateAndGetRevisionHistoryOfEntityDescriptor(ed, representation, entityDescriptorService, + entityDescriptorRepository, + txMgr, + entityManager) + + //Groovy FTW - able to call any private methods on ANY object. Get first revision + UIInfo uiinfo = entityDescriptorService.getUIInfo(getTargetEntityForRevisionIndex(entityDescriptorHistory, 0)) + + then: + entityDescriptorHistory.size() == 1 + uiinfo.displayNames[0].value == 'Initial display name' + uiinfo.informationURLs[0].value == 'http://info' + uiinfo.privacyStatementURLs[0].value == 'http://privacy' + uiinfo.descriptions[0].value == 'Initial desc' + uiinfo.logos[0].URL == 'http://logo' + uiinfo.logos[0].height == 20 + uiinfo.logos[0].width == 30 + getModifiedEntityNames(entityDescriptorHistory, 0).sort() == expectedModifiedPersistentEntities.sort() + + when: + representation = new EntityDescriptorRepresentation().with { + it.mdui = new MduiRepresentation().with { + it.displayName = 'Display name UPDATED' + it.informationUrl = 'http://info.updated' + it.privacyStatementUrl = 'http://privacy.updated' + it.description = 'Desc UPDATED' + it.logoUrl = 'http://logo.updated' + it.logoHeight = 30 + it.logoWidth = 40 + it + } + it + } + entityDescriptorHistory = updateAndGetRevisionHistoryOfEntityDescriptor(ed, representation, entityDescriptorService, + entityDescriptorRepository, + txMgr, + entityManager) + + //Get second revision + uiinfo = entityDescriptorService.getUIInfo(getTargetEntityForRevisionIndex(entityDescriptorHistory, 1)) + //And initial revision + def uiinfoInitialRevision = entityDescriptorService.getUIInfo(getTargetEntityForRevisionIndex(entityDescriptorHistory, 0)) + + then: + entityDescriptorHistory.size() == 2 + uiinfo.displayNames[0].value == 'Display name UPDATED' + uiinfo.informationURLs[0].value == 'http://info.updated' + uiinfo.privacyStatementURLs[0].value == 'http://privacy.updated' + uiinfo.descriptions[0].value == 'Desc UPDATED' + uiinfo.logos[0].URL == 'http://logo.updated' + uiinfo.logos[0].height == 30 + uiinfo.logos[0].width == 40 + getModifiedEntityNames(entityDescriptorHistory, 1).sort() == expectedModifiedPersistentEntities.sort() + + //Check the initial revision is still intact + uiinfoInitialRevision.displayNames[0].value == 'Initial display name' + uiinfoInitialRevision.informationURLs[0].value == 'http://info' + uiinfoInitialRevision.privacyStatementURLs[0].value == 'http://privacy' + uiinfoInitialRevision.descriptions[0].value == 'Initial desc' + uiinfoInitialRevision.logos[0].URL == 'http://logo' + uiinfoInitialRevision.logos[0].height == 20 + uiinfoInitialRevision.logos[0].width == 30 + } + + def "test versioning with security"() { + setup: + def expectedModifiedPersistentEntities = [EntityDescriptor.name, + KeyDescriptor.name, + KeyInfo.name, + SPSSODescriptor.name, + X509Certificate.name, + X509Data.name] + + when: + EntityDescriptor ed = new EntityDescriptor() + def representation = new EntityDescriptorRepresentation().with { + it.securityInfo = new SecurityInfoRepresentation().with { + it.authenticationRequestsSigned = true + it.x509CertificateAvailable = true + it.x509Certificates = [new SecurityInfoRepresentation.X509CertificateRepresentation(name: 'sign', type: 'signing', value: 'signingValue')] + it + } + it + } + + def entityDescriptorHistory = updateAndGetRevisionHistoryOfEntityDescriptor(ed, representation, entityDescriptorService, + entityDescriptorRepository, + txMgr, + entityManager) + + //Get initial revision + SPSSODescriptor spssoDescriptor = + entityDescriptorService.getSPSSODescriptorFromEntityDescriptor(getTargetEntityForRevisionIndex(entityDescriptorHistory,0)) + + KeyDescriptor keyDescriptor = spssoDescriptor.keyDescriptors[0] + X509Certificate x509cert = keyDescriptor.keyInfo.x509Datas[0].x509Certificates[0] + + then: + entityDescriptorHistory.size() == 1 + spssoDescriptor.isAuthnRequestsSigned() + keyDescriptor.name == 'sign' + keyDescriptor.usageType == 'signing' + x509cert.value == 'signingValue' + getModifiedEntityNames(entityDescriptorHistory, 0).sort() == expectedModifiedPersistentEntities.sort() + + when: + representation = new EntityDescriptorRepresentation().with { + it.securityInfo = new SecurityInfoRepresentation().with { + it.authenticationRequestsSigned = false + it.x509CertificateAvailable = true + it.x509Certificates = [new SecurityInfoRepresentation.X509CertificateRepresentation(name: 'sign', type: 'signing', value: 'signingValue'), + new SecurityInfoRepresentation.X509CertificateRepresentation(name: 'encrypt', type: 'encryption', value: 'encryptionValue')] + it + } + it + } + + entityDescriptorHistory = updateAndGetRevisionHistoryOfEntityDescriptor(ed, representation, entityDescriptorService, + entityDescriptorRepository, + txMgr, + entityManager) + + + //Get second revision + SPSSODescriptor spssoDescriptor_second = entityDescriptorService.getSPSSODescriptorFromEntityDescriptor(getTargetEntityForRevisionIndex(entityDescriptorHistory,1)) + + KeyDescriptor keyDescriptor_second1 = spssoDescriptor_second.keyDescriptors[0] + X509Certificate x509cert_second1 = keyDescriptor_second1.keyInfo.x509Datas[0].x509Certificates[0] + KeyDescriptor keyDescriptor_second2 = spssoDescriptor_second.keyDescriptors[1] + X509Certificate x509cert_second2 = keyDescriptor_second2.keyInfo.x509Datas[0].x509Certificates[0] + + + //Get initial revision + spssoDescriptor = + entityDescriptorService.getSPSSODescriptorFromEntityDescriptor(getTargetEntityForRevisionIndex(entityDescriptorHistory,0)) + + keyDescriptor = spssoDescriptor.keyDescriptors[0] + x509cert = keyDescriptor.keyInfo.x509Datas[0].x509Certificates[0] + + then: + entityDescriptorHistory.size() == 2 + !spssoDescriptor_second.isAuthnRequestsSigned() + keyDescriptor_second1.name == 'sign' + keyDescriptor_second1.usageType == 'signing' + keyDescriptor_second2.name == 'encrypt' + keyDescriptor_second2.usageType == 'encryption' + x509cert_second1.value == 'signingValue' + x509cert_second2.value == 'encryptionValue' + getModifiedEntityNames(entityDescriptorHistory, 1).sort() == expectedModifiedPersistentEntities.sort() + + //Check the initial version is intact + spssoDescriptor.keyDescriptors.size() == 1 + spssoDescriptor.isAuthnRequestsSigned() + keyDescriptor.name == 'sign' + keyDescriptor.usageType == 'signing' + x509cert.value == 'signingValue' + } + + def "test versioning ACS"() { + setup: + def expectedModifiedPersistentEntities = [EntityDescriptor.name, + SPSSODescriptor.name, + AssertionConsumerService.name] + + when: + EntityDescriptor ed = new EntityDescriptor() + def representation = new EntityDescriptorRepresentation().with { + it.assertionConsumerServices = [ + new AssertionConsumerServiceRepresentation(locationUrl: 'http://acs', binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST')] + it + } + + def entityDescriptorHistory = updateAndGetRevisionHistoryOfEntityDescriptor(ed, representation, entityDescriptorService, + entityDescriptorRepository, + txMgr, + entityManager) + + SPSSODescriptor spssoDescriptor = + entityDescriptorService.getSPSSODescriptorFromEntityDescriptor(getTargetEntityForRevisionIndex(entityDescriptorHistory,0)) + AssertionConsumerService acs = spssoDescriptor.assertionConsumerServices[0] + + then: + entityDescriptorHistory.size() == 1 + !acs.isDefault() + acs.location == 'http://acs' + acs.binding == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' + getModifiedEntityNames(entityDescriptorHistory, 0).sort() == expectedModifiedPersistentEntities.sort() + + + when: + representation = new EntityDescriptorRepresentation().with { + it.assertionConsumerServices = [ + new AssertionConsumerServiceRepresentation(locationUrl: 'http://acs.updated', binding: 'urn:oasis:names:tc:SAML:2.0:bindings:PAOS', makeDefault: true), + new AssertionConsumerServiceRepresentation(locationUrl: 'http://acs2', binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact')] + it + } + + entityDescriptorHistory = updateAndGetRevisionHistoryOfEntityDescriptor(ed, representation, entityDescriptorService, + entityDescriptorRepository, + txMgr, + entityManager) + + SPSSODescriptor spssoDescriptor2 = + entityDescriptorService.getSPSSODescriptorFromEntityDescriptor(getTargetEntityForRevisionIndex(entityDescriptorHistory,1)) + def (acs1, acs2) = [spssoDescriptor2.assertionConsumerServices[0], spssoDescriptor2.assertionConsumerServices[1]] + + //Initial revision + spssoDescriptor = + entityDescriptorService.getSPSSODescriptorFromEntityDescriptor(getTargetEntityForRevisionIndex(entityDescriptorHistory,0)) + acs = spssoDescriptor.assertionConsumerServices[0] + + then: + entityDescriptorHistory.size() == 2 + acs1.isDefault() + !acs2.isDefault() + acs1.location == 'http://acs.updated' + acs1.binding == 'urn:oasis:names:tc:SAML:2.0:bindings:PAOS' + acs2.location == 'http://acs2' + acs2.binding == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact' + getModifiedEntityNames(entityDescriptorHistory, 1).sort() == expectedModifiedPersistentEntities.sort() + + //Check the initial revision is intact + !acs.isDefault() + acs.location == 'http://acs' + acs.binding == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' + } + + def "test versioning logout"() { + setup: + def expectedModifiedPersistentEntities = [EntityDescriptor.name, + SPSSODescriptor.name, + SingleLogoutService.name] + + when: + EntityDescriptor ed = new EntityDescriptor() + def representation = new EntityDescriptorRepresentation().with { + it.logoutEndpoints = [new LogoutEndpointRepresentation(url: 'http://logout', bindingType: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST')] + it + } + + def entityDescriptorHistory = updateAndGetRevisionHistoryOfEntityDescriptor(ed, representation, entityDescriptorService, + entityDescriptorRepository, + txMgr, + entityManager) + + SPSSODescriptor spssoDescriptor = + entityDescriptorService.getSPSSODescriptorFromEntityDescriptor(getTargetEntityForRevisionIndex(entityDescriptorHistory, 0)) + SingleLogoutService slo = spssoDescriptor.singleLogoutServices[0] + + then: + entityDescriptorHistory.size() == 1 + slo.location == 'http://logout' + slo.binding == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' + getModifiedEntityNames(entityDescriptorHistory, 0).sort() == expectedModifiedPersistentEntities.sort() + + when: + representation = new EntityDescriptorRepresentation().with { + it.logoutEndpoints = [new LogoutEndpointRepresentation(url: 'http://logout.updated', bindingType: 'urn:oasis:names:tc:SAML:2.0:bindings:PAOS'), + new LogoutEndpointRepresentation(url: 'http://logout2', bindingType: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact')] + it + } + + entityDescriptorHistory = updateAndGetRevisionHistoryOfEntityDescriptor(ed, representation, entityDescriptorService, + entityDescriptorRepository, + txMgr, + entityManager) + + SPSSODescriptor spssoDescriptor2 = + entityDescriptorService.getSPSSODescriptorFromEntityDescriptor(getTargetEntityForRevisionIndex(entityDescriptorHistory, 1)) + def (slo1, slo2) = [spssoDescriptor2.singleLogoutServices[0], spssoDescriptor2.singleLogoutServices[1]] + + //Initial revision + spssoDescriptor = + entityDescriptorService.getSPSSODescriptorFromEntityDescriptor(getTargetEntityForRevisionIndex(entityDescriptorHistory, 0)) + slo = spssoDescriptor.singleLogoutServices[0] + + then: + entityDescriptorHistory.size() == 2 + slo1.location == 'http://logout.updated' + slo1.binding == 'urn:oasis:names:tc:SAML:2.0:bindings:PAOS' + slo2.location == 'http://logout2' + slo2.binding == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact' + getModifiedEntityNames(entityDescriptorHistory, 1).sort() == expectedModifiedPersistentEntities.sort() + + //Check the initial version is intact + slo.location == 'http://logout' + slo.binding == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' + } + + def "test versioning relying party overrides"() { + setup: + def expectedModifiedPersistentEntities = [EntityDescriptor.name, + EntityAttributes.name, + Extensions.name, + Attribute.name, + XSBoolean.name, + XSString.name] + + when: + EntityDescriptor ed = new EntityDescriptor() + def representation = new EntityDescriptorRepresentation().with { + it.relyingPartyOverrides = [signAssertion: true] + it.attributeRelease = ['attr1'] + it + } + + def entityDescriptorHistory = updateAndGetRevisionHistoryOfEntityDescriptor(ed, representation, entityDescriptorService, + entityDescriptorRepository, + txMgr, + entityManager) + + EntityAttributes attrs = entityDescriptorService.getEntityAttributes(getTargetEntityForRevisionIndex(entityDescriptorHistory, 0)) + + then: + entityDescriptorHistory.size() == 1 + attrs.attributes[0].attributeValues[0].storedValue == 'true' + attrs.attributes[1].attributeValues[0].xsStringvalue == 'attr1' + getModifiedEntityNames(entityDescriptorHistory, 0).sort() == expectedModifiedPersistentEntities.sort() + + when: + representation = new EntityDescriptorRepresentation().with { + it.relyingPartyOverrides = [signAssertion: false] + it.attributeRelease = ['attr1', 'attr2'] + it + } + + entityDescriptorHistory = updateAndGetRevisionHistoryOfEntityDescriptor(ed, representation, entityDescriptorService, + entityDescriptorRepository, + txMgr, + entityManager) + + EntityAttributes attrs2 = entityDescriptorService.getEntityAttributes(getTargetEntityForRevisionIndex(entityDescriptorHistory, 1)) + + //Initial revision + attrs = entityDescriptorService.getEntityAttributes(getTargetEntityForRevisionIndex(entityDescriptorHistory, 0)) + + expectedModifiedPersistentEntities = [EntityDescriptor.name, + EntityAttributes.name, + Attribute.name, + XSString.name] + then: + entityDescriptorHistory.size() == 2 + attrs2.attributes[0].attributeValues[0].xsStringvalue == 'attr1' + attrs2.attributes[0].attributeValues[1].xsStringvalue == 'attr2' + getModifiedEntityNames(entityDescriptorHistory, 1).sort() == expectedModifiedPersistentEntities.sort() + + //Check the initial revision is intact + attrs.attributes[0].attributeValues[0].storedValue == 'true' + attrs.attributes[1].attributeValues[0].xsStringvalue == 'attr1' + attrs.attributes[1].attributeValues[1] == null + } +} diff --git a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataFilterEnversVersioningTests.groovy b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataFilterEnversVersioningTests.groovy new file mode 100644 index 000000000..fe1e5eee4 --- /dev/null +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataFilterEnversVersioningTests.groovy @@ -0,0 +1,315 @@ +package edu.internet2.tier.shibboleth.admin.ui.repository.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.filters.EntityAttributesFilter +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityRoleWhiteListFilter +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.RequiredValidUntilFilter +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.SignatureValidationFilter +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FilesystemMetadataResolver +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.domain.resolvers.ResourceBackedMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.repository.FilterRepository +import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository +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.EnableJpaRepositories +import org.springframework.test.context.ContextConfiguration +import org.springframework.transaction.PlatformTransactionManager +import spock.lang.Specification + + +/** + * Testing metadata resolver envers versioning with metadata filters + */ +@DataJpaTest +@ContextConfiguration(classes = [CoreShibUiConfiguration, InternationalizationConfiguration, SearchConfiguration, TestConfiguration, EntitiesVersioningConfiguration]) +@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) +@EntityScan("edu.internet2.tier.shibboleth.admin.ui") +class MetadataFilterEnversVersioningTests extends Specification { + + @Autowired + MetadataResolverRepository metadataResolverRepository + + @Autowired + FilterRepository filterRepository + + @Autowired + MetadataResolverVersionService metadataResolverVersionService + + @Autowired + PlatformTransactionManager txMgr + + + def "test versioning of MetadataResolver with EntityRoleWhiteListFilter"() { + when: 'Add initial filter' + def mr = new LocalDynamicMetadataResolver(name: 'resolver') + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + EntityRoleWhiteListFilter filter = new EntityRoleWhiteListFilter().with { + it.retainedRoles = ['role1'] + it + } + mr.metadataFilters.add(filter) + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + def versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + def mrv1 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[0].id) + def mrv2 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[1].id) + + then: + versions.size() == 2 + mrv1.metadataFilters.size() == 0 + mrv2.metadataFilters.size() == 1 + + when: 'Update filter' + filter = mr.metadataFilters[0] + filter.retainedRoles = ['role1', 'role2'] + filter.removeEmptyEntitiesDescriptors = false + filter = EnversTestsSupport.doInExplicitTransaction(txMgr) { + filterRepository.save(filter) + } + mr.markAsModified() + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + mrv1 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[0].id) + mrv2 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[1].id) + def mrv3 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[2].id) + + then: + versions.size() == 3 + mrv1.metadataFilters.size() == 0 + mrv2.metadataFilters[0].retainedRoles.size() == 1 + mrv2.metadataFilters[0].retainedRoles == ['role1'] + mrv3.metadataFilters[0].retainedRoles.size() == 2 + mrv3.metadataFilters[0].retainedRoles == ['role1','role2'] + mrv3.metadataFilters[0].removeEmptyEntitiesDescriptors == false + } + + def "test versioning of MetadataResolver with EntityAttributesFilter"() { + when: 'Add initial filter' + def mr = new FileBackedHttpMetadataResolver(name: 'resolver') + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + EntityAttributesFilter filter = new EntityAttributesFilter().with { + it.attributeRelease = ['attr1'] + it + } + mr.metadataFilters.add(filter) + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + def versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + def mrv1 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[0].id) + def mrv2 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[1].id) + + then: + versions.size() == 2 + mrv1.metadataFilters.size() == 0 + mrv2.metadataFilters.size() == 1 + + when: 'Update filter' + filter = mr.metadataFilters[0] + filter.attributeRelease = ['attr1, attr2'] + filter = EnversTestsSupport.doInExplicitTransaction(txMgr) { + filterRepository.save(filter) + } + mr.markAsModified() + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + mrv1 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[0].id) + mrv2 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[1].id) + def mrv3 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[2].id) + + then: + versions.size() == 3 + mrv1.metadataFilters.size() == 0 + mrv2.metadataFilters[0].attributes[0].attributeValues[0].xsStringvalue == 'attr1' + mrv3.metadataFilters[0].attributes[0].attributeValues[0].xsStringvalue == 'attr1, attr2' + } + + def "test versioning of MetadataResolver with SignatureValidationFilter"() { + when: 'Add initial filter' + def mr = new DynamicHttpMetadataResolver(name: 'resolver') + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + SignatureValidationFilter filter = new SignatureValidationFilter().with { + it.certificateFile = 'cert1.file' + it + } + mr.metadataFilters.add(filter) + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + def versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + def mrv1 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[0].id) + def mrv2 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[1].id) + + then: + versions.size() == 2 + mrv1.metadataFilters.size() == 0 + mrv2.metadataFilters.size() == 1 + mrv2.metadataFilters[0].certificateFile == 'cert1.file' + + when: 'Update filter' + filter = mr.metadataFilters[0] + filter.certificateFile = 'cert2.file' + filter = EnversTestsSupport.doInExplicitTransaction(txMgr) { + filterRepository.save(filter) + } + mr.markAsModified() + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + mrv1 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[0].id) + mrv2 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[1].id) + def mrv3 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[2].id) + + then: + versions.size() == 3 + mrv1.metadataFilters.size() == 0 + mrv2.metadataFilters[0].certificateFile == 'cert1.file' + mrv3.metadataFilters[0].certificateFile == 'cert2.file' + } + + def "test versioning of MetadataResolver with RequiredValidUntilFilter"() { + when: 'Add initial filter' + def mr = new FilesystemMetadataResolver(name: 'resolver') + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + RequiredValidUntilFilter filter = new RequiredValidUntilFilter().with { + it.maxValidityInterval = "PT1S" + it + } + mr.metadataFilters.add(filter) + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + def versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + def mrv1 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[0].id) + def mrv2 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[1].id) + + then: + versions.size() == 2 + mrv1.metadataFilters.size() == 0 + mrv2.metadataFilters.size() == 1 + mrv2.metadataFilters[0].maxValidityInterval == 'PT1S' + + when: 'Update filter' + filter = mr.metadataFilters[0] + filter.maxValidityInterval = 'PT30S' + filter = EnversTestsSupport.doInExplicitTransaction(txMgr) { + filterRepository.save(filter) + } + mr.markAsModified() + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + mrv1 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[0].id) + mrv2 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[1].id) + def mrv3 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[2].id) + + then: + versions.size() == 3 + mrv1.metadataFilters.size() == 0 + mrv2.metadataFilters[0].maxValidityInterval == 'PT1S' + mrv3.metadataFilters[0].maxValidityInterval == 'PT30S' + } + + def "test versioning of MetadataResolver with NameIdFormatFilter"() { + when: 'Add initial filter' + def mr = new ResourceBackedMetadataResolver(name: 'resolver') + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + NameIdFormatFilter filter = new NameIdFormatFilter().with { + it.formats = ['format1'] + it + } + mr.metadataFilters.add(filter) + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + def versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + def mrv1 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[0].id) + def mrv2 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[1].id) + + then: + versions.size() == 2 + mrv1.metadataFilters.size() == 0 + mrv2.metadataFilters.size() == 1 + mrv2.metadataFilters[0].formats == ['format1'] + + when: 'Update filter' + filter = mr.metadataFilters[0] + filter.formats = ['format1', 'format2'] + filter.removeExistingFormats = true + filter = EnversTestsSupport.doInExplicitTransaction(txMgr) { + filterRepository.save(filter) + } + mr.markAsModified() + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + mrv1 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[0].id) + mrv2 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[1].id) + def mrv3 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[2].id) + + then: + versions.size() == 3 + mrv1.metadataFilters.size() == 0 + mrv2.metadataFilters[0].formats == ['format1'] + mrv3.metadataFilters[0].formats == ['format1', 'format2'] + mrv3.metadataFilters[0].removeExistingFormats == true + } + + def "test versioning of deleting a filter"() { + when: 'Add initial filter' + def mr = new MetadataResolver(name: 'resolver') + def filter = new EntityRoleWhiteListFilter() + + mr.metadataFilters.add(filter) + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + //And now remove filter + filter = filterRepository.findByResourceId(filter.resourceId) + mr.metadataFilters = [] + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + EnversTestsSupport.doInExplicitTransaction(txMgr) { + filterRepository.delete(filter) + } + + def versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + def mrv1 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[0].id) + def mrv2 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[1].id) + + then: + versions.size() == 2 + mrv1.metadataFilters.size() == 1 + mrv2.metadataFilters.size() == 0 + } +} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataResolverEntityBasicEnversVersioningTests.groovy b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataResolverEntityBasicEnversVersioningTests.groovy similarity index 95% rename from backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataResolverEntityBasicEnversVersioningTests.groovy rename to backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataResolverEntityBasicEnversVersioningTests.groovy index c4f70dbcd..f8ac3b431 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataResolverEntityBasicEnversVersioningTests.groovy +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataResolverEntityBasicEnversVersioningTests.groovy @@ -21,8 +21,6 @@ import spock.lang.Specification import javax.persistence.EntityManager -import static org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRES_NEW - /** * Testing metadata resolvers basic versioning by envers is functioning. */ @@ -55,7 +53,7 @@ class MetadataResolverEntityBasicEnversVersioningTests extends Specification { def rev = metadataResolverHistory[0] then: - rev[1].principalUserName == 'anonymous' + rev[1].principalUserName == 'anonymousUser' when: mdr.name = 'Updated' @@ -98,7 +96,7 @@ class MetadataResolverEntityBasicEnversVersioningTests extends Specification { //boundary of the test method which commits tx only after an execution of the test method. This let's us explicitly //start/commit transaction making envers data written out and verifiable private doInExplicitTransaction(Closure uow) { - def txStatus = txMgr.getTransaction(new DefaultTransactionDefinition(PROPAGATION_REQUIRES_NEW)) + def txStatus = txMgr.getTransaction(new DefaultTransactionDefinition(org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRES_NEW)) def entity = uow() txMgr.commit(txStatus) entity diff --git a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataResolverEnversVersioningTests.groovy b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataResolverEnversVersioningTests.groovy new file mode 100644 index 000000000..2ba81bcdf --- /dev/null +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataResolverEnversVersioningTests.groovy @@ -0,0 +1,318 @@ +package edu.internet2.tier.shibboleth.admin.ui.repository.envers + +import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration +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.resolvers.ClasspathMetadataResource +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicMetadataResolverAttributes +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FilesystemMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.HttpMetadataResolverAttributes +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ReloadableMetadataResolverAttributes +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ResourceBackedMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.TemplateScheme +import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects +import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository +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.EnableJpaRepositories +import org.springframework.test.context.ContextConfiguration +import org.springframework.transaction.PlatformTransactionManager +import spock.lang.Specification + +import javax.persistence.EntityManager + +import static edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.HttpMetadataResolverAttributes.HttpCachingType.file +import static edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.HttpMetadataResolverAttributes.HttpCachingType.none +import static edu.internet2.tier.shibboleth.admin.ui.repository.envers.EnversTestsSupport.* + +/** + * Testing metadata resolver envers versioning + */ +@DataJpaTest +@ContextConfiguration(classes = [CoreShibUiConfiguration, InternationalizationConfiguration, SearchConfiguration, TestConfiguration]) +@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) +@EntityScan("edu.internet2.tier.shibboleth.admin.ui") +class MetadataResolverEnversVersioningTests extends Specification { + + @Autowired + MetadataResolverRepository metadataResolverRepository + + @Autowired + EntityManager entityManager + + @Autowired + PlatformTransactionManager txMgr + + def "test versioning of LocalDynamicMetadataResolver"() { + setup: + def expectedModifiedPersistentEntities = [LocalDynamicMetadataResolver.name] + + when: + LocalDynamicMetadataResolver resolver = new LocalDynamicMetadataResolver(name: 'ldmr').with { + it.dynamicMetadataResolverAttributes = new DynamicMetadataResolverAttributes() + it + } + def resolverHistory = updateAndGetRevisionHistoryOfMetadataResolver(resolver, + metadataResolverRepository, + LocalDynamicMetadataResolver, + txMgr, + entityManager) + + then: + resolverHistory.size() == 1 + getTargetEntityForRevisionIndex(resolverHistory, 0).name == 'ldmr' + getTargetEntityForRevisionIndex(resolverHistory, 0).dynamicMetadataResolverAttributes.refreshDelayFactor == 0.75 + getRevisionEntityForRevisionIndex(resolverHistory, 0).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(resolverHistory, 0).timestamp > 0L + getModifiedEntityNames(resolverHistory, 0).sort() == expectedModifiedPersistentEntities.sort() + + when: + resolver.name = 'ldmr_UPDATED' + resolver.dynamicMetadataResolverAttributes.refreshDelayFactor = 1.00 + resolverHistory = updateAndGetRevisionHistoryOfMetadataResolver(resolver, + metadataResolverRepository, + LocalDynamicMetadataResolver, + txMgr, + entityManager) + + then: + resolverHistory.size() == 2 + getTargetEntityForRevisionIndex(resolverHistory, 1).name == 'ldmr_UPDATED' + getTargetEntityForRevisionIndex(resolverHistory, 1).dynamicMetadataResolverAttributes.refreshDelayFactor == 1.00 + getRevisionEntityForRevisionIndex(resolverHistory, 1).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(resolverHistory, 1).timestamp > 0L + + //Check the original revision is intact + getTargetEntityForRevisionIndex(resolverHistory, 0).name == 'ldmr' + getTargetEntityForRevisionIndex(resolverHistory, 0).dynamicMetadataResolverAttributes.refreshDelayFactor == 0.75 + getRevisionEntityForRevisionIndex(resolverHistory, 0).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(resolverHistory, 0).timestamp > 0L + } + + def "test versioning of FileBackedHttpMetadataResolver"() { + setup: + def expectedModifiedPersistentEntities = [FileBackedHttpMetadataResolver.name] + + when: + FileBackedHttpMetadataResolver resolver = new FileBackedHttpMetadataResolver(name: 'fbmr').with { + it.httpMetadataResolverAttributes = new HttpMetadataResolverAttributes(proxyUser: 'proxyUser', + proxyPassword: 'proxyPass', + httpCaching: none) + it.reloadableMetadataResolverAttributes = new ReloadableMetadataResolverAttributes(indexesRef: 'indexRef') + it + } + def resolverHistory = updateAndGetRevisionHistoryOfMetadataResolver(resolver, + metadataResolverRepository, + FileBackedHttpMetadataResolver, + txMgr, + entityManager) + + then: + resolverHistory.size() == 1 + getTargetEntityForRevisionIndex(resolverHistory, 0).name == 'fbmr' + getTargetEntityForRevisionIndex(resolverHistory, 0).httpMetadataResolverAttributes.proxyUser == 'proxyUser' + getTargetEntityForRevisionIndex(resolverHistory, 0).httpMetadataResolverAttributes.proxyPassword == 'proxyPass' + getTargetEntityForRevisionIndex(resolverHistory, 0).httpMetadataResolverAttributes.httpCaching == none + getTargetEntityForRevisionIndex(resolverHistory, 0).reloadableMetadataResolverAttributes.indexesRef == 'indexRef' + getRevisionEntityForRevisionIndex(resolverHistory, 0).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(resolverHistory, 0).timestamp > 0L + getModifiedEntityNames(resolverHistory, 0).sort() == expectedModifiedPersistentEntities.sort() + + when: + resolver.name = 'fbmrUPDATED' + resolver.httpMetadataResolverAttributes.proxyUser = 'proxyUserUPDATED' + resolver.httpMetadataResolverAttributes.proxyPassword = 'proxyPassUPDATED' + resolver.httpMetadataResolverAttributes.httpCaching = file + resolver.reloadableMetadataResolverAttributes.indexesRef = 'indexRefUPDATED' + + resolverHistory = updateAndGetRevisionHistoryOfMetadataResolver(resolver, + metadataResolverRepository, + FileBackedHttpMetadataResolver, + txMgr, + entityManager) + + then: + resolverHistory.size() == 2 + getTargetEntityForRevisionIndex(resolverHistory, 1).name == 'fbmrUPDATED' + getTargetEntityForRevisionIndex(resolverHistory, 1).httpMetadataResolverAttributes.proxyUser == 'proxyUserUPDATED' + getTargetEntityForRevisionIndex(resolverHistory, 1).httpMetadataResolverAttributes.proxyPassword == 'proxyPassUPDATED' + getTargetEntityForRevisionIndex(resolverHistory, 1).httpMetadataResolverAttributes.httpCaching == file + getTargetEntityForRevisionIndex(resolverHistory, 1).reloadableMetadataResolverAttributes.indexesRef == 'indexRefUPDATED' + getRevisionEntityForRevisionIndex(resolverHistory, 1).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(resolverHistory, 1).timestamp > 0L + getModifiedEntityNames(resolverHistory, 1).sort() == expectedModifiedPersistentEntities.sort() + + //Check the original revision is intact + getTargetEntityForRevisionIndex(resolverHistory, 0).name == 'fbmr' + getTargetEntityForRevisionIndex(resolverHistory, 0).httpMetadataResolverAttributes.proxyUser == 'proxyUser' + getTargetEntityForRevisionIndex(resolverHistory, 0).httpMetadataResolverAttributes.proxyPassword == 'proxyPass' + getTargetEntityForRevisionIndex(resolverHistory, 0).httpMetadataResolverAttributes.httpCaching == none + getTargetEntityForRevisionIndex(resolverHistory, 0).reloadableMetadataResolverAttributes.indexesRef == 'indexRef' + getRevisionEntityForRevisionIndex(resolverHistory, 0).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(resolverHistory, 0).timestamp > 0L + } + + def "test versioning of DynamicHttpMetadataResolver"() { + setup: + def expectedModifiedPersistentEntities = [DynamicHttpMetadataResolver.name, + TemplateScheme.name] + + when: + DynamicHttpMetadataResolver resolver = new DynamicHttpMetadataResolver(name: 'dhmr').with { + it.metadataRequestURLConstructionScheme = new TemplateScheme().with { + it.content = 'content' + it + } + it + } + + def resolverHistory = updateAndGetRevisionHistoryOfMetadataResolver(resolver, + metadataResolverRepository, + DynamicHttpMetadataResolver, + txMgr, + entityManager) + + then: + resolverHistory.size() == 1 + getTargetEntityForRevisionIndex(resolverHistory, 0).name == 'dhmr' + getRevisionEntityForRevisionIndex(resolverHistory, 0).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(resolverHistory, 0).timestamp > 0L + getModifiedEntityNames(resolverHistory, 0).sort() == expectedModifiedPersistentEntities.sort() + + when: + resolver.name = 'dhmrUPDATED' + resolver.metadataRequestURLConstructionScheme.content = 'Updated content' + + resolverHistory = updateAndGetRevisionHistoryOfMetadataResolver(resolver, + metadataResolverRepository, + DynamicHttpMetadataResolver, + txMgr, + entityManager) + + then: + resolverHistory.size() == 2 + getTargetEntityForRevisionIndex(resolverHistory, 1).name == 'dhmrUPDATED' + getTargetEntityForRevisionIndex(resolverHistory, 1).metadataRequestURLConstructionScheme.content == 'Updated content' + getRevisionEntityForRevisionIndex(resolverHistory, 1).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(resolverHistory, 1).timestamp > 0L + getModifiedEntityNames(resolverHistory, 1).sort() == expectedModifiedPersistentEntities.sort() + + //Check the original revision is intact + getTargetEntityForRevisionIndex(resolverHistory, 0).name == 'dhmr' + getRevisionEntityForRevisionIndex(resolverHistory, 0).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(resolverHistory, 0).timestamp > 0L + getModifiedEntityNames(resolverHistory, 0).sort() == expectedModifiedPersistentEntities.sort() + } + + def "test versioning of FilesystemMetadataResolver"() { + setup: + def expectedModifiedPersistentEntities = [FilesystemMetadataResolver.name] + + when: + FilesystemMetadataResolver resolver = new FilesystemMetadataResolver(name: 'fmr', metadataFile: 'metadata.xml').with { + it.reloadableMetadataResolverAttributes = new ReloadableMetadataResolverAttributes(indexesRef: 'indexesRef') + it + } + + def resolverHistory = updateAndGetRevisionHistoryOfMetadataResolver(resolver, + metadataResolverRepository, + FilesystemMetadataResolver, + txMgr, + entityManager) + + then: + resolverHistory.size() == 1 + getTargetEntityForRevisionIndex(resolverHistory, 0).name == 'fmr' + getTargetEntityForRevisionIndex(resolverHistory, 0).metadataFile == 'metadata.xml' + getTargetEntityForRevisionIndex(resolverHistory, 0).reloadableMetadataResolverAttributes.indexesRef == 'indexesRef' + getRevisionEntityForRevisionIndex(resolverHistory, 0).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(resolverHistory, 0).timestamp > 0L + getModifiedEntityNames(resolverHistory, 0).sort() == expectedModifiedPersistentEntities.sort() + + when: + resolver.name = 'fmrUPDATED' + resolver.metadataFile = 'metadataUPDATED.xml' + resolver.reloadableMetadataResolverAttributes.indexesRef = 'indexesRefUPDATED' + + resolverHistory = updateAndGetRevisionHistoryOfMetadataResolver(resolver, + metadataResolverRepository, + FilesystemMetadataResolver, + txMgr, + entityManager) + + then: + resolverHistory.size() == 2 + getTargetEntityForRevisionIndex(resolverHistory, 1).name == 'fmrUPDATED' + getTargetEntityForRevisionIndex(resolverHistory, 1).metadataFile == 'metadataUPDATED.xml' + getTargetEntityForRevisionIndex(resolverHistory, 1).reloadableMetadataResolverAttributes.indexesRef == 'indexesRefUPDATED' + getRevisionEntityForRevisionIndex(resolverHistory, 1).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(resolverHistory, 1).timestamp > 0L + getModifiedEntityNames(resolverHistory, 1).sort() == expectedModifiedPersistentEntities.sort() + + //Check the original revision is intact + getTargetEntityForRevisionIndex(resolverHistory, 0).name == 'fmr' + getTargetEntityForRevisionIndex(resolverHistory, 0).metadataFile == 'metadata.xml' + getTargetEntityForRevisionIndex(resolverHistory, 0).reloadableMetadataResolverAttributes.indexesRef == 'indexesRef' + getRevisionEntityForRevisionIndex(resolverHistory, 0).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(resolverHistory, 0).timestamp > 0L + } + + def "test versioning of ResourceBackedMetadataResolver"() { + setup: + def expectedModifiedPersistentEntities = [ResourceBackedMetadataResolver.name] + + when: + ResourceBackedMetadataResolver resolver = new ResourceBackedMetadataResolver(name: 'rbmr').with { + it.reloadableMetadataResolverAttributes = new ReloadableMetadataResolverAttributes(taskTimerRef: 'taskTimerRef') + it.classpathMetadataResource = new ClasspathMetadataResource(file: 'metadata.xml') + it + } + + def resolverHistory = updateAndGetRevisionHistoryOfMetadataResolver(resolver, + metadataResolverRepository, + ResourceBackedMetadataResolver, + txMgr, + entityManager) + + then: + resolverHistory.size() == 1 + getTargetEntityForRevisionIndex(resolverHistory, 0).name == 'rbmr' + getTargetEntityForRevisionIndex(resolverHistory, 0).reloadableMetadataResolverAttributes.taskTimerRef == 'taskTimerRef' + getTargetEntityForRevisionIndex(resolverHistory, 0).classpathMetadataResource.file == 'metadata.xml' + getRevisionEntityForRevisionIndex(resolverHistory, 0).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(resolverHistory, 0).timestamp > 0L + getModifiedEntityNames(resolverHistory, 0).sort() == expectedModifiedPersistentEntities.sort() + + when: + resolver.name = 'rbmrUPDATED' + resolver.reloadableMetadataResolverAttributes.taskTimerRef = 'taskTimerRefUPDATED' + resolver.classpathMetadataResource.file = 'metadataUPDATED.xml' + + resolverHistory = updateAndGetRevisionHistoryOfMetadataResolver(resolver, + metadataResolverRepository, + ResourceBackedMetadataResolver, + txMgr, + entityManager) + + then: + resolverHistory.size() == 2 + getTargetEntityForRevisionIndex(resolverHistory, 1).name == 'rbmrUPDATED' + getTargetEntityForRevisionIndex(resolverHistory, 1).reloadableMetadataResolverAttributes.taskTimerRef == 'taskTimerRefUPDATED' + getTargetEntityForRevisionIndex(resolverHistory, 1).classpathMetadataResource.file == 'metadataUPDATED.xml' + getRevisionEntityForRevisionIndex(resolverHistory, 1).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(resolverHistory, 1).timestamp > 0L + getModifiedEntityNames(resolverHistory, 1).sort() == expectedModifiedPersistentEntities.sort() + + //Check the original revision is intact + getTargetEntityForRevisionIndex(resolverHistory, 0).name == 'rbmr' + getTargetEntityForRevisionIndex(resolverHistory, 0).reloadableMetadataResolverAttributes.taskTimerRef == 'taskTimerRef' + getTargetEntityForRevisionIndex(resolverHistory, 0).classpathMetadataResource.file == 'metadata.xml' + getRevisionEntityForRevisionIndex(resolverHistory, 0).principalUserName == 'anonymousUser' + getRevisionEntityForRevisionIndex(resolverHistory, 0).timestamp > 0L + } +} 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 new file mode 100644 index 000000000..ac5a4d129 --- /dev/null +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/service/envers/EnversEntityDescriptorVersionServiceTests.groovy @@ -0,0 +1,121 @@ +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.repository.EntityDescriptorRepository +import edu.internet2.tier.shibboleth.admin.ui.repository.envers.EnversTestsSupport +import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService +import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorVersionService +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.EnableJpaRepositories +import org.springframework.test.context.ContextConfiguration +import org.springframework.transaction.PlatformTransactionManager +import spock.lang.Specification + +import java.time.LocalDateTime +import java.time.ZonedDateTime + + +@DataJpaTest +@ContextConfiguration(classes = [CoreShibUiConfiguration, InternationalizationConfiguration, TestConfiguration, SearchConfiguration, EntitiesVersioningConfiguration]) +@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) +@EntityScan("edu.internet2.tier.shibboleth.admin.ui") +class EnversEntityDescriptorVersionServiceTests extends Specification { + + @Autowired + EntityDescriptorVersionService entityDescriptorVersionService + + @Autowired + EntityDescriptorRepository entityDescriptorRepository + + @Autowired + EntityDescriptorService entityDescriptorService + + @Autowired + PlatformTransactionManager txMgr + + def "versioning service returns correct number of versions sorted by modified date in natural order"() { + when: 'Initial version' + EntityDescriptor ed = new EntityDescriptor(entityID: 'ed', serviceProviderName: 'SP1') + ed = EnversTestsSupport.doInExplicitTransaction(txMgr) { + entityDescriptorRepository.save(ed) + } + def versions = entityDescriptorVersionService.findVersionsForEntityDescriptor(ed.resourceId) + + then: + versions.size() == 1 + versions[0].id + versions[0].creator + versions[0].date < ZonedDateTime.now() + + when: 'Second version' + ed.serviceProviderName = 'SP2' + ed = edu.internet2.tier.shibboleth.admin.ui.repository.envers.EnversTestsSupport.doInExplicitTransaction(txMgr) { + entityDescriptorRepository.save(ed) + } + versions = entityDescriptorVersionService.findVersionsForEntityDescriptor(ed.resourceId) + + then: + versions.size() == 2 + versions[0].id && versions[1].id + versions[0].creator && versions[1].creator + versions[0].date < versions[1].date + + when: 'Third version' + ed.serviceProviderName = 'SP3' + ed = edu.internet2.tier.shibboleth.admin.ui.repository.envers.EnversTestsSupport.doInExplicitTransaction(txMgr) { + entityDescriptorRepository.save(ed) + } + versions = entityDescriptorVersionService.findVersionsForEntityDescriptor(ed.resourceId) + + then: + versions.size() == 3 + versions[0].id && versions[1].id && versions[2].id + versions[0].creator && versions[1].creator && versions[2].creator + (versions[0].date < versions[1].date) && (versions[1].date < versions[2].date) + } + + def "versioning service returns correct entity descriptor for version number"() { + when: 'Initial version' + EntityDescriptor ed = new EntityDescriptor(entityID: 'ed', serviceProviderName: 'SP1', createdBy: 'anonymousUser') + ed = edu.internet2.tier.shibboleth.admin.ui.repository.envers.EnversTestsSupport.doInExplicitTransaction(txMgr) { + entityDescriptorRepository.save(ed) + } + def versions = entityDescriptorVersionService.findVersionsForEntityDescriptor(ed.resourceId) + def v1EdRepresentation = entityDescriptorVersionService.findSpecificVersionOfEntityDescriptor(ed.resourceId, versions[0].id) + + then: + v1EdRepresentation.serviceProviderName == 'SP1' + v1EdRepresentation.id == ed.resourceId + + when: 'Update the original' + ed.serviceProviderName = 'SP2' + ed = edu.internet2.tier.shibboleth.admin.ui.repository.envers.EnversTestsSupport.doInExplicitTransaction(txMgr) { + entityDescriptorRepository.save(ed) + } + versions = entityDescriptorVersionService.findVersionsForEntityDescriptor(ed.resourceId) + def v2EdRepresentation = entityDescriptorVersionService.findSpecificVersionOfEntityDescriptor(ed.resourceId, versions[1].id) + + then: + v2EdRepresentation.serviceProviderName == 'SP2' + v2EdRepresentation.id == ed.resourceId + } + + def "versioning service returns null for non existent version number"() { + when: 'Initial version' + EntityDescriptor ed = new EntityDescriptor(entityID: 'ed', serviceProviderName: 'SP1', createdBy: 'anonymousUser') + ed = edu.internet2.tier.shibboleth.admin.ui.repository.envers.EnversTestsSupport.doInExplicitTransaction(txMgr) { + entityDescriptorRepository.save(ed) + } + def edRepresentation = entityDescriptorVersionService.findSpecificVersionOfEntityDescriptor(ed.resourceId, '1000') + + then: + !edRepresentation + } +} 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 new file mode 100644 index 000000000..8f68922ed --- /dev/null +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/service/envers/EnversMetadataResolverVersionServiceTests.groovy @@ -0,0 +1,137 @@ +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.FilesystemMetadataResolver +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.domain.resolvers.ResourceBackedMetadataResolver +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.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.EnableJpaRepositories +import org.springframework.test.context.ContextConfiguration +import org.springframework.transaction.PlatformTransactionManager +import spock.lang.Specification + +import java.time.LocalDateTime +import java.time.ZonedDateTime + + +@DataJpaTest +@ContextConfiguration(classes = [CoreShibUiConfiguration, InternationalizationConfiguration, TestConfiguration, SearchConfiguration, EntitiesVersioningConfiguration]) +@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) +@EntityScan("edu.internet2.tier.shibboleth.admin.ui") +class EnversMetadataResolverVersionServiceTests extends Specification { + + @Autowired + MetadataResolverVersionService metadataResolverVersionService + + @Autowired + MetadataResolverRepository metadataResolverRepository + + @Autowired + PlatformTransactionManager txMgr + + def "versioning service returns correct number of versions sorted by modified date in natural order"() { + when: 'Initial version' + MetadataResolver mr = new LocalDynamicMetadataResolver(name: 'ldmr') + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + def versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + + then: + versions.size() == 1 + versions[0].id + versions[0].creator + versions[0].date < ZonedDateTime.now() + + when: 'Second version' + mr.name = 'ldmr2' + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + + + then: + versions.size() == 2 + versions[0].id && versions[1].id + versions[0].creator && versions[1].creator + versions[0].date < versions[1].date + + when: 'Third version' + mr.name = 'ldmr3' + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + + then: + versions.size() == 3 + versions[0].id && versions[1].id && versions[2].id + versions[0].creator && versions[1].creator && versions[2].creator + (versions[0].date < versions[1].date) && (versions[1].date < versions[2].date) + } + + def "versioning service returns correct metadata resolver for version number"() { + when: 'Initial version' + MetadataResolver mr = new FilesystemMetadataResolver(name: 'fsmr') + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + def versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + def v1Mr = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[0].id) + + then: + v1Mr.name == 'fsmr' + v1Mr.resourceId == mr.resourceId + + when: 'Update the original' + mr.name = 'fsmr2' + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + def v2Mr = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[1].id) + + then: + v2Mr.name == 'fsmr2' + v2Mr.resourceId == mr.resourceId + } + + def "versioning service returns null for non existent version number"() { + when: 'Initial version' + MetadataResolver mr = new ResourceBackedMetadataResolver(name: 'rbmr') + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + def nonexitentMrVersion = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, '1000') + + then: + !nonexitentMrVersion + } + + def "versioning service returns null for non existent metadata resolver number"() { + when: 'Initial version' + MetadataResolver mr = new DynamicHttpMetadataResolver(name: 'dhmr') + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + def versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + def nonexitentMr = metadataResolverVersionService.findSpecificVersionOfMetadataResolver('non-existent', versions[0].id) + + then: + !nonexitentMr + } +} diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/DevConfig.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/DevConfig.groovy index 8db64fd67..e6ef57980 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/DevConfig.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/DevConfig.groovy @@ -9,6 +9,7 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.HttpMetadataResol import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataQueryProtocolScheme import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ReloadableMetadataResolverAttributes +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.repository.MetadataResolverRepository import edu.internet2.tier.shibboleth.admin.ui.security.model.Role @@ -16,6 +17,7 @@ import edu.internet2.tier.shibboleth.admin.ui.security.model.User 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.util.ModelRepresentationConversions + import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Profile import org.springframework.stereotype.Component @@ -32,11 +34,19 @@ class DevConfig { private final MetadataResolverRepository metadataResolverRepository private final EntityDescriptorRepository entityDescriptorRepository - DevConfig(UserRepository adminUserRepository, MetadataResolverRepository metadataResolverRepository, RoleRepository roleRepository, EntityDescriptorRepository entityDescriptorRepository) { + private final OpenSamlObjects openSamlObjects + + DevConfig(UserRepository adminUserRepository, + MetadataResolverRepository metadataResolverRepository, + RoleRepository roleRepository, + EntityDescriptorRepository entityDescriptorRepository, + OpenSamlObjects openSamlObjects) { + this.adminUserRepository = adminUserRepository this.metadataResolverRepository = metadataResolverRepository this.roleRepository = roleRepository this.entityDescriptorRepository = entityDescriptorRepository + this.openSamlObjects = openSamlObjects } @Transactional diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/jsonschema/RelyingPartyOverridesJsonSchemaValidatingControllerAdvice.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/jsonschema/RelyingPartyOverridesJsonSchemaValidatingControllerAdvice.groovy index 9151e4ef4..620d2252c 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/jsonschema/RelyingPartyOverridesJsonSchemaValidatingControllerAdvice.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/jsonschema/RelyingPartyOverridesJsonSchemaValidatingControllerAdvice.groovy @@ -1,16 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.jsonschema -import edu.internet2.tier.shibboleth.admin.ui.controller.ErrorResponse + import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation import mjson.Json import org.springframework.beans.factory.annotation.Autowired import org.springframework.core.MethodParameter import org.springframework.http.HttpInputMessage -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity import org.springframework.http.converter.HttpMessageConverter import org.springframework.web.bind.annotation.ControllerAdvice -import org.springframework.web.bind.annotation.ExceptionHandler import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter import javax.annotation.PostConstruct diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java index 2bd90bcc3..42e7901e7 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java @@ -23,7 +23,7 @@ @SpringBootApplication @ComponentScan(excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "edu.internet2.tier.shibboleth.admin.ui.configuration.auto.*")) -@EntityScan(basePackages = {"edu.internet2.tier.shibboleth.admin.ui.domain", "edu.internet2.tier.shibboleth.admin.ui.security.model"}) +@EntityScan(basePackages = {"edu.internet2.tier.shibboleth.admin.ui.domain", "edu.internet2.tier.shibboleth.admin.ui.envers", "edu.internet2.tier.shibboleth.admin.ui.security.model"}) @EnableJpaAuditing @EnableScheduling @EnableWebSecurity 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 new file mode 100644 index 000000000..ec21d42c4 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/EntitiesVersioningConfiguration.java @@ -0,0 +1,35 @@ +package edu.internet2.tier.shibboleth.admin.ui.configuration; + +import edu.internet2.tier.shibboleth.admin.ui.envers.EnversVersionServiceSupport; +import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService; +import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorVersionService; +import edu.internet2.tier.shibboleth.admin.ui.service.EnversEntityDescriptorVersionService; +import edu.internet2.tier.shibboleth.admin.ui.service.EnversMetadataResolverVersionService; +import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverVersionService; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +@Configuration +public class EntitiesVersioningConfiguration { + + @PersistenceContext + private EntityManager entityManager; + + @Bean + public EntityDescriptorVersionService entityDescriptorVersionService(EntityDescriptorService entityDescriptorService) { + return new EnversEntityDescriptorVersionService(entityManager, entityDescriptorService); + } + + @Bean + public MetadataResolverVersionService metadataResolverVersionService() { + return new EnversMetadataResolverVersionService(enversVersionServiceSupport()); + } + + @Bean + public EnversVersionServiceSupport enversVersionServiceSupport() { + return new EnversVersionServiceSupport(entityManager); + } +} 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 80213f5cd..8bb0da84f 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 @@ -2,13 +2,13 @@ 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.opensaml.OpenSamlObjects; import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository; import edu.internet2.tier.shibboleth.admin.ui.security.model.User; -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 edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService; +import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorVersionService; import org.opensaml.core.xml.io.MarshallingException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,8 +32,8 @@ import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import javax.annotation.PostConstruct; -import javax.xml.ws.Response; import java.net.URI; +import java.util.List; import java.util.stream.Collectors; @RestController @@ -52,20 +52,17 @@ public class EntityDescriptorController { @Autowired RestTemplateBuilder restTemplateBuilder; - private UserRepository userRepository; - - private RoleRepository roleRepository; - private UserService userService; private RestTemplate restTemplate; + private EntityDescriptorVersionService versionService; + private static Logger LOGGER = LoggerFactory.getLogger(EntityDescriptorController.class); - public EntityDescriptorController(UserRepository userRepository, RoleRepository roleRepository, UserService userService) { - this.userRepository = userRepository; - this.roleRepository = roleRepository; + public EntityDescriptorController(UserService userService, EntityDescriptorVersionService versionService) { this.userService = userService; + this.versionService = versionService; } @PostConstruct @@ -220,6 +217,40 @@ public ResponseEntity deleteOne(@PathVariable String resourceId) { } } + //Versioning endpoints + + @GetMapping("/EntityDescriptor/{resourceId}/Versions") + 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); + } + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + + @GetMapping("/EntityDescriptor/{resourceId}/Versions/{versionId}") + public ResponseEntity getSpecificVersion(@PathVariable String resourceId, @PathVariable String versionId) { + EntityDescriptorRepresentation edRepresentation = + versionService.findSpecificVersionOfEntityDescriptor(resourceId, versionId); + + if (edRepresentation == null) { + return ResponseEntity.notFound().build(); + } + if(isAuthorizedFor(edRepresentation.getCreatedBy())) { + return ResponseEntity.ok(edRepresentation); + } + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + + //Private methods + private static URI getResourceUriFor(EntityDescriptor ed) { return ServletUriComponentsBuilder .fromCurrentServletMapping().path("/api/EntityDescriptor") @@ -267,4 +298,11 @@ 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))); + } + } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java index 57d734f77..cbf5051ba 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java @@ -7,16 +7,10 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.filters.RequiredValidUntilFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.SignatureValidationFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver; -import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.opensaml.OpenSamlFunctionDrivenDynamicHTTPMetadataResolver; -import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.opensaml.OpenSamlLocalDynamicMetadataResolver; import edu.internet2.tier.shibboleth.admin.ui.repository.FilterRepository; import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService; -import net.shibboleth.utilities.java.support.component.ComponentInitializationException; -import net.shibboleth.utilities.java.support.resolver.ResolverException; -import org.opensaml.saml.metadata.resolver.ChainingMetadataResolver; -import org.opensaml.saml.metadata.resolver.RefreshableMetadataResolver; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -36,6 +30,7 @@ import java.net.URI; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.function.Supplier; import java.util.stream.Stream; @@ -102,19 +97,19 @@ public ResponseEntity create(@PathVariable String metadataResolverId, @Reques public ResponseEntity update(@PathVariable String metadataResolverId, @PathVariable String resourceId, @RequestBody MetadataFilter updatedFilter) { - MetadataFilter filterTobeUpdated = filterRepository.findByResourceId(resourceId); - if (filterTobeUpdated == null) { - return ResponseEntity.notFound().build(); - } MetadataResolver metadataResolver = findResolverOrThrowHttp404(metadataResolverId); - // check to make sure that the relationship exists - if (!metadataResolver.getMetadataFilters().contains(filterTobeUpdated)) { - // TODO: find a better response - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + //Now we operate directly on the filter attached to MetadataResolver, + //Instead of fetching filter separately, to accommodate correct envers versioning with uni-directional one-to-many + Optional filterTobeUpdatedOptional = metadataResolver.getMetadataFilters() + .stream() + .filter(it -> it.getResourceId().equals(resourceId)) + .findFirst(); + if(!filterTobeUpdatedOptional.isPresent()) { + return ResponseEntity.notFound().build(); } - + MetadataFilter filterTobeUpdated = filterTobeUpdatedOptional.get(); if (!resourceId.equals(updatedFilter.getResourceId())) { return new ResponseEntity(HttpStatus.CONFLICT); } @@ -130,6 +125,10 @@ public ResponseEntity update(@PathVariable String metadataResolverId, MetadataFilter persistedFilter = filterRepository.save(filterTobeUpdated); + //To support envers versioning from MetadataResolver side + metadataResolver.markAsModified(); + repository.save(metadataResolver); + // TODO: this is wrong metadataResolverService.reloadFilters(metadataResolver.getResourceId()); @@ -244,4 +243,4 @@ private static URI getResourceUriFor(MetadataResolver mr, String filterResourceI .build() .toUri(); } -} \ No newline at end of file +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java index 952c118c2..dbdc0ddd1 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java @@ -5,10 +5,12 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver; import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolverValidationService; import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.opensaml.OpenSamlChainingMetadataResolver; +import edu.internet2.tier.shibboleth.admin.ui.domain.versioning.Version; import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; import edu.internet2.tier.shibboleth.admin.ui.service.IndexWriterService; import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverConverterService; import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService; +import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverVersionService; import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolversPositionOrderContainerService; import edu.internet2.tier.shibboleth.admin.util.OpenSamlChainingMetadataResolverUtil; import lombok.extern.slf4j.Slf4j; @@ -69,6 +71,9 @@ public class MetadataResolversController { @Autowired MetadataResolverConverterService metadataResolverConverterService; + @Autowired + MetadataResolverVersionService versionService; + @ExceptionHandler({InvalidTypeIdException.class, IOException.class, HttpMessageNotReadableException.class}) public ResponseEntity unableToParseJson(Exception ex) { return ResponseEntity.badRequest().body(new ErrorResponse(HttpStatus.BAD_REQUEST.toString(), ex.getMessage(), ex.getCause().getMessage())); @@ -149,6 +154,30 @@ public ResponseEntity update(@PathVariable String resourceId, @RequestBody Me return ResponseEntity.ok(persistedResolver); } + //Versioning endpoints + + @GetMapping("/MetadataResolvers/{resourceId}/Versions") + public ResponseEntity getAllVersions(@PathVariable String resourceId) { + MetadataResolver resolver = resolverRepository.findByResourceId(resourceId); + if (resolver == null) { + return ResponseEntity.notFound().build(); + } + List versions = versionService.findVersionsForMetadataResolver(resourceId); + if (versions.isEmpty()) { + return ResponseEntity.notFound().build(); + } + return ResponseEntity.ok(versions); + } + + @GetMapping("/MetadataResolvers/{resourceId}/Versions/{versionId}") + public ResponseEntity getSpecificVersion(@PathVariable String resourceId, @PathVariable String versionId) { + MetadataResolver resolver = versionService.findSpecificVersionOfMetadataResolver(resourceId, versionId); + if (resolver == null) { + return ResponseEntity.notFound().build(); + } + return ResponseEntity.ok(resolver); + } + @SuppressWarnings("Unchecked") private ResponseEntity validate(MetadataResolver metadataResolver) { ValidationResult validationResult = metadataResolverValidationService.validateIfNecessary(metadataResolver); @@ -159,6 +188,8 @@ private ResponseEntity validate(MetadataResolver metadataResolver) { return null; } + //Private methods + private static URI getResourceUriFor(MetadataResolver resolver) { return ServletUriComponentsBuilder .fromCurrentServletMapping().path("/api/MetadataResolvers/") diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractAttributeExtensibleXMLObject.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractAttributeExtensibleXMLObject.java index 3e029e17e..62bb46df8 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractAttributeExtensibleXMLObject.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractAttributeExtensibleXMLObject.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.AttributeExtensibleXMLObject; import org.opensaml.core.xml.util.AttributeMap; @@ -16,6 +17,7 @@ @MappedSuperclass @EqualsAndHashCode(callSuper = true, exclude={"storageAttributeMap"}) +@Audited public abstract class AbstractAttributeExtensibleXMLObject extends AbstractXMLObject implements AttributeExtensibleXMLObject { private transient final AttributeMap unknownAttributes = new AttributeMap(this); 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 1d23d5113..567a6637e 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 @@ -3,6 +3,7 @@ import lombok.EqualsAndHashCode; import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; +import org.hibernate.envers.Audited; import org.springframework.data.annotation.CreatedBy; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedBy; @@ -22,6 +23,7 @@ @MappedSuperclass @EntityListeners(AuditingEntityListener.class) @EqualsAndHashCode +@Audited public abstract class AbstractAuditable implements Auditable { @Id diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractDescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractDescriptor.java index 1f6075f24..449cda701 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractDescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractDescriptor.java @@ -2,6 +2,7 @@ import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; +import org.hibernate.envers.Audited; import org.joda.time.DateTime; import org.opensaml.core.xml.XMLObject; import org.opensaml.saml.saml2.common.CacheableSAMLObject; @@ -18,6 +19,7 @@ @MappedSuperclass @EqualsAndHashCode(callSuper = true) +@Audited public abstract class AbstractDescriptor extends AbstractAttributeExtensibleXMLObject implements CacheableSAMLObject, TimeBoundSAMLObject, SignableXMLObject { private Long cacheDuration; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractElementExtensibleXMLObject.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractElementExtensibleXMLObject.java index d406e8256..03a91e284 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractElementExtensibleXMLObject.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractElementExtensibleXMLObject.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.ElementExtensibleXMLObject; import org.opensaml.core.xml.XMLObject; @@ -20,6 +21,7 @@ @Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) @EqualsAndHashCode(callSuper = true) +@Audited public abstract class AbstractElementExtensibleXMLObject extends AbstractXMLObject implements ElementExtensibleXMLObject { @OneToMany(cascade = CascadeType.ALL) @OrderColumn diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractLangBearingURL.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractLangBearingURL.java index 7f66a06c6..96fb81a69 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractLangBearingURL.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractLangBearingURL.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import org.hibernate.envers.Audited; import org.opensaml.saml.saml2.metadata.LocalizedURI; import javax.annotation.Nullable; @@ -10,6 +11,7 @@ @Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) +@Audited abstract class AbstractLangBearingURL extends XSURI implements LocalizedURI { @Column(name = "informationUrlXmlLang") private String xmlLang; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractXMLObject.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractXMLObject.java index 1e8f4c2a0..3b4ac0d92 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractXMLObject.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AbstractXMLObject.java @@ -3,6 +3,8 @@ import lombok.EqualsAndHashCode; import net.shibboleth.utilities.java.support.collection.LockableClassToInstanceMultiMap; import net.shibboleth.utilities.java.support.xml.QNameSupport; +import org.hibernate.envers.AuditOverride; +import org.hibernate.envers.Audited; import org.opensaml.core.config.ConfigurationService; import org.opensaml.core.xml.Namespace; import org.opensaml.core.xml.NamespaceManager; @@ -30,6 +32,8 @@ @Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) @EqualsAndHashCode(callSuper = true) +@Audited +@AuditOverride(forClass = AbstractAuditable.class) public abstract class AbstractXMLObject extends AbstractAuditable implements XMLObject { private String namespaceURI; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ArtifactResolutionService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ArtifactResolutionService.java index 0d82f8a0d..1231299a7 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ArtifactResolutionService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ArtifactResolutionService.java @@ -1,11 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class ArtifactResolutionService extends IndexedEndpoint implements org.opensaml.saml.saml2.metadata.ArtifactResolutionService { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AssertionConsumerService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AssertionConsumerService.java index a89045075..f63a76fcd 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AssertionConsumerService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AssertionConsumerService.java @@ -1,11 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class AssertionConsumerService extends IndexedEndpoint implements org.opensaml.saml.saml2.metadata.AssertionConsumerService { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Attribute.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Attribute.java index 45e1500e9..1a9a28323 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Attribute.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Attribute.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.XMLObject; import javax.annotation.Nullable; @@ -15,6 +16,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class Attribute extends AbstractAttributeExtensibleXMLObject implements org.opensaml.saml.saml2.core.Attribute { private String name; @@ -25,6 +27,7 @@ public class Attribute extends AbstractAttributeExtensibleXMLObject implements o @OneToMany(cascade = CascadeType.ALL) @OrderColumn + @Audited private List attributeValues = new ArrayList<>(); @Override diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeConsumingService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeConsumingService.java index 5080c1fc0..a1dba47fa 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeConsumingService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/AttributeConsumingService.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.schema.XSBooleanValue; import javax.persistence.CascadeType; @@ -14,6 +15,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class AttributeConsumingService extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.AttributeConsumingService { private int acsIndex; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ContactPerson.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ContactPerson.java index 104dc076f..ca0400f32 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ContactPerson.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ContactPerson.java @@ -1,6 +1,8 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; +import org.hibernate.envers.NotAudited; import org.opensaml.core.xml.XMLObject; import org.opensaml.saml.saml2.metadata.ContactPersonTypeEnumeration; @@ -12,20 +14,24 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class ContactPerson extends AbstractAttributeExtensibleXMLObject implements org.opensaml.saml.saml2.metadata.ContactPerson { private String contactPersonType; @OneToOne(cascade = CascadeType.ALL) + @NotAudited private Extensions extensions; @OneToOne(cascade = CascadeType.ALL) + @NotAudited private Company company; @OneToOne(cascade = CascadeType.ALL) private GivenName givenName; @OneToOne(cascade = CascadeType.ALL) + @NotAudited private SurName surName; @OneToMany(cascade = CascadeType.ALL) @@ -36,6 +42,7 @@ public class ContactPerson extends AbstractAttributeExtensibleXMLObject implemen @OneToMany(cascade = CascadeType.ALL) @JoinColumn(name = "contactpersn_telenmbr_id") @OrderColumn + @NotAudited private List telephoneNumbers = new ArrayList<>(); @Override diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Description.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Description.java index 81b66542d..db994740a 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Description.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Description.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.annotation.Nullable; import javax.persistence.Column; @@ -8,6 +9,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class Description extends AbstractXMLObject implements org.opensaml.saml.ext.saml2mdui.Description { @Column(name = "descriptionXMLLang") diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/DisplayName.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/DisplayName.java index 564b0b0f6..5ee4815d9 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/DisplayName.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/DisplayName.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.annotation.Nullable; import javax.persistence.Column; @@ -8,6 +9,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class DisplayName extends AbstractXMLObject implements org.opensaml.saml.ext.saml2mdui.DisplayName { @Column(name = "displayNameXMLLan") diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EmailAddress.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EmailAddress.java index 313ebd65e..2cf346579 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EmailAddress.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EmailAddress.java @@ -1,11 +1,14 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.AuditOverride; +import org.hibernate.envers.Audited; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class EmailAddress extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.EmailAddress { private String address; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EncryptionMethod.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EncryptionMethod.java index 688dea31d..06a47da63 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EncryptionMethod.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EncryptionMethod.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.xmlsec.encryption.KeySize; import org.opensaml.xmlsec.encryption.OAEPparams; @@ -10,6 +11,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class EncryptionMethod extends AbstractElementExtensibleXMLObject implements org.opensaml.saml.saml2.metadata.EncryptionMethod { private String algorithm; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Endpoint.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Endpoint.java index 97094187a..2699895a0 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Endpoint.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Endpoint.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.XMLObject; import javax.annotation.Nonnull; @@ -14,6 +15,7 @@ */ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class Endpoint extends AbstractAttributeExtensibleXMLObject implements org.opensaml.saml.saml2.metadata.Endpoint { private String binding; @@ -72,4 +74,4 @@ public List getUnknownXMLObjects() { public List getUnknownXMLObjects(@Nonnull QName qName) { return null; //TODO } -} \ No newline at end of file +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityAttributes.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityAttributes.java index ed97305fc..d48a3ea6f 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityAttributes.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/EntityAttributes.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.XMLObject; import org.opensaml.saml.saml2.core.Assertion; @@ -16,6 +17,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class EntityAttributes extends AbstractElementExtensibleXMLObject implements org.opensaml.saml.ext.saml2mdattr.EntityAttributes { @OneToMany(cascade = CascadeType.ALL) 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 2f53e5cc8..dc036fa7f 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 @@ -2,27 +2,21 @@ import com.google.common.base.MoreObjects; import com.google.common.collect.Lists; - import lombok.EqualsAndHashCode; -import org.opensaml.core.config.ConfigurationService; +import org.hibernate.envers.Audited; +import org.hibernate.envers.NotAudited; import org.opensaml.core.xml.XMLObject; -import org.opensaml.core.xml.config.XMLObjectProviderRegistry; -import org.opensaml.core.xml.io.MarshallingException; import org.springframework.util.StringUtils; -import org.w3c.dom.Element; import javax.annotation.Nullable; - -import javax.persistence.JoinColumn; import javax.persistence.CascadeType; import javax.persistence.Entity; +import javax.persistence.JoinColumn; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.OrderColumn; import javax.persistence.Transient; - import javax.xml.namespace.QName; - import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -32,7 +26,8 @@ @Entity -@EqualsAndHashCode(callSuper = true) +@EqualsAndHashCode(callSuper = true, exclude={"versionModifiedTimestamp"}) +@Audited public class EntityDescriptor extends AbstractDescriptor implements org.opensaml.saml.saml2.metadata.EntityDescriptor { private String localId; @@ -44,6 +39,8 @@ public class EntityDescriptor extends AbstractDescriptor implements org.opensaml private String resourceId; + private Long versionModifiedTimestamp; + @OneToOne(cascade = CascadeType.ALL) private Organization organization; @@ -58,18 +55,23 @@ public class EntityDescriptor extends AbstractDescriptor implements org.opensaml @OneToMany(cascade = CascadeType.ALL) @JoinColumn(name = "entitydesc_addlmetdatlocations_id") @OrderColumn + @NotAudited private List additionalMetadataLocations = new ArrayList<>(); @OneToOne(cascade = CascadeType.ALL) + @NotAudited private AuthnAuthorityDescriptor authnAuthorityDescriptor; @OneToOne(cascade = CascadeType.ALL) + @NotAudited private AttributeAuthorityDescriptor attributeAuthorityDescriptor; @OneToOne(cascade = CascadeType.ALL) + @NotAudited private PDPDescriptor pdpDescriptor; @OneToOne(cascade = CascadeType.ALL) + @NotAudited private AffiliationDescriptor affiliationDescriptor; public EntityDescriptor() { @@ -77,6 +79,10 @@ public EntityDescriptor() { this.resourceId = UUID.randomUUID().toString(); } + public void setVersionModifiedTimestamp(Long versionModifiedTimestamp) { + this.versionModifiedTimestamp = versionModifiedTimestamp; + } + //getters and setters @Override public String getID() { diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Extensions.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Extensions.java index c1538f463..01dcf4aa8 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Extensions.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Extensions.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.XMLObject; import javax.annotation.Nullable; @@ -13,6 +14,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class Extensions extends AbstractElementExtensibleXMLObject implements org.opensaml.saml.saml2.metadata.Extensions { @Nullable @Override diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/GivenName.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/GivenName.java index 785b60804..612733fc4 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/GivenName.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/GivenName.java @@ -1,11 +1,14 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.AuditOverride; +import org.hibernate.envers.Audited; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class GivenName extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.GivenName { private String name; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/IndexedEndpoint.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/IndexedEndpoint.java index e8d8f1551..cc0fdd6d5 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/IndexedEndpoint.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/IndexedEndpoint.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.schema.XSBooleanValue; import javax.persistence.Entity; @@ -8,6 +9,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class IndexedEndpoint extends Endpoint implements org.opensaml.saml.saml2.metadata.IndexedEndpoint { private Integer endpointIndex; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/InformationURL.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/InformationURL.java index 45d5477d1..e960e8463 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/InformationURL.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/InformationURL.java @@ -1,10 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class InformationURL extends AbstractLangBearingURL implements org.opensaml.saml.ext.saml2mdui.InformationURL { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyDescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyDescriptor.java index 74ddbaa2c..2b07eea4c 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyDescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyDescriptor.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.XMLObject; import org.opensaml.security.credential.UsageType; import org.opensaml.xmlsec.signature.KeyInfo; @@ -12,6 +13,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class KeyDescriptor extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.KeyDescriptor { @Column(name = "keyDescriptorName") diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyInfo.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyInfo.java index 7ea604d17..7497e5837 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyInfo.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/KeyInfo.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.XMLObject; import org.opensaml.xmlsec.encryption.AgreementMethod; import org.opensaml.xmlsec.encryption.EncryptedKey; @@ -29,6 +30,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class KeyInfo extends AbstractXMLObject implements org.opensaml.xmlsec.signature.KeyInfo { @OneToMany(cascade = CascadeType.ALL) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/LocalizedName.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/LocalizedName.java index 2aa493293..e0a54d494 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/LocalizedName.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/LocalizedName.java @@ -1,9 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import org.hibernate.envers.Audited; + import javax.annotation.Nullable; import javax.persistence.MappedSuperclass; @MappedSuperclass +@Audited public class LocalizedName extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.LocalizedName { private String xMLLang; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Logo.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Logo.java index b9173bb22..13453438a 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Logo.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Logo.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.annotation.Nullable; import javax.persistence.Column; @@ -8,6 +9,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class Logo extends AbstractXMLObject implements org.opensaml.saml.ext.saml2mdui.Logo { @Column(name = "logUrl") private String url; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ManageNameIDService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ManageNameIDService.java index ec43780d3..70924db96 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ManageNameIDService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ManageNameIDService.java @@ -1,11 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class ManageNameIDService extends Endpoint implements org.opensaml.saml.saml2.metadata.ManageNameIDService { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/NameIDFormat.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/NameIDFormat.java index 4e63ff301..148ec91e7 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/NameIDFormat.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/NameIDFormat.java @@ -1,11 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class NameIDFormat extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.NameIDFormat { private String format; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Organization.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Organization.java index 1e11b499e..7ea76b1f0 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Organization.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/Organization.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.XMLObject; import javax.annotation.Nullable; @@ -11,6 +12,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class Organization extends AbstractAttributeExtensibleXMLObject implements org.opensaml.saml.saml2.metadata.Organization { @OneToOne(cascade = CascadeType.ALL) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationDisplayName.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationDisplayName.java index 8d473aa82..971217fd8 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationDisplayName.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationDisplayName.java @@ -1,11 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class OrganizationDisplayName extends LocalizedName implements org.opensaml.saml.saml2.metadata.OrganizationDisplayName { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationName.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationName.java index edc9317c1..5af2408b9 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationName.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationName.java @@ -1,11 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class OrganizationName extends LocalizedName implements org.opensaml.saml.saml2.metadata.OrganizationName { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationURL.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationURL.java index 6af0d7318..1cf739ece 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationURL.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/OrganizationURL.java @@ -1,12 +1,14 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.annotation.Nullable; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class OrganizationURL extends AbstractXMLObject implements org.opensaml.saml.saml2.metadata.OrganizationURL { private String xMLLang; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/PrivacyStatementURL.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/PrivacyStatementURL.java index a6d792d80..a98f5a7ad 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/PrivacyStatementURL.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/PrivacyStatementURL.java @@ -1,10 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class PrivacyStatementURL extends AbstractLangBearingURL implements org.opensaml.saml.ext.saml2mdui.PrivacyStatementURL { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RequestedAttribute.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RequestedAttribute.java index a7860e931..17c932ed2 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RequestedAttribute.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RequestedAttribute.java @@ -1,12 +1,14 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.schema.XSBooleanValue; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class RequestedAttribute extends Attribute implements org.opensaml.saml.saml2.metadata.RequestedAttribute { private boolean isRequired; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RoleDescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RoleDescriptor.java index fc235c110..b5bc78bc6 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RoleDescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/RoleDescriptor.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.XMLObject; import org.opensaml.core.xml.util.AttributeMap; @@ -25,6 +26,7 @@ @Entity @EqualsAndHashCode(callSuper = true, exclude={"unknownAttributes"}) +@Audited public class RoleDescriptor extends AbstractDescriptor implements org.opensaml.saml.saml2.metadata.RoleDescriptor { @ElementCollection diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SPSSODescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SPSSODescriptor.java index 379c2c928..e90542c5a 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SPSSODescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SPSSODescriptor.java @@ -2,6 +2,7 @@ import com.google.common.collect.Lists; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.XMLObject; import org.opensaml.core.xml.schema.XSBooleanValue; @@ -14,6 +15,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class SPSSODescriptor extends SSODescriptor implements org.opensaml.saml.saml2.metadata.SPSSODescriptor { private Boolean isAuthnRequestsSigned; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SSODescriptor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SSODescriptor.java index 1f34fc931..4e885581d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SSODescriptor.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SSODescriptor.java @@ -2,6 +2,7 @@ import com.google.common.collect.Lists; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.XMLObject; import javax.annotation.Nullable; @@ -17,6 +18,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class SSODescriptor extends RoleDescriptor implements org.opensaml.saml.saml2.metadata.SSODescriptor { @OneToMany(cascade = CascadeType.ALL) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ServiceDescription.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ServiceDescription.java index 4c3a54511..9fda942ae 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ServiceDescription.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ServiceDescription.java @@ -1,11 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class ServiceDescription extends LocalizedName implements org.opensaml.saml.saml2.metadata.ServiceDescription { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ServiceName.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ServiceName.java index b4047f0c8..7e23c2b67 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ServiceName.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/ServiceName.java @@ -1,11 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class ServiceName extends LocalizedName implements org.opensaml.saml.saml2.metadata.ServiceName { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SingleLogoutService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SingleLogoutService.java index 67967676e..ca9aa095c 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SingleLogoutService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/SingleLogoutService.java @@ -1,11 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class SingleLogoutService extends Endpoint implements org.opensaml.saml.saml2.metadata.SingleLogoutService { } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/UIInfo.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/UIInfo.java index f7912aa65..241eebfac 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/UIInfo.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/UIInfo.java @@ -3,6 +3,7 @@ import lombok.EqualsAndHashCode; import org.hibernate.annotations.Cascade; import org.hibernate.annotations.CascadeType; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.XMLObject; import org.opensaml.saml.ext.saml2mdui.Description; import org.opensaml.saml.ext.saml2mdui.DisplayName; @@ -22,6 +23,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class UIInfo extends AbstractXMLObject implements org.opensaml.saml.ext.saml2mdui.UIInfo { @OneToMany @Cascade(CascadeType.ALL) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Certificate.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Certificate.java index 2fe5ea571..b1db21b72 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Certificate.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Certificate.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.annotation.Nullable; import javax.persistence.Column; @@ -9,6 +10,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class X509Certificate extends AbstractXMLObject implements org.opensaml.xmlsec.signature.X509Certificate { @Column(name = "x509CertificateValue") @Lob diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Data.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Data.java index 46c58324a..e875932cd 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Data.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/X509Data.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.XMLObject; import org.opensaml.xmlsec.signature.X509CRL; import org.opensaml.xmlsec.signature.X509Certificate; @@ -23,6 +24,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class X509Data extends AbstractXMLObject implements org.opensaml.xmlsec.signature.X509Data { @OneToMany(cascade = CascadeType.ALL) @OrderColumn diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSAny.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSAny.java index 1c1d32f4b..de81fcdf6 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSAny.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSAny.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.util.AttributeMap; import javax.annotation.Nonnull; @@ -10,6 +11,7 @@ @Entity @EqualsAndHashCode(callSuper = true, exclude = {"unknownAttributes"}) +@Audited public class XSAny extends AbstractElementExtensibleXMLObject implements org.opensaml.core.xml.schema.XSAny { private String textContext; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSBase64Binary.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSBase64Binary.java index 0d886d52a..edfd8eec8 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSBase64Binary.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSBase64Binary.java @@ -1,12 +1,14 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.annotation.Nullable; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class XSBase64Binary extends AbstractXMLObject implements org.opensaml.core.xml.schema.XSBase64Binary { private String b64value; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSBoolean.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSBoolean.java index 72399bf06..8b0f258d1 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSBoolean.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSBoolean.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.opensaml.core.xml.schema.XSBooleanValue; import javax.annotation.Nullable; @@ -9,6 +10,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class XSBoolean extends AbstractXMLObject implements org.opensaml.core.xml.schema.XSBoolean { private String storedValue; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSDateTime.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSDateTime.java index 0ea3116f8..1b13a3cd4 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSDateTime.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSDateTime.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import org.joda.time.DateTime; import org.joda.time.chrono.ISOChronology; import org.joda.time.format.DateTimeFormatter; @@ -13,6 +14,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class XSDateTime extends AbstractXMLObject implements org.opensaml.core.xml.schema.XSDateTime { private DateTime dateTime; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSInteger.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSInteger.java index 294d0a575..cd6c205da 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSInteger.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSInteger.java @@ -1,12 +1,14 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.annotation.Nullable; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class XSInteger extends AbstractXMLObject implements org.opensaml.core.xml.schema.XSInteger { private int intValue; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSQName.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSQName.java index 259e3df36..b210ea7a4 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSQName.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSQName.java @@ -2,6 +2,7 @@ import lombok.EqualsAndHashCode; import net.shibboleth.utilities.java.support.xml.QNameSupport; +import org.hibernate.envers.Audited; import javax.annotation.Nullable; import javax.persistence.Entity; @@ -10,6 +11,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class XSQName extends AbstractXMLObject implements org.opensaml.core.xml.schema.XSQName { @Nullable @Override diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSString.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSString.java index f35e8e287..dbc0d4c38 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSString.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSString.java @@ -1,12 +1,14 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.annotation.Nullable; import javax.persistence.Entity; @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class XSString extends AbstractXMLObject implements org.opensaml.core.xml.schema.XSString { private String xsStringvalue; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSURI.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSURI.java index 2780c5472..8663bf818 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSURI.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/XSURI.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.EqualsAndHashCode; +import org.hibernate.envers.Audited; import javax.annotation.Nullable; import javax.persistence.Column; @@ -8,6 +9,7 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited public class XSURI extends AbstractXMLObject implements org.opensaml.core.xml.schema.XSURI { @Column(name = "xsuriValue") private String value; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java index 9d68c39b3..5d6d3132a 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java @@ -6,6 +6,7 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; +import org.hibernate.envers.Audited; import javax.persistence.CascadeType; import javax.persistence.Entity; @@ -28,6 +29,7 @@ @Getter @Setter @ToString +@Audited public class EntityAttributesFilter extends MetadataFilter { public EntityAttributesFilter() { @@ -70,4 +72,4 @@ public void intoTransientRepresentation() { this.attributeRelease = getAttributeReleaseListFromAttributeList(this.attributes); this.relyingPartyOverrides = getRelyingPartyOverridesRepresentationFromAttributeList(this.attributes); } -} \ No newline at end of file +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilterTarget.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilterTarget.java index 5effdae3d..0abbbd11c 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilterTarget.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilterTarget.java @@ -1,8 +1,9 @@ package edu.internet2.tier.shibboleth.admin.ui.domain.filters; -import com.fasterxml.jackson.annotation.JsonIgnore; import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; import lombok.EqualsAndHashCode; +import org.hibernate.envers.AuditOverride; +import org.hibernate.envers.Audited; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,6 +15,8 @@ @Entity @EqualsAndHashCode(callSuper = true) +@Audited +@AuditOverride(forClass = AbstractAuditable.class) public class EntityAttributesFilterTarget extends AbstractAuditable { public enum EntityAttributesFilterTargetType { ENTITY, CONDITION_SCRIPT, CONDITION_REF, REGEX diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityRoleWhiteListFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityRoleWhiteListFilter.java index 111b75c2a..26c5e9976 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityRoleWhiteListFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityRoleWhiteListFilter.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; +import org.hibernate.envers.Audited; import javax.persistence.CollectionTable; import javax.persistence.Column; @@ -19,6 +20,7 @@ @Getter @Setter @ToString +@Audited public class EntityRoleWhiteListFilter extends MetadataFilter { public EntityRoleWhiteListFilter() { 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 5ee500437..b15058cd0 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 @@ -10,6 +10,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; +import org.hibernate.envers.AuditOverride; import org.hibernate.envers.Audited; import javax.persistence.Column; @@ -36,6 +37,7 @@ @JsonSubTypes.Type(value=RequiredValidUntilFilter.class, name="RequiredValidUntil"), @JsonSubTypes.Type(value=NameIdFormatFilter.class, name="NameIDFormat")}) @Audited +@AuditOverride(forClass = AbstractAuditable.class) public class MetadataFilter extends AbstractAuditable { @JsonProperty("@type") @@ -59,4 +61,4 @@ public int getVersion() { } return this.hashCode(); } -} \ No newline at end of file +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilter.java index 608561407..157e5f766 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilter.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; +import org.hibernate.envers.Audited; import javax.persistence.CascadeType; import javax.persistence.ElementCollection; @@ -17,6 +18,7 @@ @Getter @Setter @ToString +@Audited public class NameIdFormatFilter extends MetadataFilter { public NameIdFormatFilter() { diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilterTarget.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilterTarget.java index a346d983f..c8fd10c90 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilterTarget.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/NameIdFormatFilterTarget.java @@ -3,6 +3,8 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; import lombok.EqualsAndHashCode; import lombok.ToString; +import org.hibernate.envers.AuditOverride; +import org.hibernate.envers.Audited; import javax.persistence.ElementCollection; import javax.persistence.Entity; @@ -13,6 +15,8 @@ @Entity @EqualsAndHashCode(callSuper = true) @ToString +@Audited +@AuditOverride(forClass = AbstractAuditable.class) public class NameIdFormatFilterTarget extends AbstractAuditable { public enum NameIdFormatFilterTargetType { diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/RequiredValidUntilFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/RequiredValidUntilFilter.java index 1aca84d4b..cda431408 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/RequiredValidUntilFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/RequiredValidUntilFilter.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; +import org.hibernate.envers.Audited; import javax.persistence.Entity; @@ -12,6 +13,7 @@ @Getter @Setter @ToString +@Audited public class RequiredValidUntilFilter extends MetadataFilter { public RequiredValidUntilFilter() { diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/SignatureValidationFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/SignatureValidationFilter.java index 7e83ae6d4..5cf71225e 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/SignatureValidationFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/SignatureValidationFilter.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; +import org.hibernate.envers.Audited; import javax.persistence.Entity; @@ -12,6 +13,7 @@ @Getter @Setter @ToString +@Audited public class SignatureValidationFilter extends MetadataFilter { public SignatureValidationFilter() { diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/DynamicHttpMetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/DynamicHttpMetadataResolver.java index e8deb0e3e..732078747 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/DynamicHttpMetadataResolver.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/DynamicHttpMetadataResolver.java @@ -22,6 +22,7 @@ @Getter @Setter @ToString +@Audited public class DynamicHttpMetadataResolver extends MetadataResolver { public static final String DEFAULT_TIMEOUT = "PT5S"; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/FileBackedHttpMetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/FileBackedHttpMetadataResolver.java index 4ffadae52..17fe28ab9 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/FileBackedHttpMetadataResolver.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/FileBackedHttpMetadataResolver.java @@ -1,11 +1,10 @@ package edu.internet2.tier.shibboleth.admin.ui.domain.resolvers; -import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import lombok.ToString; -import org.hibernate.envers.AuditOverride; + import org.hibernate.envers.Audited; import javax.persistence.Embedded; @@ -17,7 +16,6 @@ @Setter @ToString @Audited -@AuditOverride(forClass = AbstractAuditable.class) public class FileBackedHttpMetadataResolver extends MetadataResolver { public FileBackedHttpMetadataResolver() { type = "FileBackedHttpMetadataResolver"; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/FilesystemMetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/FilesystemMetadataResolver.java index b96a74c14..ce3319d02 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/FilesystemMetadataResolver.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/FilesystemMetadataResolver.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; +import org.hibernate.envers.Audited; import javax.persistence.Embedded; import javax.persistence.Entity; @@ -16,6 +17,7 @@ @Getter @Setter @ToString +@Audited public class FilesystemMetadataResolver extends MetadataResolver { public FilesystemMetadataResolver() { type = "FilesystemMetadataResolver"; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/LocalDynamicMetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/LocalDynamicMetadataResolver.java index 6b722803a..49cc09642 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/LocalDynamicMetadataResolver.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/LocalDynamicMetadataResolver.java @@ -8,11 +8,14 @@ import javax.persistence.Embedded; import javax.persistence.Entity; +import org.hibernate.envers.Audited; + @Entity @EqualsAndHashCode(callSuper = true) @Getter @Setter @ToString +@Audited public class LocalDynamicMetadataResolver extends MetadataResolver { public LocalDynamicMetadataResolver() { type = "LocalDynamicMetadataResolver"; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataQueryProtocolScheme.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataQueryProtocolScheme.java index cc6dffb98..77b7cc733 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataQueryProtocolScheme.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataQueryProtocolScheme.java @@ -3,6 +3,7 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; +import org.hibernate.envers.Audited; import javax.persistence.Entity; @@ -13,6 +14,7 @@ @Getter @Setter @EqualsAndHashCode(callSuper = true) +@Audited public class MetadataQueryProtocolScheme extends MetadataRequestURLConstructionScheme { public MetadataQueryProtocolScheme() { diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataRequestURLConstructionScheme.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataRequestURLConstructionScheme.java index adddcc5aa..af3b0eab5 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataRequestURLConstructionScheme.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataRequestURLConstructionScheme.java @@ -7,6 +7,8 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; +import org.hibernate.envers.AuditOverride; +import org.hibernate.envers.Audited; import javax.persistence.Entity; import javax.persistence.Transient; @@ -24,6 +26,8 @@ @JsonSubTypes({@JsonSubTypes.Type(value=MetadataQueryProtocolScheme.class, name="MetadataQueryProtocol"), @JsonSubTypes.Type(value=TemplateScheme.class, name="Template"), @JsonSubTypes.Type(value=RegexScheme.class, name="Regex")}) +@Audited +@AuditOverride(forClass = AbstractAuditable.class) public abstract class MetadataRequestURLConstructionScheme extends AbstractAuditable { public enum SchemeType { METADATA_QUERY_PROTOCOL("MetadataQueryProtocol"), 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 ec639ba38..dc30d40d8 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 @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain.resolvers; import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; @@ -11,6 +12,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; +import org.hibernate.envers.AuditOverride; import org.hibernate.envers.Audited; import javax.persistence.CascadeType; @@ -27,7 +29,7 @@ @Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) -@EqualsAndHashCode(callSuper = true, exclude = {"version"}) +@EqualsAndHashCode(callSuper = true, exclude = {"version", "versionModifiedTimestamp"}) @NoArgsConstructor @Getter @Setter @@ -39,6 +41,7 @@ @JsonSubTypes.Type(value = FilesystemMetadataResolver.class, name = "FilesystemMetadataResolver"), @JsonSubTypes.Type(value = ResourceBackedMetadataResolver.class, name = "ResourceBackedMetadataResolver")}) @Audited +@AuditOverride(forClass = AbstractAuditable.class) public class MetadataResolver extends AbstractAuditable { @JsonProperty("@type") @@ -70,6 +73,9 @@ public class MetadataResolver extends AbstractAuditable { private Boolean doInitialization = true; + @JsonIgnore + private Long versionModifiedTimestamp; + @OneToMany(cascade = CascadeType.ALL) @OrderColumn private List metadataFilters = new ArrayList<>(); @@ -84,4 +90,8 @@ public int getVersion() { } return this.hashCode(); } + + public void markAsModified() { + this.versionModifiedTimestamp = System.currentTimeMillis(); + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/RegexScheme.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/RegexScheme.java index a8d4fffaa..ce0202b6c 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/RegexScheme.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/RegexScheme.java @@ -3,6 +3,7 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; +import org.hibernate.envers.Audited; import javax.persistence.Column; import javax.persistence.Entity; @@ -15,6 +16,7 @@ @Getter @Setter @EqualsAndHashCode(callSuper = true) +@Audited public class RegexScheme extends MetadataRequestURLConstructionScheme { public RegexScheme() { diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/ResourceBackedMetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/ResourceBackedMetadataResolver.java index 55c833f2d..2b480641f 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/ResourceBackedMetadataResolver.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/ResourceBackedMetadataResolver.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; +import org.hibernate.envers.Audited; import javax.persistence.Embedded; import javax.persistence.Entity; @@ -16,6 +17,7 @@ @Getter @Setter @ToString +@Audited public class ResourceBackedMetadataResolver extends MetadataResolver { public ResourceBackedMetadataResolver() { type = "ResourceBackedMetadataResolver"; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/TemplateScheme.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/TemplateScheme.java index 6cb0c8d90..a176bacd7 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/TemplateScheme.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/TemplateScheme.java @@ -3,6 +3,7 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; +import org.hibernate.envers.Audited; import javax.persistence.Entity; @@ -13,6 +14,7 @@ @Getter @Setter @EqualsAndHashCode(callSuper = true) +@Audited public class TemplateScheme extends MetadataRequestURLConstructionScheme { public TemplateScheme () { diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/versioning/Version.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/versioning/Version.java new file mode 100644 index 000000000..100b0fdbd --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/versioning/Version.java @@ -0,0 +1,34 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.versioning; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import java.io.Serializable; +import java.time.ZonedDateTime; + +/** + * Represents version information of any versioned entity in the system. + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +@EqualsAndHashCode +public class Version implements Serializable { + + private String id; + + private String creator; + + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") + private ZonedDateTime date; + + private static final long serialVersionUID = 3429591830989243421L; + +} 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 new file mode 100644 index 000000000..964ede86a --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/envers/EnversVersionServiceSupport.java @@ -0,0 +1,61 @@ +package edu.internet2.tier.shibboleth.admin.ui.envers; + +import edu.internet2.tier.shibboleth.admin.ui.domain.versioning.Version; +import org.hibernate.envers.AuditReaderFactory; +import org.hibernate.envers.query.AuditEntity; + +import javax.persistence.EntityManager; +import javax.persistence.NoResultException; +import java.time.ZoneOffset; +import java.util.List; + +import static java.util.Comparator.comparing; +import static java.util.stream.Collectors.toList; + +/** + * Encapsulates common functionality interfacing with Envers AuditReader low level API + * to query for revisions of various persistent entities. + */ +public class EnversVersionServiceSupport { + + private EntityManager entityManager; + + public EnversVersionServiceSupport(EntityManager entityManager) { + this.entityManager = entityManager; + } + + public List findVersionsForPersistentEntity(String resourceId, Class entityClass) { + List revs = AuditReaderFactory.get(entityManager).createQuery() + .forRevisionsOfEntity(entityClass, 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; + } + + public Object findSpecificVersionOfPersistentEntity(String resourceId, String versionId, Class entityClass) { + try { + return 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) { + return null; + } + } +} 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 8ee27218f..6a2847b38 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 @@ -3,6 +3,7 @@ import lombok.Getter; import lombok.Setter; import org.hibernate.envers.DefaultRevisionEntity; +import org.hibernate.envers.DefaultTrackingModifiedEntitiesRevisionEntity; import org.hibernate.envers.RevisionEntity; import javax.persistence.Entity; @@ -14,7 +15,11 @@ @RevisionEntity(PrincipalEnhancingRevisionListener.class) @Getter @Setter -public class PrincipalAwareRevisionEntity extends DefaultRevisionEntity { +public class PrincipalAwareRevisionEntity extends DefaultTrackingModifiedEntitiesRevisionEntity { private String principalUserName; + + public String idAsString() { + return String.valueOf(getId()); + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/envers/PrincipalEnhancingRevisionListener.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/envers/PrincipalEnhancingRevisionListener.java index 12af196ed..c1895b052 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/envers/PrincipalEnhancingRevisionListener.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/envers/PrincipalEnhancingRevisionListener.java @@ -9,7 +9,7 @@ */ public class PrincipalEnhancingRevisionListener implements RevisionListener { - private static final String ANONYMOUS = "anonymous"; + private static final String ANONYMOUS = "anonymousUser"; @Override public void newRevision(Object revisionEntity) { diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityDescriptorVersionService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityDescriptorVersionService.java new file mode 100644 index 000000000..5e1542ea2 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityDescriptorVersionService.java @@ -0,0 +1,17 @@ +package edu.internet2.tier.shibboleth.admin.ui.service; + +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 java.util.List; + +/** + * API containing operations pertaining to {@link EntityDescriptor} versioning. + */ +public interface EntityDescriptorVersionService { + + List findVersionsForEntityDescriptor(String resourceId); + + EntityDescriptorRepresentation findSpecificVersionOfEntityDescriptor(String resourceId, String versionId); +} 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 new file mode 100644 index 000000000..f21d9d227 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EnversEntityDescriptorVersionService.java @@ -0,0 +1,79 @@ +package edu.internet2.tier.shibboleth.admin.ui.service; + +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 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 + */ +public class EnversEntityDescriptorVersionService implements EntityDescriptorVersionService { + + private EntityManager entityManager; + + private EntityDescriptorService entityDescriptorService; + + public EnversEntityDescriptorVersionService(EntityManager entityManager, EntityDescriptorService entityDescriptorService) { + this.entityManager = entityManager; + 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; + } + + @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; + } + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EnversMetadataResolverVersionService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EnversMetadataResolverVersionService.java new file mode 100644 index 000000000..c98203eca --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EnversMetadataResolverVersionService.java @@ -0,0 +1,32 @@ +package edu.internet2.tier.shibboleth.admin.ui.service; + +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver; +import edu.internet2.tier.shibboleth.admin.ui.domain.versioning.Version; +import edu.internet2.tier.shibboleth.admin.ui.envers.EnversVersionServiceSupport; + +import java.util.List; + + +/** + * Hibernate Envers based implementation of {@link MetadataResolverVersionService}. + */ +public class EnversMetadataResolverVersionService implements MetadataResolverVersionService { + + + private EnversVersionServiceSupport enversVersionServiceSupport; + + public EnversMetadataResolverVersionService(EnversVersionServiceSupport enversVersionServiceSupport) { + this.enversVersionServiceSupport = enversVersionServiceSupport; + } + + @Override + public List findVersionsForMetadataResolver(String resourceId) { + return enversVersionServiceSupport.findVersionsForPersistentEntity(resourceId, MetadataResolver.class); + } + + @Override + public MetadataResolver findSpecificVersionOfMetadataResolver(String resourceId, String versionId) { + Object mrObject = enversVersionServiceSupport.findSpecificVersionOfPersistentEntity(resourceId, versionId, MetadataResolver.class); + return mrObject == null ? null : (MetadataResolver) mrObject; + } +} 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 9bbf5ec2a..ca7a8659e 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 @@ -56,6 +56,7 @@ 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; @@ -120,6 +121,10 @@ private EntityDescriptor buildDescriptorFromRepresentation(final EntityDescripto setupLogout(ed, representation); setupRelyingPartyOverrides(ed, representation); + //Let envers recognize update revision type for EntityDescriptor type + //when modifying Attributes and SPSSODescriptor inside RoleDescriptors collection + ed.setVersionModifiedTimestamp(System.currentTimeMillis()); + return ed; } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/MetadataResolverVersionService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/MetadataResolverVersionService.java new file mode 100644 index 000000000..16f3dd1a3 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/MetadataResolverVersionService.java @@ -0,0 +1,16 @@ +package edu.internet2.tier.shibboleth.admin.ui.service; + +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver; +import edu.internet2.tier.shibboleth.admin.ui.domain.versioning.Version; + +import java.util.List; + +/** + * API containing operations pertaining to {@link MetadataResolver} versioning. + */ +public interface MetadataResolverVersionService { + + List findVersionsForMetadataResolver(String resourceId); + + MetadataResolver findSpecificVersionOfMetadataResolver(String resourceId, String versionId); +} diff --git a/backend/src/main/resources/i18n/messages.properties b/backend/src/main/resources/i18n/messages.properties index 7cadee855..e04fe3b90 100644 --- a/backend/src/main/resources/i18n/messages.properties +++ b/backend/src/main/resources/i18n/messages.properties @@ -48,9 +48,13 @@ action.move-down=Move Down action.edit=Edit action.add-filter=Add Filter action.manage-filters=Manage Filters +action.version-history=Version History +action.options=Options +action.xml=XML value.enabled=Enabled value.disabled=Disabled +value.current=Current value.none=None value.file=File value.memory=Memory @@ -236,6 +240,7 @@ label.filter-name=Filter Name label.filter-enabled=Filter Enabled label.filter-target=FilterTarget label.filter-type=Filter Type +label.option=Option label.value=Value label.binding-type=Binding Type label.sign-assertion=Sign Assertions @@ -384,6 +389,13 @@ label.email=Email label.role=Role label.delete=Delete? +label.title=Title +label.enabled=Enabled +label.author=Author +label.creation-date=Creation Date +label.order=Order +label.provider-type=Provider Type + label.metadata-resolver-history=Metadata resolver history label.metadata-version-history=Metadata Version History label.select-version=Select Version @@ -396,6 +408,9 @@ label.current=Current label.restore=Restore label.compare-selected=Compare Selected +label.saved=Saved +label.by=by + message.delete-user-title=Delete User? message.delete-user-body=You are requesting to delete a user. If you complete this process the user will be removed. This cannot be undone. Do you wish to continue? diff --git a/backend/src/main/resources/metadata-sources-ui-schema.json b/backend/src/main/resources/metadata-sources-ui-schema.json index 3f002253a..9d64e75b4 100644 --- a/backend/src/main/resources/metadata-sources-ui-schema.json +++ b/backend/src/main/resources/metadata-sources-ui-schema.json @@ -310,6 +310,7 @@ }, "attributeRelease": { "type": "array", + "title": "label.attribute-release", "description": "Attribute release table - select the attributes you want to release (default unchecked)", "widget": { "id": "checklist", 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 928cd18ec..89cb89d54 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 @@ -11,6 +11,7 @@ import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorReposit 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 edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorVersionService import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityDescriptorServiceImpl import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityServiceImpl import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator @@ -69,6 +70,7 @@ class EntityDescriptorControllerTests extends Specification { RoleRepository roleRepository = Mock() UserService userService + EntityDescriptorVersionService versionService = Mock() def setup() { generator = new TestObjectGenerator() @@ -78,7 +80,7 @@ class EntityDescriptorControllerTests extends Specification { userService = new UserService(roleRepository, userRepository) service = new JPAEntityDescriptorServiceImpl(openSamlObjects, new JPAEntityServiceImpl(openSamlObjects), userService) - controller = new EntityDescriptorController(userRepository, roleRepository, userService) + controller = new EntityDescriptorController(userService, versionService) controller.entityDescriptorRepository = entityDescriptorRepository controller.openSamlObjects = openSamlObjects controller.entityDescriptorService = service diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy index 802afdb81..16ab5f6e0 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy @@ -201,7 +201,6 @@ class MetadataFiltersControllerTests extends Specification { updatedMetadataResolver.getMetadataFilters().add(updatedFilter) 1 * metadataResolverRepository.findByResourceId(_) >> originalMetadataResolver - 1 * metadataFilterRepository.findByResourceId(_) >> originalFilter 1 * metadataFilterRepository.save(_) >> updatedFilter def filterUUID = updatedFilter.getResourceId() @@ -240,7 +239,6 @@ class MetadataFiltersControllerTests extends Specification { originalMetadataResolver.getMetadataFilters().add(randomFilter) 1 * metadataResolverRepository.findByResourceId(_) >> originalMetadataResolver - 1 * metadataFilterRepository.findByResourceId(_) >> randomFilter def filterUUID = randomFilter.getResourceId() @@ -253,4 +251,4 @@ class MetadataFiltersControllerTests extends Specification { then: result.andExpect(status().is(409)) } -} \ No newline at end of file +} 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 new file mode 100644 index 000000000..011fe7033 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/versioning/VersionJsonSerializationBasicTests.groovy @@ -0,0 +1,41 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.versioning + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import groovy.json.JsonSlurper +import spock.lang.Specification + +import java.time.ZonedDateTime + +class VersionJsonSerializationBasicTests extends Specification { + + ObjectMapper mapper + + JsonSlurper jsonSlurper + + def setup() { + mapper = new ObjectMapper() + mapper.registerModule(new JavaTimeModule()) + jsonSlurper = new JsonSlurper() + } + + def "Verify basic Version JSON serialization"() { + given: + def staticDate = ZonedDateTime.parse("2019-05-20T15:00:00.574Z") + def version = new Version('2', 'kramer', staticDate) + def expectedJson = """ + { + "id": "2", + "creator": "kramer", + "date": "2019-05-20T15:00:00.574Z" + } + """ + def expectedJsonMap = jsonSlurper.parseText(expectedJson) + + when: + def deSerializedJsonMap = jsonSlurper.parseText(mapper.writeValueAsString(version)) + + then: + deSerializedJsonMap.date == expectedJsonMap.date + } +} 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 new file mode 100644 index 000000000..2052cf9d1 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/EnversTestsSupport.groovy @@ -0,0 +1,76 @@ +package edu.internet2.tier.shibboleth.admin.ui.repository.envers + +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.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.service.EntityDescriptorService +import org.hibernate.envers.AuditReaderFactory +import org.hibernate.envers.query.AuditEntity +import org.hibernate.envers.query.AuditQuery +import org.springframework.transaction.PlatformTransactionManager +import org.springframework.transaction.support.DefaultTransactionDefinition + +import javax.persistence.EntityManager + +import static org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRES_NEW + +class EnversTestsSupport { + + //This explicit low level transaction dance is required in order to verify history/version data that envers + //writes out only after the explicit transaction is committed, therefore making it impossible to verify within the main tx + //boundary of the test method which commits tx only after an execution of the test method. This let's us explicitly + //start/commit transaction making envers data written out and verifiable + static doInExplicitTransaction(PlatformTransactionManager txMgr, Closure uow) { + def txStatus = txMgr.getTransaction(new DefaultTransactionDefinition(PROPAGATION_REQUIRES_NEW)) + def entity = uow() + txMgr.commit(txStatus) + entity + } + + static updateAndGetRevisionHistoryOfEntityDescriptor(EntityDescriptor ed, EntityDescriptorRepresentation representation, + EntityDescriptorService eds, + EntityDescriptorRepository edr, + PlatformTransactionManager txMgr, + EntityManager em) { + eds.updateDescriptorFromRepresentation(ed, representation) + doInExplicitTransaction(txMgr) { + edr.save(ed) + } + getRevisionHistoryForEntityType(em, EntityDescriptor, ed.resourceId) + } + + static updateAndGetRevisionHistoryOfMetadataResolver(MetadataResolver mr, + MetadataResolverRepository mrr, + Class < ? > type, + PlatformTransactionManager + txMgr, EntityManager em) { + + doInExplicitTransaction(txMgr) { + mrr.save(mr) + } + getRevisionHistoryForEntityType(em, type, mr.resourceId) + } + + static getRevisionHistoryForEntityType(EntityManager em, Class entityType, String resourceId) { + def auditReader = AuditReaderFactory.get(em) + AuditQuery auditQuery = auditReader + .createQuery() + .forRevisionsOfEntity(entityType, false, false) + .add(AuditEntity.property("resourceId").eq(resourceId)) + auditQuery.resultList + } + + static getTargetEntityForRevisionIndex(List revHistory, int revIndex) { + revHistory[revIndex][0] + } + + static getRevisionEntityForRevisionIndex(List revHistory, int revIndex) { + revHistory[revIndex][1] + } + + static getModifiedEntityNames(List revHistory, int revIndex) { + getRevisionEntityForRevisionIndex(revHistory, revIndex).modifiedEntityNames + } +} 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 b0626d431..1c1778c60 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 @@ -5,7 +5,6 @@ import edu.internet2.tier.shibboleth.admin.ui.ShibbolethUiApplication import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomPropertiesConfiguration import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor -import edu.internet2.tier.shibboleth.admin.ui.domain.SPSSODescriptor import edu.internet2.tier.shibboleth.admin.ui.domain.XSAny import edu.internet2.tier.shibboleth.admin.ui.domain.XSAnyBuilder import edu.internet2.tier.shibboleth.admin.ui.domain.XSBoolean @@ -26,7 +25,6 @@ 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 import edu.internet2.tier.shibboleth.admin.util.AttributeUtility -import groovy.json.JsonOutput import org.skyscreamer.jsonassert.JSONAssert import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest @@ -37,7 +35,6 @@ import org.xmlunit.builder.DiffBuilder import org.xmlunit.builder.Input import org.xmlunit.diff.DefaultNodeMatcher import org.xmlunit.diff.ElementSelectors -import spock.lang.Ignore import spock.lang.Specification @ContextConfiguration(classes=[CoreShibUiConfiguration, CustomPropertiesConfiguration]) diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 0022b801c..8b3566fc6 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -390,71 +390,6 @@ paths: '409': description: Already Exists deprecated: false - '/MetadataResolvers/{metadataResolverId}/Filters/{resourceId}/Versions': - get: - tags: - - metadata-filters-controller - description: Get list of Versions for an entity descriptor - produces: - - application/json - parameters: - - name: metadataResolverId - in: path - description: metadataResolverId - required: true - type: string - - name: resourceId - in: path - description: resourceId - required: true - type: string - responses: - '200': - description: OK. Items returned should be ordered by `date`, ascending (oldest version first). - schema: - type: array - items: - $ref: '#/definitions/Version' - '401': - description: Unauthorized - '403': - description: Forbidden - '404': - description: Not Found - '/MetadataResolvers/{metadataResolverId}/Filters/{resourceId}/Versions/{versionId}': - get: - tags: - - metadata-filters-controller - description: Get a Version of an entity descriptor - produces: - - application/json - parameters: - - name: metadataResolverId - in: path - description: metadataResolverId - required: true - type: string - - name: resourceId - in: path - description: resourceId - required: true - type: string - - name: versionId - in: path - description: versionId of a version of the EntityDescriptor - required: true - type: string - responses: - '200': - description: OK - schema: - $ref: '#/definitions/MetadataFilter' - '401': - description: Unauthorized - '403': - description: Forbidden - '404': - description: Not Found '/MetadataResolvers/{resourceId}': get: tags: diff --git a/settings.gradle b/settings.gradle index 8fae26617..b3ab3e757 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include 'backend', 'ui', 'pac4j-module' \ No newline at end of file +include 'backend', 'ui', 'pac4j-module' diff --git a/ui/package-lock.json b/ui/package-lock.json index fc76f65cc..25fad932c 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -3511,6 +3511,7 @@ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2", "longest": "^1.0.1", @@ -3582,9 +3583,9 @@ "dev": true }, "are-we-there-yet": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", - "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, "requires": { "delegates": "^1.0.0", @@ -3717,9 +3718,9 @@ } }, "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true }, "assign-symbols": { @@ -3796,16 +3797,17 @@ } }, "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", "dev": true }, "aws4": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", - "dev": true + "dev": true, + "optional": true }, "axobject-query": { "version": "2.0.2", @@ -4164,15 +4166,6 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true, - "requires": { - "hoek": "2.x.x" - } - }, "bootstrap": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.3.1.tgz", @@ -4759,6 +4752,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", "dev": true, + "optional": true, "requires": { "delayed-stream": "~1.0.0" } @@ -5025,15 +5019,6 @@ "which": "^1.2.9" } }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true, - "requires": { - "boom": "2.x.x" - } - }, "crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", @@ -6364,14 +6349,25 @@ "dev": true }, "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, "requires": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.5", + "combined-stream": "^1.0.6", "mime-types": "^2.1.12" + }, + "dependencies": { + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + } } }, "forwarded": { @@ -6472,7 +6468,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -6493,12 +6490,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6513,17 +6512,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -6640,7 +6642,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -6652,6 +6655,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6666,6 +6670,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6673,12 +6678,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -6697,6 +6704,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -6777,7 +6785,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -6789,6 +6798,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -6874,7 +6884,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -6910,6 +6921,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6929,6 +6941,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6972,19 +6985,21 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, "fstream": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -7016,29 +7031,14 @@ } }, "gaze": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", - "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", "dev": true, "requires": { "globule": "^1.0.0" } }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", - "dev": true - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "dev": true, - "requires": { - "is-property": "^1.0.0" - } - }, "genfun": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", @@ -7150,14 +7150,22 @@ } }, "globule": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", - "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", + "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", "dev": true, "requires": { "glob": "~7.1.1", - "lodash": "~4.17.4", + "lodash": "~4.17.10", "minimatch": "~3.0.2" + }, + "dependencies": { + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + } } }, "graceful-fs": { @@ -7273,29 +7281,58 @@ } }, "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", "dev": true }, "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "dev": true, "requires": { - "ajv": "^4.9.1", - "har-schema": "^1.0.5" + "ajv": "^6.5.5", + "har-schema": "^2.0.0" }, "dependencies": { "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", "dev": true, "requires": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" } } } @@ -7431,18 +7468,6 @@ "minimalistic-assert": "^1.0.0" } }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true, - "requires": { - "boom": "2.x.x", - "cryptiles": "2.x.x", - "hoek": "2.x.x", - "sntp": "1.x.x" - } - }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", @@ -7460,12 +7485,6 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true - }, "homedir-polyfill": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", @@ -7963,12 +7982,12 @@ } }, "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, "requires": { - "assert-plus": "^0.2.0", + "assert-plus": "^1.0.0", "jsprim": "^1.2.2", "sshpk": "^1.7.0" } @@ -8332,25 +8351,6 @@ "is-extglob": "^2.1.1" } }, - "is-my-ip-valid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", - "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==", - "dev": true - }, - "is-my-json-valid": { - "version": "2.17.2", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz", - "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==", - "dev": true, - "requires": { - "generate-function": "^2.0.0", - "generate-object-property": "^1.1.0", - "is-my-ip-valid": "^1.0.0", - "jsonpointer": "^4.0.0", - "xtend": "^4.0.0" - } - }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -8416,12 +8416,6 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", - "dev": true - }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -8928,9 +8922,9 @@ } }, "js-base64": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.3.tgz", - "integrity": "sha512-H7ErYLM34CvDMto3GbD6xD0JLUGYXR3QTcH6B/tr4Hi/QpSThnCsIp+Sy5FRTw3B0d6py4HcNkW7nO/wdtGWEw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", + "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==", "dev": true }, "js-tokens": { @@ -8980,15 +8974,6 @@ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", "dev": true }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "requires": { - "jsonify": "~0.0.0" - } - }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -9016,24 +9001,12 @@ "graceful-fs": "^4.1.6" } }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, - "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", - "dev": true - }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -9329,7 +9302,8 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==", - "dev": true + "dev": true, + "optional": true }, "http-signature": { "version": "1.2.0", @@ -9484,12 +9458,6 @@ "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", "dev": true }, - "lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", - "dev": true - }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -9512,12 +9480,6 @@ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" }, - "lodash.mergewith": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", - "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==", - "dev": true - }, "lodash.tail": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", @@ -9583,7 +9545,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true + "dev": true, + "optional": true }, "loose-envify": { "version": "1.3.1", @@ -10232,6 +10195,14 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, + "ngx-infinite-scroll": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-7.2.0.tgz", + "integrity": "sha512-EcqjKpU1ukRV3YXOW8cTVtbzPpa9UPaRtYBCg0ZQH3ceCDm+xzLbd4pXy6oKAIN4zN1r/pyGuf5XOJkA8vr6yg==", + "requires": { + "opencollective-postinstall": "^2.0.2" + } + }, "ngx-schema-form": { "version": "2.2.0-beta.1", "resolved": "https://registry.npmjs.org/ngx-schema-form/-/ngx-schema-form-2.2.0-beta.1.tgz", @@ -10274,20 +10245,19 @@ "dev": true }, "node-gyp": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz", - "integrity": "sha1-m/vlRWIoYoSDjnUOrAUpWFP6HGA=", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", "dev": true, "requires": { "fstream": "^1.0.0", "glob": "^7.0.3", "graceful-fs": "^4.1.2", - "minimatch": "^3.0.2", "mkdirp": "^0.5.0", "nopt": "2 || 3", "npmlog": "0 || 1 || 2 || 3 || 4", "osenv": "0", - "request": "2", + "request": "^2.87.0", "rimraf": "2", "semver": "~5.3.0", "tar": "^2.0.0", @@ -10334,9 +10304,9 @@ } }, "node-sass": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.0.tgz", - "integrity": "sha512-QFHfrZl6lqRU3csypwviz2XLgGNOoWQbo2GOvtsfQqOfL4cy1BtWnhx/XUeAO9LT3ahBzSRXcEO6DdvAH9DzSg==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz", + "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==", "dev": true, "requires": { "async-foreach": "^0.1.3", @@ -10346,15 +10316,13 @@ "get-stdin": "^4.0.1", "glob": "^7.0.3", "in-publish": "^2.0.0", - "lodash.assign": "^4.2.0", - "lodash.clonedeep": "^4.3.2", - "lodash.mergewith": "^4.6.0", + "lodash": "^4.17.11", "meow": "^3.7.0", "mkdirp": "^0.5.1", - "nan": "^2.10.0", - "node-gyp": "^3.3.1", + "nan": "^2.13.2", + "node-gyp": "^3.8.0", "npmlog": "^4.0.0", - "request": "~2.79.0", + "request": "^2.88.0", "sass-graph": "^2.2.4", "stdout-stream": "^1.4.0", "true-case-path": "^1.0.2" @@ -10366,12 +10334,6 @@ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, - "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", - "dev": true - }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", @@ -10385,69 +10347,23 @@ "supports-color": "^2.0.0" } }, - "har-validator": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", - "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", - "dev": true, - "requires": { - "chalk": "^1.1.1", - "commander": "^2.9.0", - "is-my-json-valid": "^2.12.4", - "pinkie-promise": "^2.0.0" - } - }, - "nan": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, - "qs": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", - "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=", + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", "dev": true }, - "request": { - "version": "2.79.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", - "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", - "dev": true, - "requires": { - "aws-sign2": "~0.6.0", - "aws4": "^1.2.1", - "caseless": "~0.11.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.0", - "forever-agent": "~0.6.1", - "form-data": "~2.1.1", - "har-validator": "~2.0.6", - "hawk": "~3.1.3", - "http-signature": "~1.1.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.7", - "oauth-sign": "~0.8.1", - "qs": "~6.3.0", - "stringstream": "~0.0.4", - "tough-cookie": "~2.3.0", - "tunnel-agent": "~0.4.1", - "uuid": "^3.0.0" - } - }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true } } }, @@ -10618,7 +10534,8 @@ "version": "0.8.2", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -10748,6 +10665,11 @@ "is-wsl": "^1.1.0" } }, + "opencollective-postinstall": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz", + "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==" + }, "opener": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", @@ -11273,9 +11195,9 @@ } }, "performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, "pify": { @@ -11547,6 +11469,12 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, + "psl": { + "version": "1.1.33", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.33.tgz", + "integrity": "sha512-LTDP2uSrsc7XCb5lO7A8BI1qYxRe/8EqlRvMeEl6rsnYAqDOl8xHR+8lSAIVfrNaSAlTPTNOCgNjWcoUL3AZsw==", + "dev": true + }, "public-encrypt": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", @@ -11594,9 +11522,9 @@ "dev": true }, "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true }, "querystring": { @@ -11879,33 +11807,97 @@ } }, "request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "dev": true, "requires": { - "aws-sign2": "~0.6.0", - "aws4": "^1.2.1", + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", "forever-agent": "~0.6.1", - "form-data": "~2.1.1", - "har-validator": "~4.2.1", - "hawk": "~3.1.3", - "http-signature": "~1.1.0", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.7", - "oauth-sign": "~0.8.1", - "performance-now": "^0.2.0", - "qs": "~6.4.0", - "safe-buffer": "^5.0.1", - "stringstream": "~0.0.4", - "tough-cookie": "~2.3.0", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", "tunnel-agent": "^0.6.0", - "uuid": "^3.0.0" + "uuid": "^3.3.2" + }, + "dependencies": { + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "dev": true + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "dev": true, + "requires": { + "mime-db": "1.40.0" + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + } } }, "require-directory": { @@ -12474,15 +12466,6 @@ "kind-of": "^3.2.0" } }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true, - "requires": { - "hoek": "2.x.x" - } - }, "socket.io": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", @@ -12833,9 +12816,9 @@ "dev": true }, "stdout-stream": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.0.tgz", - "integrity": "sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", "dev": true, "requires": { "readable-stream": "^2.0.1" @@ -12940,7 +12923,8 @@ "version": "0.0.5", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", - "dev": true + "dev": true, + "optional": true }, "strip-ansi": { "version": "3.0.1", @@ -13069,13 +13053,13 @@ "dev": true }, "tar": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", "dev": true, "requires": { "block-stream": "*", - "fstream": "^1.0.2", + "fstream": "^1.0.12", "inherits": "2" } }, @@ -13190,6 +13174,7 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", "dev": true, + "optional": true, "requires": { "punycode": "^1.4.1" } @@ -13213,27 +13198,12 @@ "dev": true }, "true-case-path": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.2.tgz", - "integrity": "sha1-fskRMJJHZsf1c74wIMNPj9/QDWI=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", "dev": true, "requires": { - "glob": "^6.0.4" - }, - "dependencies": { - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", - "dev": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } + "glob": "^7.1.2" } }, "tryer": { @@ -15416,12 +15386,12 @@ "dev": true }, "wide-align": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", - "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "requires": { - "string-width": "^1.0.2" + "string-width": "^1.0.2 || 2" } }, "window-size": { diff --git a/ui/package.json b/ui/package.json index 6cfe807cd..b293be085 100644 --- a/ui/package.json +++ b/ui/package.json @@ -6,7 +6,7 @@ "ng": "ng", "start": "ng serve --proxy-config proxy.conf.json", "build": "ng build", - "test": "ng test --code-coverage --source-map=false", + "test": "ng test --code-coverage --source-map=true", "lint": "ng lint", "e2e": "ng e2e", "build:static": "node-sass src/static.scss ./dist/unsecured/static.css", @@ -43,6 +43,7 @@ "deepmerge": "^2.2.1", "file-saver": "^1.3.3", "font-awesome": "^4.7.0", + "ngx-infinite-scroll": "^7.2.0", "ngx-schema-form": "^2.2.0-beta.1", "rxjs": "^6.5.1", "rxjs-compat": "^6.5.1", @@ -72,6 +73,7 @@ "karma-jasmine-html-reporter": "^1.4.2", "karma-spec-reporter": "0.0.32", "ncp": "^2.0.0", + "node-sass": "^4.12.0", "path": "^0.12.7", "ts-node": "~3.2.0", "tslint": "~5.3.2", diff --git a/ui/src/app/admin/component/enable-metadata.component.html b/ui/src/app/admin/component/enable-metadata.component.html index cba54f177..01465184f 100644 --- a/ui/src/app/admin/component/enable-metadata.component.html +++ b/ui/src/app/admin/component/enable-metadata.component.html @@ -4,9 +4,8 @@ aria-label="Provider Item Accordion. Press Spacebar to open"> resolvers.map(r => new FileBackedHttpMetadataResolver(r))) ); this.loading$ = this.store.select(fromDashboard.getSearchLoading); - this.entitiesOpen$ = this.store.select(fromDashboard.getOpenProviders); this.total$ = this.resolvers$.pipe(map(list => list.length)); } @@ -51,10 +49,6 @@ export class EnableMetadataComponent implements OnInit { this.router.navigate(['metadata', 'resolver', entity.getId(), 'edit']); } - toggleEntity(entity: MetadataEntity): void { - this.store.dispatch(new ToggleEntityDisplay(entity.getId())); - } - openPreviewDialog(entity: MetadataEntity): void { this.store.dispatch(new PreviewEntity({ id: entity.getId(), entity })); } diff --git a/ui/src/app/app.component.html b/ui/src/app/app.component.html index fb19ae16a..52b5a0f87 100644 --- a/ui/src/app/app.component.html +++ b/ui/src/app/app.component.html @@ -21,26 +21,19 @@ Add New