From 83631bd910d474fa34bec3e766bf6b34227a704c Mon Sep 17 00:00:00 2001 From: chasegawa Date: Thu, 29 Jun 2023 13:53:25 -0700 Subject: [PATCH] SHIBUI-2584 Added additional beacon data collection --- .../CoreShibUiConfiguration.java | 7 ++- .../ui/controller/ActivateController.java | 12 +++++ .../controller/MetadataFiltersController.java | 12 ++++- .../MetadataResolversController.java | 25 ++++++---- .../admin/ui/domain/beacon/BeaconEvent.java | 30 ++++++++++++ .../ui/domain/beacon/BeaconEventType.java | 7 +++ .../ui/repository/BeaconEventRepository.java | 7 +++ .../ui/service/BeaconDataServiceImpl.java | 24 ++++++++-- .../admin/ui/service/IBeaconDataService.java | 5 ++ .../JPAEntityDescriptorServiceImpl.java | 19 +++++++- .../ui/service/JPAFilterServiceImpl.java | 3 ++ .../ui/BaseDataJpaTestConfiguration.groovy | 47 ++++++++++++++++++- .../MetadataFiltersControllerTests.groovy | 5 ++ 13 files changed, 180 insertions(+), 23 deletions(-) create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/beacon/BeaconEvent.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/beacon/BeaconEventType.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/BeaconEventRepository.java diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java index 66ad8db9d..2c33e61c4 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java @@ -4,6 +4,7 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.BeaconConfiguration; import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects; import edu.internet2.tier.shibboleth.admin.ui.repository.BeaconConfigurationRepository; +import edu.internet2.tier.shibboleth.admin.ui.repository.BeaconEventRepository; import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository; import edu.internet2.tier.shibboleth.admin.ui.repository.FilterRepository; import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; @@ -47,7 +48,6 @@ import jakarta.servlet.http.HttpServletRequest; import net.javacrumbs.shedlock.core.LockProvider; import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider; -import org.apache.commons.lang3.StringUtils; import org.apache.lucene.analysis.Analyzer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -68,7 +68,6 @@ import org.springframework.security.authentication.AuthenticationEventPublisher; import org.springframework.security.authentication.DefaultAuthenticationEventPublisher; import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; @@ -285,8 +284,8 @@ public String getBeaconCronValue(BeaconConfigurationRepository repo) public IBeaconDataService getBeaconDataService(@Value("${shibui.beacon.productName:ShibUi}") String productName, InfoEndpoint info, @Value("#{environment.TIERVERSION}") String tierVersion, EntityDescriptorRepository entityDescriptorRepository, MetadataResolverRepository metadataResolverRepository, FilterRepository filterRepository, GroupsRepository groupsRepository, RoleRepository roleRepository, BeaconConfigurationRepository beaconConfigurationRepository, - UserService userService, HealthEndpoint healthEndpoint) { - BeaconDataServiceImpl result = new BeaconDataServiceImpl(productName, info, tierVersion, entityDescriptorRepository, metadataResolverRepository, filterRepository, groupsRepository, roleRepository, beaconConfigurationRepository, userService, healthEndpoint); + UserService userService, HealthEndpoint healthEndpoint, BeaconEventRepository beaconEventRepository) { + BeaconDataServiceImpl result = new BeaconDataServiceImpl(productName, info, tierVersion, entityDescriptorRepository, metadataResolverRepository, filterRepository, groupsRepository, roleRepository, beaconConfigurationRepository, userService, healthEndpoint, beaconEventRepository); return result; } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/ActivateController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/ActivateController.java index eea15896a..2d22a172d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/ActivateController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/ActivateController.java @@ -1,5 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.controller; +import edu.internet2.tier.shibboleth.admin.ui.domain.beacon.BeaconEvent; +import edu.internet2.tier.shibboleth.admin.ui.domain.beacon.BeaconEventType; import edu.internet2.tier.shibboleth.admin.ui.domain.exceptions.MetadataFileNotFoundException; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation; @@ -11,6 +13,7 @@ import edu.internet2.tier.shibboleth.admin.ui.service.DynamicRegistrationService; import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService; import edu.internet2.tier.shibboleth.admin.ui.service.FilterService; +import edu.internet2.tier.shibboleth.admin.ui.service.IBeaconDataService; import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tags; @@ -40,6 +43,9 @@ public class ActivateController { @Autowired private MetadataResolverService metadataResolverService; + + @Autowired + private IBeaconDataService beaconDataService; @PatchMapping(path = "/DynamicRegistration/{resourceId}/{mode}") @Transactional @@ -69,6 +75,9 @@ public ResponseEntity enableEntityDescriptor(@PathVariable String resourceId, public ResponseEntity enableFilter(@PathVariable String metadataResolverId, @PathVariable String resourceId, @PathVariable String mode) throws PersistentEntityNotFound, ForbiddenException, ScriptException { boolean status = "enable".equalsIgnoreCase(mode); MetadataFilter persistedFilter = filterService.updateFilterEnabledStatus(metadataResolverId, resourceId, status); + if (status) { + beaconDataService.addBeaconEvent(new BeaconEvent(BeaconEventType.METADATA_FILTER_ENABLED)); + } return ResponseEntity.ok(persistedFilter); } @@ -77,6 +86,9 @@ public ResponseEntity enableFilter(@PathVariable String metadataResolverId, @ public ResponseEntity enableProvider(@PathVariable String resourceId, @PathVariable String mode) throws PersistentEntityNotFound, ForbiddenException, MetadataFileNotFoundException, InitializationException { boolean status = "enable".equalsIgnoreCase(mode); MetadataResolver metadataResolver = metadataResolverService.updateMetadataResolverEnabledStatus(resourceId, status); + if (status) { + beaconDataService.addBeaconEvent(new BeaconEvent(BeaconEventType.METADATA_PROVIDER_ENABLED)); + } return ResponseEntity.ok(metadataResolver); } } \ No newline at end of file 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 a6430fa76..530d0af97 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 @@ -1,5 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.controller; +import edu.internet2.tier.shibboleth.admin.ui.domain.beacon.BeaconEvent; +import edu.internet2.tier.shibboleth.admin.ui.domain.beacon.BeaconEventType; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.ITargetable; @@ -9,6 +11,7 @@ import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; import edu.internet2.tier.shibboleth.admin.ui.security.service.IGroupService; import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService; +import edu.internet2.tier.shibboleth.admin.ui.service.IBeaconDataService; import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tags; @@ -50,6 +53,9 @@ public class MetadataFiltersController { private static final Supplier HTTP_400_BAD_REQUEST_EXCEPTION = () -> new HttpClientErrorException(BAD_REQUEST); private static final Supplier HTTP_404_CLIENT_ERROR_EXCEPTION = () -> new HttpClientErrorException(NOT_FOUND); + @Autowired + private IBeaconDataService beaconDataService; + @Autowired org.opensaml.saml.metadata.resolver.MetadataResolver chainingMetadataResolver; @@ -82,6 +88,7 @@ public ResponseEntity create(@PathVariable String metadataResolverId, @Reques reloadFiltersAndHandleScriptException(persistedMr.getResourceId()); MetadataFilter persistedFilter = newlyPersistedFilter(persistedMr.getMetadataFilters().stream(), newFilter.getResourceId()); + beaconDataService.addBeaconEvent(new BeaconEvent(BeaconEventType.METADATA_FILTER_CREATED)); return ResponseEntity.created(getResourceUriFor(persistedMr, newFilter.getResourceId())).body(persistedFilter); } @@ -110,6 +117,7 @@ public ResponseEntity delete(@PathVariable String metadataResolverId, @PathVa //TODO: do we need to reload filters here?!? //metadataResolverService.reloadFilters(persistedMr.getName()); + beaconDataService.addBeaconEvent(new BeaconEvent(BeaconEventType.METADATA_FILTER_REMOVED)); return ResponseEntity.noContent().build(); } @@ -189,8 +197,7 @@ private void reloadFiltersAndHandleScriptException(String resolverResourceId) { @PutMapping("/Filters/{resourceId}") @Transactional - public ResponseEntity update(@PathVariable String metadataResolverId, @PathVariable String resourceId, @RequestBody MetadataFilter updatedFilter) - throws ScriptException { + public ResponseEntity update(@PathVariable String metadataResolverId, @PathVariable String resourceId, @RequestBody MetadataFilter updatedFilter) throws ScriptException { MetadataResolver metadataResolver = findResolverOrThrowHttp404(metadataResolverId); //Now we operate directly on the filter attached to MetadataResolver, @@ -228,6 +235,7 @@ public ResponseEntity update(@PathVariable String metadataResolverId, @PathVa // TODO: do we need to reload filters here? We shouldnt throw a script exception here now - we check above... reloadFiltersAndHandleScriptException(metadataResolver.getResourceId()); + beaconDataService.addBeaconEvent(new BeaconEvent(BeaconEventType.METADATA_FILTER_MODIFIED)); return ResponseEntity.ok().body(persistedFilter); } 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 51d0d4753..05ce3eb55 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 @@ -1,12 +1,15 @@ package edu.internet2.tier.shibboleth.admin.ui.controller; import com.fasterxml.jackson.databind.exc.InvalidTypeIdException; +import edu.internet2.tier.shibboleth.admin.ui.domain.beacon.BeaconEvent; +import edu.internet2.tier.shibboleth.admin.ui.domain.beacon.BeaconEventType; import edu.internet2.tier.shibboleth.admin.ui.domain.exceptions.MetadataFileNotFoundException; import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver; import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.opensaml.OpenSamlChainingMetadataResolver; import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.validator.MetadataResolverValidationService; 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.IBeaconDataService; 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; @@ -52,27 +55,29 @@ @Slf4j @Tags(value = {@Tag(name = "metadata resolvers")}) public class MetadataResolversController { + @Autowired + private IBeaconDataService beaconDataService; @Autowired - MetadataResolverRepository resolverRepository; + org.opensaml.saml.metadata.resolver.MetadataResolver chainingMetadataResolver; @Autowired - MetadataResolverValidationService metadataResolverValidationService; + IndexWriterService indexWriterService; @Autowired - MetadataResolverService metadataResolverService; + MetadataResolverConverterService metadataResolverConverterService; @Autowired - MetadataResolversPositionOrderContainerService positionOrderContainerService; + MetadataResolverService metadataResolverService; @Autowired - IndexWriterService indexWriterService; + MetadataResolverValidationService metadataResolverValidationService; @Autowired - org.opensaml.saml.metadata.resolver.MetadataResolver chainingMetadataResolver; + MetadataResolversPositionOrderContainerService positionOrderContainerService; @Autowired - MetadataResolverConverterService metadataResolverConverterService; + MetadataResolverRepository resolverRepository; @Autowired MetadataResolverVersionService versionService; @@ -156,7 +161,7 @@ public ResponseEntity create(@RequestBody MetadataResolver newResolver) throw MetadataResolver persistedResolver = resolverRepository.save(newResolver); positionOrderContainerService.appendPositionOrderForNew(persistedResolver); doResolverInitialization(persistedResolver); - + beaconDataService.addBeaconEvent(new BeaconEvent(BeaconEventType.METADATA_PROVIDER_CREATED)); return ResponseEntity.created(getResourceUriFor(persistedResolver)).body(persistedResolver); } @@ -168,8 +173,7 @@ public ResponseEntity update(@PathVariable String resourceId, @RequestBody Me return ResponseEntity.notFound().build(); } if (existingResolver.getVersion() != updatedResolver.getVersion()) { - log.info("Metadata Resolver version conflict. Latest resolver in database version: {}. Resolver version sent from UI: {}", - existingResolver.getVersion(), updatedResolver.getVersion()); + log.info("Metadata Resolver version conflict. Latest resolver in database version: {}. Resolver version sent from UI: {}", existingResolver.getVersion(), updatedResolver.getVersion()); return ResponseEntity.status(HttpStatus.CONFLICT).build(); } @@ -182,6 +186,7 @@ public ResponseEntity update(@PathVariable String resourceId, @RequestBody Me MetadataResolver persistedResolver = resolverRepository.save(updatedResolver); doResolverInitialization(persistedResolver); + beaconDataService.addBeaconEvent(new BeaconEvent(BeaconEventType.METADATA_PROVIDER_MODIFIED)); return ResponseEntity.ok(resolverRepository.findByResourceId(resourceId)); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/beacon/BeaconEvent.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/beacon/BeaconEvent.java new file mode 100644 index 000000000..74eb6cf40 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/beacon/BeaconEvent.java @@ -0,0 +1,30 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.beacon; + +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import java.util.Date; + +@Entity +@Data +public class BeaconEvent { + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + private Long id; + + private Date eventTime; + + private String eventName; + + public BeaconEvent() { + eventTime = new Date(); + } + + public BeaconEvent(BeaconEventType eventType) { + eventTime = new Date(); + this.eventName = eventType.name(); + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/beacon/BeaconEventType.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/beacon/BeaconEventType.java new file mode 100644 index 000000000..6856ea4a5 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/beacon/BeaconEventType.java @@ -0,0 +1,7 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.beacon; + +public enum BeaconEventType { + METADATA_SOURCE_MODIFIED, METADATA_SOURCE_REMOVED, METADATA_SOURCE_ENABLED, METADATA_SOURCE_CREATED, + METADATA_PROVIDER_MODIFIED, METADATA_PROVIDER_REMOVED, METADATA_PROVIDER_ENABLED, METADATA_PROVIDER_CREATED, + METADATA_FILTER_MODIFIED, METADATA_FILTER_REMOVED, METADATA_FILTER_ENABLED, METADATA_FILTER_CREATED +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/BeaconEventRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/BeaconEventRepository.java new file mode 100644 index 000000000..966b295cf --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/BeaconEventRepository.java @@ -0,0 +1,7 @@ +package edu.internet2.tier.shibboleth.admin.ui.repository; + +import edu.internet2.tier.shibboleth.admin.ui.domain.beacon.BeaconEvent; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BeaconEventRepository extends JpaRepository { +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/BeaconDataServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/BeaconDataServiceImpl.java index 97cb5f06d..962b9e6e1 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/BeaconDataServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/BeaconDataServiceImpl.java @@ -2,23 +2,26 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; -import edu.internet2.tier.shibboleth.admin.ui.service.beacon.BeaconDetail; -import edu.internet2.tier.shibboleth.admin.ui.service.beacon.ShibuiDetail; import edu.internet2.tier.shibboleth.admin.ui.domain.BeaconConfiguration; +import edu.internet2.tier.shibboleth.admin.ui.domain.beacon.BeaconEvent; +import edu.internet2.tier.shibboleth.admin.ui.domain.beacon.BeaconEventType; import edu.internet2.tier.shibboleth.admin.ui.repository.BeaconConfigurationRepository; +import edu.internet2.tier.shibboleth.admin.ui.repository.BeaconEventRepository; import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository; 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.security.repository.GroupsRepository; import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository; import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService; +import edu.internet2.tier.shibboleth.admin.ui.service.beacon.BeaconDetail; +import edu.internet2.tier.shibboleth.admin.ui.service.beacon.ShibuiDetail; import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.info.InfoEndpoint; +import org.springframework.stereotype.Service; import java.util.Arrays; -import java.util.HashMap; import java.util.Map; public class BeaconDataServiceImpl implements IBeaconDataService { @@ -35,11 +38,12 @@ public class BeaconDataServiceImpl implements IBeaconDataService { private RoleRepository roleRepository; private BeaconConfigurationRepository beaconConfigurationRepository; private UserService userService; + private BeaconEventRepository beaconEventRepository; public BeaconDataServiceImpl(String productName, InfoEndpoint info, String tierVersion, EntityDescriptorRepository entityDescriptorRepository, MetadataResolverRepository metadataResolverRepository, FilterRepository filterRepository, GroupsRepository groupsRepository, RoleRepository roleRepository, BeaconConfigurationRepository beaconConfigurationRepository, UserService userService, - HealthEndpoint health) { + HealthEndpoint health, BeaconEventRepository beaconEventRepository) { mapper = new ObjectMapper(); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // skip any null values @@ -56,16 +60,26 @@ public BeaconDataServiceImpl(String productName, InfoEndpoint info, String tierV this.roleRepository = roleRepository; this.beaconConfigurationRepository = beaconConfigurationRepository; this.userService = userService; + this.beaconEventRepository = beaconEventRepository; } @Override @SneakyThrows public String getBeaconData() { BeaconDetail detail = new BeaconDetail().setTbProduct(productName).setTbProductVersion(version).setTbTIERRelease(tierVersion).setShibui(getShibuiDetailData()); - return mapper.writeValueAsString(detail); } + @Override + public void addBeaconEvent(BeaconEvent event) { + beaconEventRepository.save(event); + } + + @Override + public void addEntityDescEnabledEvent() { + addBeaconEvent(new BeaconEvent(BeaconEventType.METADATA_SOURCE_ENABLED)); + } + private ShibuiDetail getShibuiDetailData() { BeaconConfiguration configuration = beaconConfigurationRepository.getReferenceById(1); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/IBeaconDataService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/IBeaconDataService.java index 4509b0584..ccbd4563e 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/IBeaconDataService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/IBeaconDataService.java @@ -1,7 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui.service; import com.fasterxml.jackson.core.JsonProcessingException; +import edu.internet2.tier.shibboleth.admin.ui.domain.beacon.BeaconEvent; public interface IBeaconDataService { String getBeaconData() throws JsonProcessingException; + + void addBeaconEvent(BeaconEvent event); + + void addEntityDescEnabledEvent(); } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAEntityDescriptorServiceImpl.java index 93a1dbce6..2d31663b3 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 @@ -10,6 +10,8 @@ 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.XSInteger; +import edu.internet2.tier.shibboleth.admin.ui.domain.beacon.BeaconEvent; +import edu.internet2.tier.shibboleth.admin.ui.domain.beacon.BeaconEventType; 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; @@ -72,6 +74,9 @@ @Slf4j @Service public class JPAEntityDescriptorServiceImpl implements EntityDescriptorService { + @Autowired + private IBeaconDataService beaconDataService; + @Autowired private EntityDescriptorRepository entityDescriptorRepository; @@ -257,6 +262,10 @@ public EntityDescriptorRepresentation createNew(EntityDescriptorRepresentation e ownershipRepository.deleteEntriesForOwnedObject(ed); ownershipRepository.save(new Ownership(userService.getCurrentUserGroup(), ed)); + if(ed.isServiceEnabled()) { + beaconDataService.addEntityDescEnabledEvent(); + } + beaconDataService.addBeaconEvent(new BeaconEvent(BeaconEventType.METADATA_SOURCE_CREATED)); return createRepresentationFromDescriptor(entityDescriptorRepository.save(ed)); } @@ -274,6 +283,7 @@ public EntityDescriptorRepresentation createNewEntityDescriptorFromXMLOrigin(Ent ed.setApproved(true); } EntityDescriptor savedEntity = entityDescriptorRepository.save(ed); + beaconDataService.addBeaconEvent(new BeaconEvent(BeaconEventType.METADATA_SOURCE_CREATED)); return createRepresentationFromDescriptor(savedEntity); } @@ -468,7 +478,7 @@ public void delete(String resourceId) throws ForbiddenException, PersistentEntit } ownershipRepository.deleteEntriesForOwnedObject(ed); entityDescriptorRepository.delete(ed); - + beaconDataService.addBeaconEvent(new BeaconEvent(BeaconEventType.METADATA_SOURCE_REMOVED)); } private EntityDescriptorProtocol determineEntityDescriptorProtocol(EntityDescriptor ed) { @@ -640,6 +650,8 @@ public EntityDescriptorRepresentation update(EntityDescriptorRepresentation edRe } validateEntityIdAndACSUrls(edRep); + boolean updateEnablesEd = !existingEd.isServiceEnabled() && edRep.isServiceEnabled(); + updateDescriptorFromRepresentation(existingEd, edRep); existingEd = entityDescriptorRepository.save(existingEd); ownershipRepository.deleteEntriesForOwnedObject(existingEd); @@ -647,6 +659,10 @@ public EntityDescriptorRepresentation update(EntityDescriptorRepresentation edRe public String getOwnerId() { return edRep.getIdOfOwner(); } public OwnerType getOwnerType() { return OwnerType.GROUP; } }, existingEd)); + if (updateEnablesEd) { + beaconDataService.addEntityDescEnabledEvent(); + } + beaconDataService.addBeaconEvent(new BeaconEvent(BeaconEventType.METADATA_SOURCE_MODIFIED)); return createRepresentationFromDescriptor(existingEd); } @@ -680,6 +696,7 @@ public EntityDescriptorRepresentation updateEntityDescriptorEnabledStatus(String ed.setServiceEnabled(enabled); if (enabled == true) { ed.setApproved(true); + beaconDataService.addEntityDescEnabledEvent(); } ed = entityDescriptorRepository.save(ed); return createRepresentationFromDescriptor(ed); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImpl.java index 03a72602e..ddfeef564 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/JPAFilterServiceImpl.java @@ -28,6 +28,9 @@ */ @Service public class JPAFilterServiceImpl implements FilterService { + @Autowired + IBeaconDataService beaconDataService; + @Autowired EntityDescriptorService entityDescriptorService; diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/BaseDataJpaTestConfiguration.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/BaseDataJpaTestConfiguration.groovy index 0244c32a3..3b96ccc82 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/BaseDataJpaTestConfiguration.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/BaseDataJpaTestConfiguration.groovy @@ -10,7 +10,10 @@ import edu.internet2.tier.shibboleth.admin.ui.configuration.ShibUIConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.StringTrimModule import edu.internet2.tier.shibboleth.admin.ui.controller.support.RestControllersSupport import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects +import edu.internet2.tier.shibboleth.admin.ui.repository.BeaconConfigurationRepository +import edu.internet2.tier.shibboleth.admin.ui.repository.BeaconEventRepository import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository +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.security.model.listener.GroupUpdatedEntityListener import edu.internet2.tier.shibboleth.admin.ui.security.model.listener.UserUpdatedEntityListener @@ -19,17 +22,30 @@ import edu.internet2.tier.shibboleth.admin.ui.security.permission.ShibUiPermissi import edu.internet2.tier.shibboleth.admin.ui.security.repository.DynamicRegistrationInfoRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.GroupsRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.OwnershipRepository +import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository import edu.internet2.tier.shibboleth.admin.ui.security.service.GroupServiceForTesting import edu.internet2.tier.shibboleth.admin.ui.security.service.GroupServiceImpl import edu.internet2.tier.shibboleth.admin.ui.security.service.IGroupService import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService +import edu.internet2.tier.shibboleth.admin.ui.service.BeaconDataServiceImpl import edu.internet2.tier.shibboleth.admin.ui.service.DirectoryService import edu.internet2.tier.shibboleth.admin.ui.service.DirectoryServiceImpl +import edu.internet2.tier.shibboleth.admin.ui.service.IBeaconDataService import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityServiceImpl import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator import edu.internet2.tier.shibboleth.admin.util.AttributeUtility import edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions import org.springframework.boot.SpringBootConfiguration +import org.springframework.boot.actuate.endpoint.annotation.Selector +import org.springframework.boot.actuate.health.DefaultHealthContributorRegistry +import org.springframework.boot.actuate.health.Health +import org.springframework.boot.actuate.health.HealthComponent +import org.springframework.boot.actuate.health.HealthEndpoint +import org.springframework.boot.actuate.health.HealthEndpointGroup +import org.springframework.boot.actuate.health.HealthEndpointGroups +import org.springframework.boot.actuate.health.Status +import org.springframework.boot.actuate.info.InfoContributor +import org.springframework.boot.actuate.info.InfoEndpoint import org.springframework.context.annotation.Bean import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.Import @@ -116,7 +132,7 @@ class BaseDataJpaTestConfiguration { } @Bean - public IShibUiPermissionEvaluator shibUiPermissionEvaluator(DynamicRegistrationInfoRepository driRepo, EntityDescriptorRepository entityDescriptorRepository, UserService userService) { + IShibUiPermissionEvaluator shibUiPermissionEvaluator(DynamicRegistrationInfoRepository driRepo, EntityDescriptorRepository entityDescriptorRepository, UserService userService) { return new ShibUiPermissionDelegate(driRepo, entityDescriptorRepository, userService); } @@ -132,4 +148,33 @@ class BaseDataJpaTestConfiguration { it } } + + @Bean + InfoEndpoint getInfoEndpoint() { + return new InfoEndpoint(new ArrayList()); + } + + @Bean + HealthEndpoint getHealthEndpoint() { + return new HealthEndpoint(new DefaultHealthContributorRegistry(), new HealthEndpointGroups() {} { + @Override HealthEndpointGroup getPrimary() { return null } + + @Override Set getNames() { return null } + + @Override HealthEndpointGroup get(String name) { return null } + }) { + @Override + HealthComponent healthForPath(@Selector(match = Selector.Match.ALL_REMAINING) String... path) { + return new Health(new Status(""), new HashMap()); + } + } + } + + @Bean + IBeaconDataService getBeaconDataService(InfoEndpoint info, EntityDescriptorRepository entityDescriptorRepository, MetadataResolverRepository metadataResolverRepository, FilterRepository filterRepository, + GroupsRepository groupsRepository, RoleRepository roleRepository, BeaconConfigurationRepository beaconConfigurationRepository, + UserService userService, HealthEndpoint healthEndpoint, BeaconEventRepository beaconEventRepository) { + BeaconDataServiceImpl result = new BeaconDataServiceImpl("SHIBUI_UNIT_TESTS", info, "NA", entityDescriptorRepository, metadataResolverRepository, filterRepository, groupsRepository, roleRepository, beaconConfigurationRepository, userService, healthEndpoint, beaconEventRepository); + return result; + } } \ No newline at end of file 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 1107af074..a666f102b 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 @@ -15,6 +15,7 @@ import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects 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.FilterService +import edu.internet2.tier.shibboleth.admin.ui.service.IBeaconDataService import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityServiceImpl import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator @@ -47,6 +48,9 @@ class MetadataFiltersControllerTests extends AbstractBaseDataJpaTest { @Autowired AttributeUtility attributeUtility + @Autowired + private IBeaconDataService beaconDataService + @Autowired CustomPropertiesConfiguration customPropertiesConfiguration @@ -77,6 +81,7 @@ class MetadataFiltersControllerTests extends AbstractBaseDataJpaTest { controller = new MetadataFiltersController ( repository: metadataResolverRepository, filterRepository: metadataFilterRepository, + beaconDataService: beaconDataService, groupService: groupService, userService: userService, metadataResolverService: new MetadataResolverService() {