diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/PlaceholderResolverComponentsConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/PlaceholderResolverComponentsConfiguration.java new file mode 100644 index 000000000..bd391e0d5 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/PlaceholderResolverComponentsConfiguration.java @@ -0,0 +1,21 @@ +package edu.internet2.tier.shibboleth.admin.ui.configuration; + +import edu.internet2.tier.shibboleth.admin.ui.service.TokenPlaceholderValueResolvingService; +import edu.internet2.tier.shibboleth.admin.util.TokenPlaceholderResolvers; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.ConfigurableEnvironment; + +@Configuration +public class PlaceholderResolverComponentsConfiguration { + + @Bean + public TokenPlaceholderValueResolvingService tokenPlaceholderValueResolvingService(ConfigurableEnvironment env) { + return TokenPlaceholderValueResolvingService.shibbolethPlaceholderPrefixAware(env.getPropertySources()); + } + + @Bean + public TokenPlaceholderResolvers tokenPlaceholderResolvers(TokenPlaceholderValueResolvingService service) { + return new TokenPlaceholderResolvers(service); + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/postprocessors/IdpHomeValueSettingEnvironmentPostProcessor.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/postprocessors/IdpHomeValueSettingEnvironmentPostProcessor.java new file mode 100644 index 000000000..8f8bd3499 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/postprocessors/IdpHomeValueSettingEnvironmentPostProcessor.java @@ -0,0 +1,49 @@ +package edu.internet2.tier.shibboleth.admin.ui.configuration.postprocessors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.env.EnvironmentPostProcessor; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; + +/** + * Spring Boot Environment Post Processor setting the value for idp.home property to a temp directory + * if no IDP_HOME environment variable has been set already. + * + * @author Dmitriy Kopylenko + */ +public class IdpHomeValueSettingEnvironmentPostProcessor implements EnvironmentPostProcessor { + + private static final String IDP_HOME_PROP = "idp.home"; + + private static final String METADATA_DIR = "metadata"; + + private static final Logger LOGGER = LoggerFactory.getLogger(IdpHomeValueSettingEnvironmentPostProcessor.class); + + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { + if(environment.getProperty(IDP_HOME_PROP) == null) { + Map map = new HashMap<>(1); + try { + Path tempDir = Files.createTempDirectory(null); + tempDir.toFile().deleteOnExit(); + String tempDirName = tempDir.toAbsolutePath().toString(); + Path tempMetadataSubDir = Paths.get(tempDirName, METADATA_DIR); + Files.createDirectories(tempMetadataSubDir); + map.put(IDP_HOME_PROP, tempDirName); + } catch (IOException e) { + LOGGER.error(e.getMessage()); + throw new RuntimeException(e); + } + environment.getPropertySources().addLast(new MapPropertySource("idp.home.propertysource", map)); + } + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/opensaml/OpenSamlFileBackedHTTPMetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/opensaml/OpenSamlFileBackedHTTPMetadataResolver.java index d0a9b6ff4..f1e551b8e 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/opensaml/OpenSamlFileBackedHTTPMetadataResolver.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/opensaml/OpenSamlFileBackedHTTPMetadataResolver.java @@ -17,6 +17,7 @@ import javax.annotation.Nullable; import static edu.internet2.tier.shibboleth.admin.util.DurationUtility.toMillis; +import static edu.internet2.tier.shibboleth.admin.util.TokenPlaceholderResolvers.placeholderResolverService; /** * @author Bill Smith (wsmith@unicon.net) @@ -45,8 +46,11 @@ public OpenSamlFileBackedHTTPMetadataResolver(ParserPool parserPool, OpenSamlMetadataResolverConstructorHelper.updateOpenSamlMetadataResolverFromReloadableMetadataResolverAttributes( this, sourceResolver.getReloadableMetadataResolverAttributes(), parserPool); - this.setBackupFile(sourceResolver.getBackingFile()); - this.setBackupFileInitNextRefreshDelay(toMillis(sourceResolver.getBackupFileInitNextRefreshDelay())); + this.setBackupFile(placeholderResolverService() + .resolveValueFromPossibleTokenPlaceholder(sourceResolver.getBackingFile())); + this.setBackupFileInitNextRefreshDelay(toMillis(placeholderResolverService() + .resolveValueFromPossibleTokenPlaceholder(sourceResolver.getBackupFileInitNextRefreshDelay()))); + this.setInitializeFromBackupFile(sourceResolver.getInitializeFromBackupFile()); this.setMetadataFilter(new MetadataFilterChain()); @@ -72,6 +76,7 @@ protected void processConditionalRetrievalHeaders(HttpResponse response) { protected void initMetadataResolver() throws ComponentInitializationException { super.initMetadataResolver(); + delegate.addIndexedDescriptorsFromBackingStore(this.getBackingStore(), this.sourceResolver.getResourceId(), indexWriter); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/opensaml/OpenSamlMetadataResolverConstructorHelper.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/opensaml/OpenSamlMetadataResolverConstructorHelper.java index c5a6845e4..d52e47448 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/opensaml/OpenSamlMetadataResolverConstructorHelper.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/opensaml/OpenSamlMetadataResolverConstructorHelper.java @@ -9,6 +9,7 @@ import org.opensaml.saml.metadata.resolver.impl.AbstractReloadingMetadataResolver; import static edu.internet2.tier.shibboleth.admin.util.DurationUtility.toMillis; +import static edu.internet2.tier.shibboleth.admin.util.TokenPlaceholderResolvers.placeholderResolverService; /** * @author Bill Smith (wsmith@unicon.net) @@ -21,11 +22,14 @@ public static void updateOpenSamlMetadataResolverFromDynamicMetadataResolverAttr AbstractDynamicMetadataResolver dynamicMetadataResolver = (AbstractDynamicMetadataResolver) metadataResolver; if (attributes.getBackgroundInitializationFromCacheDelay() != null) { - dynamicMetadataResolver.setBackgroundInitializationFromCacheDelay(toMillis(attributes.getBackgroundInitializationFromCacheDelay())); + dynamicMetadataResolver + .setBackgroundInitializationFromCacheDelay(toMillis(placeholderResolverService() + .resolveValueFromPossibleTokenPlaceholder(attributes.getBackgroundInitializationFromCacheDelay()))); } if (attributes.getCleanupTaskInterval() != null) { - dynamicMetadataResolver.setCleanupTaskInterval(toMillis(attributes.getCleanupTaskInterval())); + dynamicMetadataResolver.setCleanupTaskInterval(toMillis(placeholderResolverService() + .resolveValueFromPossibleTokenPlaceholder(attributes.getCleanupTaskInterval()))); } if (attributes.getInitializeFromPersistentCacheInBackground()) { @@ -33,19 +37,23 @@ public static void updateOpenSamlMetadataResolverFromDynamicMetadataResolverAttr } if (attributes.getMaxCacheDuration() != null) { - dynamicMetadataResolver.setMaxCacheDuration(toMillis(attributes.getMaxCacheDuration())); + dynamicMetadataResolver.setMaxCacheDuration(toMillis(placeholderResolverService() + .resolveValueFromPossibleTokenPlaceholder(attributes.getMaxCacheDuration()))); } if (attributes.getMaxIdleEntityData() != null) { - dynamicMetadataResolver.setMaxIdleEntityData(toMillis(attributes.getMaxIdleEntityData())); + dynamicMetadataResolver.setMaxIdleEntityData(toMillis(placeholderResolverService() + .resolveValueFromPossibleTokenPlaceholder(attributes.getMaxIdleEntityData()))); } if (attributes.getMinCacheDuration() != null) { - dynamicMetadataResolver.setMinCacheDuration(toMillis(attributes.getMinCacheDuration())); + dynamicMetadataResolver.setMinCacheDuration(toMillis(placeholderResolverService() + .resolveValueFromPossibleTokenPlaceholder(attributes.getMinCacheDuration()))); } if (attributes.getBackgroundInitializationFromCacheDelay() != null) { - dynamicMetadataResolver.setBackgroundInitializationFromCacheDelay(toMillis(attributes.getBackgroundInitializationFromCacheDelay())); + dynamicMetadataResolver.setBackgroundInitializationFromCacheDelay(toMillis(placeholderResolverService() + .resolveValueFromPossibleTokenPlaceholder(attributes.getBackgroundInitializationFromCacheDelay()))); } if (attributes.getRefreshDelayFactor() != null) { @@ -87,13 +95,17 @@ public static void updateOpenSamlMetadataResolverFromReloadableMetadataResolverA if (attributes != null) { if (attributes.getExpirationWarningThreshold() != null) { - reloadingMetadataResolver.setExpirationWarningThreshold(toMillis(attributes.getExpirationWarningThreshold())); + reloadingMetadataResolver + .setExpirationWarningThreshold(toMillis(placeholderResolverService() + .resolveValueFromPossibleTokenPlaceholder(attributes.getExpirationWarningThreshold()))); } if (attributes.getMaxRefreshDelay() != null) { - reloadingMetadataResolver.setMaxRefreshDelay(toMillis(attributes.getMaxRefreshDelay())); + reloadingMetadataResolver.setMaxRefreshDelay(toMillis(placeholderResolverService() + .resolveValueFromPossibleTokenPlaceholder(attributes.getMaxRefreshDelay()))); } if (attributes.getMinRefreshDelay() != null) { - reloadingMetadataResolver.setMinRefreshDelay(toMillis(attributes.getMinRefreshDelay())); + reloadingMetadataResolver.setMinRefreshDelay(toMillis(placeholderResolverService() + .resolveValueFromPossibleTokenPlaceholder(attributes.getMinRefreshDelay()))); } if (attributes.getResolveViaPredicatesOnly() != null) { diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/MetadataResolverConverterServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/MetadataResolverConverterServiceImpl.java index e1ed54f82..1ddcbb9a4 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/MetadataResolverConverterServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/MetadataResolverConverterServiceImpl.java @@ -28,6 +28,8 @@ import java.io.IOException; import java.net.URL; +import static edu.internet2.tier.shibboleth.admin.util.TokenPlaceholderResolvers.placeholderResolverService; + /** * @author Bill Smith (wsmith@unicon.net) */ @@ -59,7 +61,8 @@ private OpenSamlFileBackedHTTPMetadataResolver convertToOpenSamlRepresentation(F private OpenSamlFilesystemMetadataResolver convertToOpenSamlRepresentation(FilesystemMetadataResolver resolver) throws IOException, ResolverException, ComponentInitializationException { IndexWriter indexWriter = indexWriterService.getIndexWriter(resolver.getResourceId()); - URL url = Thread.currentThread().getContextClassLoader().getResource(resolver.getMetadataFile()); + URL url = Thread.currentThread().getContextClassLoader().getResource(placeholderResolverService() + .resolveValueFromPossibleTokenPlaceholder(resolver.getMetadataFile())); File metadataFile = new File(url.getPath()); OpenSamlFilesystemMetadataResolver openSamlResolver = new OpenSamlFilesystemMetadataResolver(openSamlObjects.getParserPool(), @@ -75,7 +78,8 @@ private OpenSamlLocalDynamicMetadataResolver convertToOpenSamlRepresentation(Loc XMLObjectLoadSaveManager manager = null; try { - manager = new FilesystemLoadSaveManager(resolver.getSourceDirectory()); + manager = new FilesystemLoadSaveManager(placeholderResolverService() + .resolveValueFromPossibleTokenPlaceholder(resolver.getSourceDirectory())); } catch (ConstraintViolationException e) { // the base directory string instance was null or empty //TODO: What should we do here? Currently, this causes a test to fail. @@ -95,7 +99,8 @@ private OpenSamlResourceBackedMetadataResolver convertToOpenSamlRepresentation(R //TODO: What sort of resource type should be created here? URL? break; case CLASSPATH: - resource = ResourceHelper.of(new ClassPathResource(resolver.getClasspathMetadataResource().getFile())); + resource = ResourceHelper.of(new ClassPathResource(placeholderResolverService() + .resolveValueFromPossibleTokenPlaceholder(resolver.getClasspathMetadataResource().getFile()))); break; default: throw new RuntimeException("Unsupported resource type!"); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/ShibbolethPlaceholderTokenAwareValueResolvingService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/ShibbolethPlaceholderTokenAwareValueResolvingService.java new file mode 100644 index 000000000..5ae9a60cc --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/ShibbolethPlaceholderTokenAwareValueResolvingService.java @@ -0,0 +1,31 @@ +package edu.internet2.tier.shibboleth.admin.ui.service; + +import org.springframework.core.env.PropertyResolver; +import org.springframework.core.env.PropertySources; +import org.springframework.core.env.PropertySourcesPropertyResolver; + + +/** + * Implementation of {@link TokenPlaceholderValueResolvingService} based on Spring Framework's property resolver which + * understands Shibboleth Idp custom placeholder prefix of %{ and can resolve property values from these + * placeholders against existing property sources. + * + * @author Dmitriy Kopylenko + */ +public class ShibbolethPlaceholderTokenAwareValueResolvingService implements TokenPlaceholderValueResolvingService { + + private PropertyResolver propertyResolver; + + ShibbolethPlaceholderTokenAwareValueResolvingService(PropertySources propertySources) { + PropertySourcesPropertyResolver propertySourcesPropertyResolver = new PropertySourcesPropertyResolver(propertySources); + propertySourcesPropertyResolver.setPlaceholderPrefix("%{"); + this.propertyResolver = propertySourcesPropertyResolver; + } + + @Override + public String resolveValueFromPossibleTokenPlaceholder(String potentialTokenPlaceholder) { + return potentialTokenPlaceholder != null + ? this.propertyResolver.resolveRequiredPlaceholders(potentialTokenPlaceholder) + : potentialTokenPlaceholder; + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/TokenPlaceholderValueResolvingService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/TokenPlaceholderValueResolvingService.java new file mode 100644 index 000000000..19d8217ff --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/TokenPlaceholderValueResolvingService.java @@ -0,0 +1,20 @@ +package edu.internet2.tier.shibboleth.admin.ui.service; + +import org.springframework.core.env.PropertyResolver; +import org.springframework.core.env.PropertySources; + +/** + * Generic API to resolve values from arbitrary tokenized placeholders such as '%{token.placeholder}' etc. + * + * @author Dmitriy Kopylenko + */ +@FunctionalInterface +public interface TokenPlaceholderValueResolvingService { + + String resolveValueFromPossibleTokenPlaceholder(String potentialTokenPlaceholder); + + + static TokenPlaceholderValueResolvingService shibbolethPlaceholderPrefixAware(PropertySources propertySources) { + return new ShibbolethPlaceholderTokenAwareValueResolvingService(propertySources); + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/TokenPlaceholderResolvers.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/TokenPlaceholderResolvers.java new file mode 100644 index 000000000..9ac8947b7 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/TokenPlaceholderResolvers.java @@ -0,0 +1,21 @@ +package edu.internet2.tier.shibboleth.admin.util; + +import edu.internet2.tier.shibboleth.admin.ui.service.TokenPlaceholderValueResolvingService; + +/** + * Accessor facade class to expose {@link TokenPlaceholderValueResolvingService} to non-Spring-managed classes. + * + * @author Dmitriy Kopylenko + */ +public class TokenPlaceholderResolvers { + + private static TokenPlaceholderValueResolvingService placeholderResolverService; + + public TokenPlaceholderResolvers(TokenPlaceholderValueResolvingService service) { + TokenPlaceholderResolvers.placeholderResolverService = service; + } + + public static TokenPlaceholderValueResolvingService placeholderResolverService() { + return placeholderResolverService; + } +} diff --git a/backend/src/main/resources/META-INF/spring.factories b/backend/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..fc0a891d0 --- /dev/null +++ b/backend/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.env.EnvironmentPostProcessor=\ + edu.internet2.tier.shibboleth.admin.ui.configuration.postprocessors.IdpHomeValueSettingEnvironmentPostProcessor \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptorTest.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptorTest.groovy index 79a539086..5fe53b85b 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptorTest.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/domain/EntityDescriptorTest.groovy @@ -2,6 +2,7 @@ package edu.internet2.tier.shibboleth.admin.ui.domain 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.PlaceholderResolverComponentsConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.HttpMetadataResolverAttributes @@ -18,6 +19,7 @@ import org.opensaml.saml.metadata.resolver.RefreshableMetadataResolver 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.boot.test.context.SpringBootTest import org.springframework.boot.test.context.TestConfiguration import org.springframework.context.annotation.Bean import org.springframework.data.jpa.repository.config.EnableJpaRepositories @@ -31,7 +33,7 @@ import java.nio.file.Files * @author Bill Smith (wsmith@unicon.net) */ @DataJpaTest -@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, InternationalizationConfiguration, MyConfig]) +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, InternationalizationConfiguration, MyConfig, PlaceholderResolverComponentsConfiguration]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @EntityScan("edu.internet2.tier.shibboleth.admin.ui") @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) @@ -58,14 +60,13 @@ class EntityDescriptorTest extends Specification { def "entity descriptors properly marshall to xml"() { given: - def tempDir = Files.createTempDirectory('test') ((OpenSamlChainingMetadataResolver)metadataResolver).resolvers = [ new OpenSamlFileBackedHTTPMetadataResolver( openSamlObjects.parserPool, indexWriterService.getIndexWriter('testme'), new FileBackedHttpMetadataResolver( metadataURL: 'https://idp.unicon.net/idp/shibboleth', - backingFile: "${tempDir.toString()}/test.xml", + backingFile: "%{idp.home}/metadata/test.xml", reloadableMetadataResolverAttributes: new ReloadableMetadataResolverAttributes(), httpMetadataResolverAttributes: new HttpMetadataResolverAttributes() ) diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/TokenPlaceholderValueResolvingServiceTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/TokenPlaceholderValueResolvingServiceTests.groovy new file mode 100644 index 000000000..8e3ba09f7 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/TokenPlaceholderValueResolvingServiceTests.groovy @@ -0,0 +1,81 @@ +package edu.internet2.tier.shibboleth.admin.ui.service + +import edu.internet2.tier.shibboleth.admin.ui.configuration.PlaceholderResolverComponentsConfiguration +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.util.TestPropertyValues +import org.springframework.core.env.ConfigurableEnvironment +import org.springframework.test.context.ContextConfiguration + +import spock.lang.Specification +import spock.lang.Subject + +/** + * @author Dmitriy Kopylenko + */ +@ContextConfiguration(classes = [PlaceholderResolverComponentsConfiguration]) +class TokenPlaceholderValueResolvingServiceTests extends Specification { + + @Autowired + @Subject + TokenPlaceholderValueResolvingService serviceUnderTest + + @Autowired + ConfigurableEnvironment environment + + static final IDP_HOME = '/tmp/test/idp' + static final REFRESH_INTERVAL = 'PT5M' + static final PLAIN_VALUE = 'Plain String value' + + def setup() { + def propPairs = ["idp.home=$IDP_HOME".toString(), "refresh.interval=$REFRESH_INTERVAL".toString()] + TestPropertyValues.of(propPairs).applyTo(environment) + } + + def "resolves correctly existing properties from well-formed shibboleth idp style placeholder tokens: %{}"() { + when: 'Valid placeholder token is passed in for which property values are defined' + def idpHome = serviceUnderTest.resolveValueFromPossibleTokenPlaceholder('%{idp.home}') + def refreshInterval = serviceUnderTest.resolveValueFromPossibleTokenPlaceholder('%{refresh.interval}') + + then: 'Correct property value resolution is performed' + idpHome == IDP_HOME + refreshInterval == REFRESH_INTERVAL + } + + def "returns value as is if no well-formed shibboleth idp style placeholder tokens: %{} are passed in"() { + when: 'Plain value without placeholder token is passed in' + def idpHome = serviceUnderTest.resolveValueFromPossibleTokenPlaceholder(IDP_HOME) + def plainValue = serviceUnderTest.resolveValueFromPossibleTokenPlaceholder(PLAIN_VALUE) + + then: 'Value returned as is' + idpHome == IDP_HOME + plainValue == PLAIN_VALUE + + when: 'Malformed placeholder value is passed in' + plainValue = serviceUnderTest.resolveValueFromPossibleTokenPlaceholder('%{malformed.value') + + then: + plainValue == '%{malformed.value' + } + + def "Throws IllegalArgumentException for unresolvable properties"() { + when: 'Valid placeholder token is passed in for which property values are undefined' + serviceUnderTest.resolveValueFromPossibleTokenPlaceholder("%{i.am.not.defined}") + + then: + thrown IllegalArgumentException + + when: 'Combination of resolvable and unresolvable tokens are passed in' + serviceUnderTest.resolveValueFromPossibleTokenPlaceholder("%{idp.home}/%{i.am.not.defined}") + + then: + thrown IllegalArgumentException + } + + def "resolves correctly combination of existing properties from well-formed shibboleth idp style placeholder tokens: %{}"() { + when: 'Valid placeholder token is passed in for which property values are defined' + def combinedValue = serviceUnderTest.resolveValueFromPossibleTokenPlaceholder('%{idp.home} AND %{refresh.interval}') + + then: 'Correct combined property values resolution is performed' + combinedValue == "$IDP_HOME AND $REFRESH_INTERVAL" + } +} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy index 13dc8a554..0c7fe9abf 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/TestObjectGenerator.groovy @@ -471,7 +471,7 @@ class TestObjectGenerator { new FileBackedHttpMetadataResolver().with { it.name = 'HTTPMetadata' it.xmlId = 'HTTPMetadata' - it.backingFile = 'unicon.xml' + it.backingFile = '%{idp.home}/metadata/metadata.xml' it.metadataURL = 'https://idp.unicon.net/idp/shibboleth' it.reloadableMetadataResolverAttributes = new ReloadableMetadataResolverAttributes().with { diff --git a/backend/src/test/resources/conf/278.2.xml b/backend/src/test/resources/conf/278.2.xml index 5a34f756b..ad3cb6c44 100644 --- a/backend/src/test/resources/conf/278.2.xml +++ b/backend/src/test/resources/conf/278.2.xml @@ -38,8 +38,9 @@ + diff --git a/backend/src/test/resources/conf/278.xml b/backend/src/test/resources/conf/278.xml index a337f72d7..cb5317531 100644 --- a/backend/src/test/resources/conf/278.xml +++ b/backend/src/test/resources/conf/278.xml @@ -31,8 +31,8 @@ + backingFile="%{idp.home}/metadata/metadata.xml" + metadataURL="https://idp.unicon.net/idp/shibboleth"> diff --git a/backend/src/test/resources/conf/532.xml b/backend/src/test/resources/conf/532.xml index 85fead908..b2be43498 100644 --- a/backend/src/test/resources/conf/532.xml +++ b/backend/src/test/resources/conf/532.xml @@ -7,6 +7,7 @@ xsi:schemaLocation="urn:mace:shibboleth:2.0:metadata http://shibboleth.net/schema/idp/shibboleth-metadata.xsd urn:mace:shibboleth:2.0:resource http://shibboleth.net/schema/idp/shibboleth-resource.xsd urn:mace:shibboleth:2.0:security http://shibboleth.net/schema/idp/shibboleth-security.xsd urn:oasis:names:tc:SAML:2.0:metadata http://docs.oasis-open.org/security/saml/v2.0/saml-schema-metadata-2.0.xsd urn:oasis:names:tc:SAML:2.0:assertion http://docs.oasis-open.org/security/saml/v2.0/saml-schema-assertion-2.0.xsd"> +