Skip to content

Commit

Permalink
Merge branch 'feature/shibui-2270' of bitbucket.org:unicon/shib-idp-u…
Browse files Browse the repository at this point in the history
…i into feature/SHIBUI-2270-property-list
  • Loading branch information
rmathis committed Aug 18, 2022
2 parents 8c9d7f4 + 4ac5009 commit 057466f
Show file tree
Hide file tree
Showing 12 changed files with 1,628 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package edu.internet2.tier.shibboleth.admin.ui.service

import com.opencsv.CSVReader
import edu.internet2.tier.shibboleth.admin.ui.domain.ShibConfigurationProperty
import groovy.util.logging.Slf4j
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.context.event.ApplicationStartedEvent
import org.springframework.context.event.EventListener
import org.springframework.core.io.ClassPathResource
import org.springframework.core.io.Resource
import org.springframework.stereotype.Component

import javax.transaction.Transactional

@Component
@Slf4j
class ShibPropertiesBootstrap {
@Autowired
private ShibConfigurationService service

ShibPropertiesBootstrap(ShibConfigurationService service) {
this.service = service
}

@Transactional
@EventListener
void bootstrapUsersAndRoles(ApplicationStartedEvent e) {
log.info("Ensuring base Shibboleth properties configuration has loaded")

Resource resource = new ClassPathResource('shib_configuration_prop.csv')
final HashMap<String, ShibConfigurationProperty> propertiesMap = new HashMap<>()

// Read in the defaults in the configuration file
new CSVReader(new InputStreamReader(resource.inputStream)).each { fields ->
def (resource_id,category,config_file,description,idp_version,module,module_version,note,default_value,property_name,property_type,selection_items,property_value) = fields
ShibConfigurationProperty prop = new ShibConfigurationProperty().with {
it.resourceId = resource_id
it.category = category
it.configFile = config_file
it.description = description
it.idpVersion = idp_version
it.module = module
it.moduleVersion = module_version
it.note = note
it.defaultValue = default_value
it.description = description
it.propertyName = property_name
def pt = property_type
it.setPropertyType(pt)
it.selectionItems = selection_items
// we shouldn't have property values coming in from the config...
it
}
propertiesMap.put(prop.getPropertyName(), prop)
}

// If we already have the property in the db, ignore the configuration setup for that property
service.getExistingPropertyNames().each {
propertiesMap.remove(it)
}

// Save anything that's left
if (propertiesMap.size() > 0) {
log.info("Saving/loading [" + propertiesMap.size() + "] properties to the database")
service.addAll(propertiesMap.values())
}

log.info("COMPLETED: ensuring base Shibboleth properties configuration has loaded")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@

import edu.internet2.tier.shibboleth.admin.ui.domain.IRelyingPartyOverrideProperty;
import edu.internet2.tier.shibboleth.admin.ui.domain.RelyingPartyOverrideProperty;
import edu.internet2.tier.shibboleth.admin.ui.domain.ShibConfigurationProperty;
import edu.internet2.tier.shibboleth.admin.ui.service.CustomEntityAttributesDefinitionService;
import edu.internet2.tier.shibboleth.admin.ui.service.ShibConfigurationService;
import edu.internet2.tier.shibboleth.admin.ui.service.events.CustomEntityAttributeDefinitionChangeEvent;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;

@Configuration
@ConfigurationProperties(prefix = "custom")
public class CustomPropertiesConfiguration implements ApplicationListener<CustomEntityAttributeDefinitionChangeEvent> {
Expand All @@ -28,6 +28,10 @@ public class CustomPropertiesConfiguration implements ApplicationListener<Custom

private List<RelyingPartyOverrideProperty> overridesFromConfigFile = new ArrayList<>();

private List<ShibConfigurationProperty> shibprops = new ArrayList<>();

private ShibConfigurationService shibConfigurationService;

private void buildRelyingPartyOverrides() {
// Start over with a clean map and get the CustomEntityAttributesDefinitions from the DB
HashMap<String, IRelyingPartyOverrideProperty> reloaded = new HashMap<>();
Expand Down Expand Up @@ -68,6 +72,7 @@ public void onApplicationEvent(CustomEntityAttributeDefinitionChangeEvent arg0)
public void postConstruct() {
// Make sure we have the right data
buildRelyingPartyOverrides();
updateShibPropsDatabase();
}

public void setAttributes(List<? extends Map<String, String>> attributes) {
Expand All @@ -79,10 +84,36 @@ public void setCeadService(CustomEntityAttributesDefinitionService ceadService)
this.ceadService = ceadService;
}

@Autowired
public void setShibConfigurationService(ShibConfigurationService service) {
this.shibConfigurationService = service;
}

/**
* This setter will get used by Spring's property system to create objects from a config file (should the properties exist)
* This setter will get used by Spring's property system to create objects from application.yml (should the properties exist)
*/
public void setOverrides(List<RelyingPartyOverrideProperty> overridesFromConfigFile) {
this.overridesFromConfigFile = overridesFromConfigFile;
}
}

/**
* This setter will get used by Spring's property system to create objects from application.yml (should the properties exist)
*/
public void setShibprops(List<ShibConfigurationProperty> props) {
this.shibprops = props;
}

/**
* Add any custom properties from the application.yml - any incoming property with the same name as an existing property will be
* ignored (ie this will not update/replace information for existing properties). This shouldn't be considered standard, but
* offers users the ability to add properties to their system from an addon module, new feature etc.
*/
private void updateShibPropsDatabase() {
List<String> existingPropNames = shibConfigurationService.getExistingPropertyNames();
shibprops.forEach(prop -> {
if (!existingPropNames.contains(prop.getPropertyName())) {
shibConfigurationService.save(prop);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package edu.internet2.tier.shibboleth.admin.ui.controller;

import edu.internet2.tier.shibboleth.admin.ui.service.ShibConfigurationService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.tags.Tags;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value = "/api/shib")
@Tags(value = {@Tag(name = "Shibboleth Properties")})
public class ShibPropertiesController {
@Autowired
private ShibConfigurationService service;

@GetMapping("/properties")
@Transactional(readOnly = true)
public ResponseEntity<?> getAll() {
return ResponseEntity.ok(service.getAll());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package edu.internet2.tier.shibboleth.admin.ui.domain;

import com.fasterxml.jackson.annotation.JsonIgnore;
import edu.internet2.tier.shibboleth.admin.util.EmptyStringToNullConverter;
import lombok.Data;
import org.hibernate.envers.Audited;

import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.UUID;

@Entity(name = "shib_configuration_prop")
@Audited
@Data
public class ShibConfigurationProperty {
@Id
@Column(name = "resource_id", nullable = false)
String resourceId = UUID.randomUUID().toString();

@Column(name = "category", nullable = false)
String category;

@Column(name = "config_file", nullable = false)
String configFile;

@Column(name = "default_value")
@Convert(converter = EmptyStringToNullConverter.class)
String defaultValue;

@Column(name = "description")
@Convert(converter = EmptyStringToNullConverter.class)
String description;

@Column(name = "idp_version", nullable = false)
String idpVersion;

@Column(name = "module")
@Convert(converter = EmptyStringToNullConverter.class)
String module;

@Column(name = "module_version")
@Convert(converter = EmptyStringToNullConverter.class)
String moduleVersion;

@Column(name = "note")
@Convert(converter = EmptyStringToNullConverter.class)
String note;

@Column(name = "property_name", nullable = false)
String propertyName;

@Column(name = "property_type", nullable = false)
@JsonIgnore // display type is sent to the ui instead
PropertyType propertyType;

@Column(name = "selection_items")
@Convert(converter = EmptyStringToNullConverter.class)
String selectionItems;

public String getDisplayType() {
switch (propertyType) {
case BOOLEAN:
return propertyType.name().toLowerCase();
case INTEGER:
return "number";
case SELECTION_LIST:
return "list";
default: // DURATION, SPRING_BEAN_ID, STRING
return "string";
}
}

public void setPropertyType(String val) {
this.propertyType = PropertyType.valueOf(val);
}

}

enum PropertyType {
BOOLEAN, DURATION, INTEGER, SELECTION_LIST, SPRING_BEAN_ID, STRING
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package edu.internet2.tier.shibboleth.admin.ui.repository;

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

import java.util.List;

/**
* Repository to manage {@link ShibConfigurationProperty} instances.
*/
public interface ShibConfigurationRepository extends JpaRepository<ShibConfigurationProperty, String> {
@Query(value = "select property_name from shib_configuration_prop", nativeQuery = true)
List<String> getPropertyNames();
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class CustomEntityAttributesDefinitionServiceImpl implements CustomEntity
private ApplicationEventPublisher applicationEventPublisher;

@Autowired
EntityManager entityManager;
EntityManager entityManager; // Why is this here - it isn't used

@Autowired
private CustomEntityAttributeDefinitionRepository repository;
Expand Down Expand Up @@ -53,4 +53,4 @@ public List<CustomEntityAttributeDefinition> getAllDefinitions() {
private void notifyListeners() {
applicationEventPublisher.publishEvent(new CustomEntityAttributeDefinitionChangeEvent(this));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package edu.internet2.tier.shibboleth.admin.ui.service;

import edu.internet2.tier.shibboleth.admin.ui.domain.ShibConfigurationProperty;

import java.util.Collection;
import java.util.List;

public interface ShibConfigurationService {
void addAll(Collection<ShibConfigurationProperty> newProperties);

List<String> getExistingPropertyNames();

void save(ShibConfigurationProperty prop);

List<ShibConfigurationProperty> getAll();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package edu.internet2.tier.shibboleth.admin.ui.service;

import edu.internet2.tier.shibboleth.admin.ui.domain.ShibConfigurationProperty;
import edu.internet2.tier.shibboleth.admin.ui.repository.ShibConfigurationRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.List;

@Service
public class ShibConfigurationServiceImpl implements ShibConfigurationService {
@Autowired
private ShibConfigurationRepository repository;

@Override
public void addAll(Collection<ShibConfigurationProperty> newProperties) {
repository.saveAll(newProperties);
}

@Override
public List<String> getExistingPropertyNames() {
return repository.getPropertyNames();
}

@Override
public void save(ShibConfigurationProperty prop) {
repository.save(prop);
}

@Override
public List<ShibConfigurationProperty> getAll() {
return repository.findAll();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package edu.internet2.tier.shibboleth.admin.util;

import org.apache.commons.lang3.StringUtils;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

@Converter
public class EmptyStringToNullConverter implements AttributeConverter<String, String> {
@Override
public String convertToDatabaseColumn(String string) {
// if whitespace is set on a value, send null to the db
return StringUtils.defaultIfBlank(string, null);
}

@Override
public String convertToEntityAttribute(String dbData) {
// keep nulls from the db as nulls
return dbData;
}
}
14 changes: 13 additions & 1 deletion backend/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,16 @@ custom:
displayType: boolean
helpText: tooltip.ignore-request-signatures
attributeName: http://shibboleth.net/ns/profiles/ignoreRequestSignatures
attributeFriendlyName: ignoreRequestSignatures
attributeFriendlyName: ignoreRequestSignatures
# shibprops:
# - category: main # required
# configFile: random.properties # required
# defaultValue: foo
# description: whatever
# idpVersion: 4.1 # required
# module: some random module
# moduleVersion: 1
# note: this is an example for the application.yml file
# propertyName: example.property.name # required
# propertyType: SELECTION_LIST # required as one of: BOOLEAN, DURATION, INTEGER, SELECTION_LIST, SPRING_BEAN_ID, STRING
# selectionItems: dddd,eeee # required if propertyType is SELECTION_LIST - comma seperated values
Loading

0 comments on commit 057466f

Please sign in to comment.