Skip to content

Commit

Permalink
Merged in merge/shibui-2584 (pull request #662)
Browse files Browse the repository at this point in the history
Merge/shibui 2584

Approved-by: Doug Sonaty
  • Loading branch information
chasegawa authored and credman committed Jul 20, 2023
2 parents 4711ada + d5ed2ff commit 89a378c
Show file tree
Hide file tree
Showing 23 changed files with 363 additions and 43 deletions.
4 changes: 2 additions & 2 deletions backend/src/main/app-resources/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@
## Only set the installationID if you wish to define the ID used by the beacon. RECOMMENDED: ignore - a default random value will be used
## Only change the urls if instructed or if you wish to redirect for testing (if multiple, separate list with commas
## Only change the productName for testing purposes
## Set the cron time to send the beacon data at a specific time - if unset, the system will send at a random time between 12AM and 4AM once per day
## Set the cron time to send the beacon data at a specific time - if unset, the system will send at a random time between 12AM and 4AM once per day (random preferred)
# beacon:
# enabled: ture
# installationID: [user-defined value]
# urls: http://collector.testbed.tier.internet2.edu:5001
# productName: ShibUI
# send:
# cron: 0 4 * * * *
# cron: 0 59 3 * * ?

# pac4j-enabled: true
# pac4j:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -45,16 +46,16 @@
import edu.internet2.tier.shibboleth.admin.util.LuceneUtility;
import edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
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;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.info.InfoEndpoint;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
Expand All @@ -68,7 +69,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;
Expand All @@ -83,11 +83,13 @@
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Random;

@SpringBootConfiguration
@Import(SearchConfiguration.class)
@ComponentScan(basePackages = "{ edu.internet2.tier.shibboleth.admin.ui.service }")
@EnableConfigurationProperties({CustomPropertiesConfiguration.class, ShibUIConfiguration.class})
@Slf4j
public class CoreShibUiConfiguration {
@Bean
public OpenSamlObjects openSamlObjects() {
Expand Down Expand Up @@ -275,18 +277,39 @@ public LockProvider lockProvider(DataSource dataSource) {
}

@Bean
public String getBeaconCronValue(BeaconConfigurationRepository repo)
public String getBeaconCronValue(BeaconConfigurationRepository repo, @Value("${shibui.beacon.send.cron:nodefault}") String valueFromConfig)
{
Optional<BeaconConfiguration> bc = repo.findById(1);
return bc.isPresent() ? bc.get().getSendCron() : "0 3 * * * *";
Optional<BeaconConfiguration> obc = repo.findById(1);
BeaconConfiguration bc;
if (obc.isEmpty()) {
//set random cron in db
BeaconConfiguration newbc = new BeaconConfiguration();
Random rand = new Random();
String cron = "0 " + rand.nextInt(60) + " " + rand.nextInt(4) + " * * ?";
if (valueFromConfig.endsWith("?")) {
cron = valueFromConfig;
}
newbc.setSendCron(cron);
try {
repo.save(newbc);
}
catch(Exception e) {
log.debug("Error trying to create new BEACON CRON - should be ok");
}
bc = repo.findById(1).get();
} else {
bc = obc.get();
}
log.info("Scheduling beacon cron: {}", bc.getSendCron());
return bc.getSendCron();
}

@Bean
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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf((csrf) -> csrf.csrfTokenRequestHandler(requestHandler));
http
.authorizeHttpRequests()
.requestMatchers("/unsecured/**/*","/entities/**/*","/actuator/**").permitAll()
.requestMatchers("/unsecured/**/*","/entities/**/*","/actuator/**", "/api/beacon/send").permitAll()
.anyRequest().hasAnyRole(acceptedAuthenticationRoles)
.and().exceptionHandling().accessDeniedHandler((request, response, accessDeniedException) -> response.sendRedirect("/unsecured/error.html"))
.and().authenticationProvider(new SimpleAuthenticationProvider(adminUserService())).formLogin()
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -40,6 +43,9 @@ public class ActivateController {

@Autowired
private MetadataResolverService metadataResolverService;

@Autowired
private IBeaconDataService beaconDataService;

@PatchMapping(path = "/DynamicRegistration/{resourceId}/{mode}")
@Transactional
Expand Down Expand Up @@ -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);
}

Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public ResponseEntity<?> getDetail() throws JsonProcessingException {
return ResponseEntity.ok(service.getBeaconData());
}

@PostMapping("/send")
// This should be a POST, but to make it easier to hit, this is a GET
@GetMapping("/send")
public ResponseEntity<?> forceSendBeaconData() {
beaconReporter.sendBeaconData();
return ResponseEntity.ok("Manual push of beacon data completed");
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -50,6 +53,9 @@ public class MetadataFiltersController {
private static final Supplier<HttpClientErrorException> HTTP_400_BAD_REQUEST_EXCEPTION = () -> new HttpClientErrorException(BAD_REQUEST);
private static final Supplier<HttpClientErrorException> HTTP_404_CLIENT_ERROR_EXCEPTION = () -> new HttpClientErrorException(NOT_FOUND);

@Autowired
private IBeaconDataService beaconDataService;

@Autowired
org.opensaml.saml.metadata.resolver.MetadataResolver chainingMetadataResolver;

Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -156,7 +161,8 @@ 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));
newResolver.getMetadataFilters().forEach(f -> beaconDataService.addBeaconEvent(new BeaconEvent(BeaconEventType.METADATA_FILTER_CREATED)));
return ResponseEntity.created(getResourceUriFor(persistedResolver)).body(persistedResolver);
}

Expand All @@ -168,8 +174,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();
}

Expand All @@ -182,6 +187,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));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package edu.internet2.tier.shibboleth.admin.ui.domain.beacon;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Data;

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();
}
}
Original file line number Diff line number Diff line change
@@ -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_ENABLED, METADATA_PROVIDER_CREATED,
METADATA_FILTER_MODIFIED, METADATA_FILTER_REMOVED, METADATA_FILTER_ENABLED, METADATA_FILTER_CREATED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
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;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.Date;
import java.util.List;

public interface BeaconEventRepository extends JpaRepository<BeaconEvent, Long> {

@Query(value = "SELECT be FROM BeaconEvent be WHERE be.eventTime >= :sinceDate")
List<BeaconEvent> getEventsSince(@Param("sinceDate") Date sinceDate);

}
Loading

0 comments on commit 89a378c

Please sign in to comment.