Skip to content

Commit

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

Approved-by: Chad Redman
  • Loading branch information
chasegawa authored and credman committed Jul 11, 2023
2 parents be27aee + 90997b0 commit 1a665d4
Show file tree
Hide file tree
Showing 43 changed files with 663 additions and 391 deletions.
2 changes: 0 additions & 2 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,6 @@ dependencies {
runtimeOnly "com.microsoft.sqlserver:mssql-jdbc:${project.'sqlserverVersion'}"
//Pacj4 sub-project
runtimeOnly project(':pac4j-module')
//Beacon
runtimeOnly project(':beacon:spring')
// runtime libraries for later java versions
runtimeOnly 'org.glassfish.jaxb:jaxb-runtime:2.3.1' // com.sun.xml.bind package

Expand Down
14 changes: 14 additions & 0 deletions backend/src/main/app-resources/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@
## The first time the scheduler executes on this node, write out the entity descriptors to file [true|false] (default is true)
# entityDescriptor:
# writeOnStartup: true
### BEACON SETTINGS
## Set enabled to false to disable sending beacon data
## 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
# beacon:
# enabled: ture
# installationID: [user-defined value]
# urls: http://collector.testbed.tier.internet2.edu:5001
# productName: ShibUI
# send:
# cron: 0 4 * * * *

# pac4j-enabled: true
# pac4j:
# keystorePath: "/etc/shibui/samlKeystore.jks"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package edu.internet2.tier.shibboleth.admin.ui.configuration;

import com.fasterxml.jackson.databind.Module;
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.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.repository.MetadataResolversPositionOrderContainerRepository;
import edu.internet2.tier.shibboleth.admin.ui.scheduled.EntityDescriptorFilesScheduledTasks;
Expand All @@ -19,6 +22,7 @@
import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository;
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.DefaultMetadataResolversPositionOrderContainerService;
import edu.internet2.tier.shibboleth.admin.ui.service.DirectoryService;
import edu.internet2.tier.shibboleth.admin.ui.service.DirectoryServiceImpl;
Expand All @@ -29,6 +33,7 @@
import edu.internet2.tier.shibboleth.admin.ui.service.FileCheckingFileWritingService;
import edu.internet2.tier.shibboleth.admin.ui.service.FileWritingService;
import edu.internet2.tier.shibboleth.admin.ui.service.FilterTargetService;
import edu.internet2.tier.shibboleth.admin.ui.service.IBeaconDataService;
import edu.internet2.tier.shibboleth.admin.ui.service.JPADynamicRegistrationServiceImpl;
import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityServiceImpl;
import edu.internet2.tier.shibboleth.admin.ui.service.JPAFilterTargetServiceImpl;
Expand All @@ -42,20 +47,28 @@
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;
import org.springframework.beans.factory.annotation.Value;
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;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
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 @@ -64,6 +77,12 @@
import org.springframework.web.util.UrlPathHelper;

import javax.sql.DataSource;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

@SpringBootConfiguration
@Import(SearchConfiguration.class)
Expand Down Expand Up @@ -254,4 +273,49 @@ public DynamicRegistrationService dynamicRegistrationService(DynamicRegistration
public LockProvider lockProvider(DataSource dataSource) {
return new JdbcTemplateLockProvider(JdbcTemplateLockProvider.Configuration.builder().withJdbcTemplate(new JdbcTemplate(dataSource)).usingDbTime().build());
}

@Bean
public String getBeaconCronValue(BeaconConfigurationRepository repo)
{
Optional<BeaconConfiguration> bc = repo.findById(1);
return bc.isPresent() ? bc.get().getSendCron() : "0 3 * * * *";
}

@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);
return result;
}

@Bean
public List<URL> beaconEndpointUrl(@Value("${shibui.beacon.url}") String urls) throws MalformedURLException {
List<URL> result = new ArrayList<>();
for (String url : Arrays.asList(urls.split(","))) {
result.add(new URL(url));
}
return result;
}

@Bean
public AuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher, UserService userService) {
return new RecordLoginHandler(applicationEventPublisher, userService);
}

class RecordLoginHandler extends DefaultAuthenticationEventPublisher {
private UserService userService;

public RecordLoginHandler(ApplicationEventPublisher applicationEventPublisher, UserService userService) {
super(applicationEventPublisher);
this.userService = userService;
}

@Override
public void publishAuthenticationSuccess(Authentication authentication) {
super.publishAuthenticationSuccess(authentication);
userService.updateLoginRecord(authentication.getName());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.internet2.tier.shibboleth.admin.ui.configuration;

import edu.internet2.tier.shibboleth.admin.ui.domain.BeaconConfiguration;
import edu.internet2.tier.shibboleth.admin.ui.security.DefaultAuditorAware;
import edu.internet2.tier.shibboleth.admin.ui.security.model.Role;
import edu.internet2.tier.shibboleth.admin.ui.security.model.User;
Expand Down Expand Up @@ -65,6 +66,10 @@ public class SpringSecurityConfig {
@Autowired
private UserService userService;

public SpringSecurityConfig() {
BeaconConfiguration.setAuthMechanisms("default");
}

private UserDetailsService adminUserService() {
if (defaultPassword != null && !"".equals(defaultPassword)) {
User adminUser = userRepository.findByUsername(rootUser).orElseGet(() ->{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package edu.internet2.tier.shibboleth.admin.ui.configuration.postprocessors;

import edu.internet2.tier.shibboleth.admin.ui.domain.BeaconConfiguration;
import edu.internet2.tier.shibboleth.admin.ui.repository.BeaconConfigurationRepository;
import jakarta.persistence.EntityExistsException;
import jakarta.transaction.Transactional;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;

/**
* This approach for running logic waits until after the Spring context has been initialized.
* We will always use an ID of 1 for the beaconConfiguration, so if the data doesn't exist, we can try to write the data. If the
* data exists, we don't need to do anything.
*
* Because multiple instances could be starting at the same time, we must account for a save "losing" to another entity - that's ok
* and if we get an exception because the entity with the id already exists, we can just bail out.
*/
@Component
public class BeaconConfigurationStartupProcessing {
@Autowired
BeaconConfigurationRepository beaconRepo;

@EventListener
@Transactional
public void onApplicationEvent(ContextRefreshedEvent event) {
// If there is nothing in the db, create the entry to use going forward
if (beaconRepo.count() == 0) {
String appId = event.getApplicationContext().getEnvironment().getProperty("shibui.beacon.installationID");
BeaconConfiguration bc = new BeaconConfiguration();
bc.setInstallationId(StringUtils.isBlank(appId) ? UUID.randomUUID().toString() : appId);

String cronDef = event.getApplicationContext().getEnvironment().getProperty("shibui.beacon.send.cron");

// If not set, set a random time between 12AM-4AM
if (StringUtils.isBlank(cronDef)) {
int randomMin = ThreadLocalRandom.current().nextInt(0, 60);
int randomHour = ThreadLocalRandom.current().nextInt(0, 5);
cronDef = "" + randomMin + " " + randomHour + " * * * *";
}

bc.setSendCron(cronDef);

try {
beaconRepo.save(bc);
}
catch (EntityExistsException ignore) {
// Race between startup instances - as long as one of them won...
}
}
// if the entry exists, check that the cron timing hasn't changed
else {
String cronDef = event.getApplicationContext().getEnvironment().getProperty("shibui.beacon.send.cron");
if (StringUtils.isNotBlank(cronDef)) {
BeaconConfiguration bc = beaconRepo.getReferenceById(1);
if (StringUtils.isNotBlank(cronDef) && !cronDef.equals(bc.getSendCron())) {
bc.setSendCron(cronDef);
beaconRepo.save(bc);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package edu.internet2.tier.shibboleth.admin.ui.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import edu.internet2.tier.shibboleth.admin.ui.scheduled.BeaconReportingTask;
import edu.internet2.tier.shibboleth.admin.ui.service.IBeaconDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Profile({"dev", "very-dangerous"})
@RestController
@RequestMapping(value = "/api/beacon")
public class BeaconController {
@Autowired
BeaconReportingTask beaconReporter;

@Autowired
private IBeaconDataService service;

@GetMapping(value = "/detail")
public ResponseEntity<?> getDetail() throws JsonProcessingException {
return ResponseEntity.ok(service.getBeaconData());
}

@PostMapping("/send")
public ResponseEntity<?> forceSendBeaconData() {
beaconReporter.sendBeaconData();
return ResponseEntity.ok("Manual push of beacon data completed");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.info.InfoEndpoint;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
Expand All @@ -18,6 +19,7 @@

@Controller
public class RootUiViewController {
@Autowired HealthEndpoint healthEndpoint;

@Autowired InfoEndpoint infoEndpoint;

Expand Down Expand Up @@ -46,4 +48,9 @@ public void indexHtml(HttpServletRequest request, HttpServletResponse response)
writer.write(content.getBytes());
}
}

@GetMapping(value = "/health")
public ResponseEntity<?> getHealth() {
return ResponseEntity.ok(healthEndpoint.health());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package edu.internet2.tier.shibboleth.admin.ui.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Transient;
import lombok.Data;
import lombok.Setter;

@Data
@Entity
public class BeaconConfiguration {
@Id
private int id = 1;

private String installationId;

private String sendCron;

// Comma separated list of the auth mechanisms used.
@Setter
@Transient
private static String authMechanisms = "unset";

public static String getAuthMechanisms() {
return authMechanisms;
}

/**
* enforce that id will only ever be 1
*/
public void setId(int ignored) {
this.id = 1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package edu.internet2.tier.shibboleth.admin.ui.repository;

import edu.internet2.tier.shibboleth.admin.ui.domain.BeaconConfiguration;
import org.springframework.data.jpa.repository.JpaRepository;

public interface BeaconConfigurationRepository extends JpaRepository<BeaconConfiguration, Integer> {

}
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,6 @@ public interface EntityDescriptorRepository extends JpaRepository<EntityDescript
" and e.approved = false")
List<EntityDescriptorProjection> getEntityDescriptorsNeedingApproval(@Param("groupIds") List<String> groupIds);

@Query("SELECT COUNT(ed) FROM EntityDescriptor ed WHERE ed.serviceEnabled = true")
int getActiveEntityCount();
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package edu.internet2.tier.shibboleth.admin.ui.repository;

import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;

public interface FilterRepository extends CrudRepository<MetadataFilter, Long> {
MetadataFilter findByResourceId(String resourceId);
}

@Query("SELECT COUNT(f) FROM MetadataFilter f WHERE f.filterEnabled = true")
int getActiveFilterCount();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package edu.internet2.tier.shibboleth.admin.ui.repository;

import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;

/**
Expand All @@ -11,4 +12,7 @@ public interface MetadataResolverRepository extends CrudRepository<MetadataResol
MetadataResolver findByName(String name);

MetadataResolver findByResourceId(String resourceId);
}

@Query("SELECT COUNT(mr) FROM MetadataResolver mr WHERE mr.enabled = true")
int getActiveMetadataProviderCount();
}
Loading

0 comments on commit 1a665d4

Please sign in to comment.