Skip to content
This repository was archived by the owner on Nov 29, 2022. It is now read-only.

spring-projects/spring-security-saml#233 #461

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ def coreProjects = subprojects.findAll { !it.name.contains("sample") }
configure(allprojects) { project ->
group = "org.springframework.security.extensions"

ext.slf4jVersion = "1.7.25"
ext.springVersion = "4.3.14.RELEASE"
ext.springSecurityVersion = "4.2.4.RELEASE"
ext.bcprovVersion = "1.60"
ext.bcpkixVersion = "1.60"
ext.slf4jVersion = "1.7.29"
ext.springVersion = "4.3.25.RELEASE"
ext.springSecurityVersion = "4.2.13.RELEASE"
ext.bcprovVersion = "1.64"
ext.bcpkixVersion = "1.64"
ext.openSamlVersion = "2.6.6"
ext.openSamlXmlSec = "1.5.8"
ext.esapiVersion = "2.1.0.1"
ext.esapiVersion = "2.2.0.0"
ext.xalanVersion = "2.7.2"
ext.xmlApisVersion = "1.4.01"
ext.gradleScriptDir = "${rootProject.projectDir}/gradle"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,6 @@
*/
package org.springframework.security.saml.metadata;

import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.saml2.metadata.EntitiesDescriptor;
import org.opensaml.saml2.metadata.EntityDescriptor;
Expand Down Expand Up @@ -62,6 +50,19 @@
import org.springframework.security.saml.util.SAMLUtil;
import org.springframework.util.Assert;

import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
* Class offers extra services on top of the underlying chaining MetadataProviders. Manager keeps track of all available
* identity and service providers configured inside the chained metadata providers. Exactly one service provider can
Expand All @@ -80,46 +81,46 @@ public class MetadataManager extends ChainingMetadataProvider implements Extende
protected final Logger log = LoggerFactory.getLogger(MetadataManager.class);

// Lock for the instance
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

// Lock for the refresh mechanism
private final ReentrantReadWriteLock refreshLock = new ReentrantReadWriteLock();
protected final ReentrantReadWriteLock refreshLock = new ReentrantReadWriteLock();

private String hostedSPName;
protected String hostedSPName;

private String defaultIDP;
protected String defaultIDP;

private ExtendedMetadata defaultExtendedMetadata;
protected ExtendedMetadata defaultExtendedMetadata;

// Timer used to refresh the metadata upon changes
private Timer timer;
protected Timer timer;

// Internal of metadata refresh checking in ms
private long refreshCheckInterval = 10000l;
protected long refreshCheckInterval = 10000l;

// Flag indicating whether metadata needs to be reloaded
private boolean refreshRequired = true;
protected boolean refreshRequired = true;

// Storage for cryptographic data used to verify metadata signatures
protected KeyManager keyManager;

// All providers which were added, not all may be active
private List<ExtendedMetadataDelegate> availableProviders;
protected List<ExtendedMetadataDelegate> availableProviders;

/**
* Set of IDP names available in the system.
*/
private Set<String> idpName;
protected Set<String> idpName;

/**
* Set of SP names available in the system.
*/
private Set<String> spName;
protected Set<String> spName;

/**
* All valid aliases.
*/
private Set<String> aliasSet;
protected Set<String> aliasSet;

/**
* Creates new metadata manager, automatically registers itself for notifications from metadata changes and calls
Expand Down Expand Up @@ -258,18 +259,18 @@ public void refreshMetadata() {

try {

log.debug("Refreshing metadata provider {}", provider.toString());
log.debug("Refreshing metadata provider {}", provider);
initializeProviderFilters(provider);
initializeProvider(provider);
initializeProviderData(provider);

// Make provider available for queries
super.addMetadataProvider(provider);
log.debug("Metadata provider was initialized {}", provider.toString());
log.debug("Metadata provider was initialized {}", provider);

} catch (MetadataProviderException e) {

log.error("Initialization of metadata provider " + provider + " failed, provider will be ignored", e);
log.error("Initialization of metadata provider {} failed, provider will be ignored", provider, e);

}

Expand Down Expand Up @@ -551,19 +552,12 @@ protected void initializeProviderFilters(ExtendedMetadataDelegate provider) thro

log.debug("Created new trust manager for metadata provider {}", provider);

// Combine any existing filters with the signature verification
MetadataFilter currentFilter = provider.getMetadataFilter();
if (currentFilter != null) {
if (currentFilter instanceof MetadataFilterChain) {
log.debug("Adding signature filter into existing chain");
MetadataFilterChain chain = (MetadataFilterChain) currentFilter;
chain.getFilters().add(filter);
} else {
log.debug("Combining signature filter with the existing in a new chain");
MetadataFilterChain chain = new MetadataFilterChain();
chain.getFilters().add(currentFilter);
chain.getFilters().add(filter);
}
log.debug("Adding signature filter before existing filters");
MetadataFilterChain chain = new MetadataFilterChain();
chain.setFilters(Arrays.asList(filter, currentFilter));
provider.setMetadataFilter(chain);
} else {
log.debug("Adding signature filter");
provider.setMetadataFilter(filter);
Expand Down Expand Up @@ -640,19 +634,21 @@ protected PKIXValidationInformationResolver getPKIXResolver(MetadataProvider pro

// Resolve allowed certificates to build the anchors
List<X509Certificate> certificates = new LinkedList<X509Certificate>();
Set<String> trustedSubjectDns = new HashSet<String>();
for (String key : trustedKeys) {
log.debug("Adding PKIX trust anchor {} for metadata verification of provider {}", key, provider);
X509Certificate certificate = keyManager.getCertificate(key);
if (certificate != null) {
certificates.add(certificate);
trustedSubjectDns.add( certificate.getSubjectDN().getName() );
} else {
log.warn("Cannot construct PKIX trust anchor for key with alias {} for provider {}, key isn't included in the keystore", key, provider);
}
}

List<PKIXValidationInformation> info = new LinkedList<PKIXValidationInformation>();
info.add(new BasicPKIXValidationInformation(certificates, null, 4));
return new StaticPKIXValidationInformationResolver(info, trustedNames) {
return new StaticPKIXValidationInformationResolver(info, trustedNames != null ? trustedNames : trustedSubjectDns) {
@Override
public Set<String> resolveTrustedNames(CriteriaSet criteriaSet)
throws SecurityException, UnsupportedOperationException {
Expand Down Expand Up @@ -704,7 +700,7 @@ protected List<String> parseProvider(MetadataProvider provider) throws MetadataP
*/
private void addDescriptors(List<String> result, EntitiesDescriptor descriptors) throws MetadataProviderException {

log.debug("Found metadata EntitiesDescriptor with ID", descriptors.getID());
log.debug("Found metadata EntitiesDescriptor with ID {}", descriptors.getID());

if (descriptors.getEntitiesDescriptors() != null) {
for (EntitiesDescriptor descriptor : descriptors.getEntitiesDescriptors()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@
import java.net.InetAddress;
import java.net.Socket;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.*;

/**
* Socket factory can be used with HTTP Client for creation of SSL/TLS sockets. Implementation uses internal KeyManager
Expand Down Expand Up @@ -136,14 +133,17 @@ protected PKIXValidationInformationResolver getPKIXResolver() {

// Resolve allowed certificates to build the anchors
List<X509Certificate> certificates = new ArrayList<X509Certificate>(trustedKeys.size());
Set<String> subjectDNs = new HashSet<String>(trustedKeys.size());
for (String key : trustedKeys) {
log.debug("Adding PKIX trust anchor {} for SSL/TLS verification {}", key);
certificates.add(keyManager.getCertificate(key));
X509Certificate cert = keyManager.getCertificate(key);
certificates.add(cert);
subjectDNs.add(cert.getSubjectDN().getName());
}

List<PKIXValidationInformation> info = new LinkedList<PKIXValidationInformation>();
info.add(new BasicPKIXValidationInformation(certificates, null, 4));
return new StaticPKIXValidationInformationResolver(info, null);
return new StaticPKIXValidationInformationResolver(info, subjectDNs);

}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.springframework.security.saml.metadata;

import org.junit.Before;
import org.junit.Test;
import org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider;
import org.opensaml.xml.parse.ParserPool;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.saml.key.KeyManager;

import java.io.File;

import static junit.framework.Assert.assertNotNull;

public class MetadataManagerWithTLSConfigurer {

ApplicationContext context;
KeyManager keyManager;
MetadataManager manager;
ParserPool pool;

@Before
public void initialize() throws Exception {
String resName = "/" + getClass().getName().replace('.', '/') + ".xml";
context = new ClassPathXmlApplicationContext(resName);
keyManager = context.getBean("keyManager", KeyManager.class);
manager = context.getBean("metadata", MetadataManager.class);
pool = context.getBean("parserPool", ParserPool.class);
}

@Test
public void testExplicitKeyStore() throws Exception {
ExtendedMetadataDelegate provider = getMetadata("classpath:testSP_signed_ca2_chain.xml");
provider.setMetadataRequireSignature(true);
provider.setMetadataTrustCheck(true);
provider.setForceMetadataRevocationCheck(true);

manager.addMetadataProvider(provider);
manager.refreshMetadata();

assertNotNull(manager.getEntityDescriptor("test_ca2"));

}

protected ExtendedMetadataDelegate getMetadata(String fileName) throws Exception {
File file = context.getResource(fileName).getFile();
FilesystemMetadataProvider innerProvider = new FilesystemMetadataProvider(file);
innerProvider.setParserPool(pool);
return new ExtendedMetadataDelegate(innerProvider);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

<context:component-scan base-package="org.springframework.security.saml"/>

<!-- Central storage of cryptographic keys -->
<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
<constructor-arg value="classpath:org/springframework/security/saml/key/keystore.jks"/>
<constructor-arg type="java.lang.String" value="nalle123"/>
<constructor-arg>
<map>
<entry key="apollo" value="nalle123"/>
</map>
</constructor-arg>
<constructor-arg type="java.lang.String" value="apollo"/>
</bean>

<!-- IDP Metadata configuration - paths to metadata of IDPs in circle of trust is here -->
<!-- Do no forget to call iniitalize method on providers -->
<bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
<constructor-arg index="0">
<list/>
</constructor-arg>
<property name="hostedSPName" value="hostedSP"/>
<!-- OPTIONAL property: can tell the system which IDP should be used for authenticating user by default. -->
<property name="defaultIDP" value="nest3"/>
<property name="refreshCheckInterval" value="10000"/>
</bean>

<!-- XML parser pool needed for OpenSAML parsing -->
<bean id="parserPool" class="org.opensaml.xml.parse.BasicParserPool" scope="singleton"/>

<!-- Initialization of OpenSAML library-->
<bean class="org.springframework.security.saml.SAMLBootstrap"/>

<bean class="org.springframework.security.saml.trust.httpclient.TLSProtocolConfigurer">
<property name="sslHostnameVerification" value="default"/>
</bean>

</beans>
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
version=1.0.10.BUILD-SNAPSHOT
version=1.0.11.BUILD-SNAPSHOT
maxParallelForks=1