Skip to content
Snippets Groups Projects
Commit 834ae524 authored by OZGCloud's avatar OZGCloud
Browse files

OZG-7092 refactor service configurations

parent 3273062a
No related branches found
No related tags found
1 merge request!1OZG-7092 Anpassung TokenChecker
/*
* Copyright (c) 2024.
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
package de.ozgcloud.token;
import static de.ozgcloud.token.saml.SamlTokenUtils.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import jakarta.annotation.PostConstruct;
import org.opensaml.core.config.ConfigurationService;
import org.opensaml.core.config.InitializationException;
import org.opensaml.core.config.InitializationService;
import org.opensaml.core.criterion.EntityIdCriterion;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.core.xml.config.XMLObjectProviderRegistry;
import org.opensaml.saml.criterion.ProtocolCriterion;
import org.opensaml.saml.metadata.criteria.role.impl.EvaluableProtocolRoleDescriptorCriterion;
import org.opensaml.saml.saml2.encryption.Decrypter;
import org.opensaml.saml.saml2.encryption.EncryptedElementTypeEncryptedKeyResolver;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.credential.CredentialResolver;
import org.opensaml.security.credential.CredentialSupport;
import org.opensaml.security.credential.UsageType;
import org.opensaml.security.credential.criteria.impl.EvaluableEntityIDCredentialCriterion;
import org.opensaml.security.credential.criteria.impl.EvaluableUsageCredentialCriterion;
import org.opensaml.security.credential.impl.CollectionCredentialResolver;
import org.opensaml.security.criteria.UsageCriterion;
import org.opensaml.security.x509.BasicX509Credential;
import org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap;
import org.opensaml.xmlsec.encryption.support.ChainingEncryptedKeyResolver;
import org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;
import org.opensaml.xmlsec.encryption.support.InlineEncryptedKeyResolver;
import org.opensaml.xmlsec.encryption.support.SimpleRetrievalMethodEncryptedKeyResolver;
import org.opensaml.xmlsec.keyinfo.impl.CollectionKeyInfoCredentialResolver;
import org.opensaml.xmlsec.signature.support.SignatureTrustEngine;
import org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.security.saml2.Saml2Exception;
import org.springframework.security.saml2.core.Saml2X509Credential;
import org.springframework.stereotype.Component;
import de.ozgcloud.common.errorhandling.TechnicalException;
import de.ozgcloud.token.saml.ConfigurationEntity;
import de.ozgcloud.token.saml.SamlSetting;
import de.ozgcloud.token.saml.SamlSettingsRegistry;
import de.ozgcloud.token.saml.SamlTokenUtils;
import lombok.RequiredArgsConstructor;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
import net.shibboleth.utilities.java.support.xml.ParserPool;
import net.shibboleth.utilities.java.support.xml.XMLParserException;
@Component
@Configuration
@RequiredArgsConstructor
public class TokenCheckConfiguration {
private XMLObjectProviderRegistry registry;
private final TokenCheckProperties tokenCheckerProperties;
private final ParserPool parserPool;
private final SamlSettingsRegistry samlSettingsRegistry;
private static final EncryptedKeyResolver encryptedKeyResolver = new ChainingEncryptedKeyResolver(
Arrays.asList(new InlineEncryptedKeyResolver(), new EncryptedElementTypeEncryptedKeyResolver(),
new SimpleRetrievalMethodEncryptedKeyResolver()));
@PostConstruct
public void initOpenSAML() {
try {
registry = new XMLObjectProviderRegistry();
ConfigurationService.register(XMLObjectProviderRegistry.class, registry);
registry.setParserPool(parserPool);
InitializationService.initialize();
tokenCheckerProperties.getEntities().forEach(this::initSamlSetting);
} catch (InitializationException e) {
throw new TechnicalException("Initialization failed", e);
}
}
private void initSamlSetting(final ConfigurationEntity entity) {
var trustEngine = initTrustEngine(entity.getMetadata(), entity.getIdpEntityId());
var verificationCriteria = getVerificationCriteria(entity.getIdpEntityId());
var decrypter = initDecrypter(entity);
var samlSetting = SamlSetting.builder()
.trustEngine(trustEngine)
.criteriaSet(verificationCriteria)
.decrypter(decrypter)
.mappings(entity.getMappings())
.idIsPostfachId(entity.getUseIdAsPostfachId())
.build();
samlSettingsRegistry.addSetting(entity.getIdpEntityId(), samlSetting);
}
private SignatureTrustEngine initTrustEngine(Resource metadata, String idpEntityId) {
Set<Credential> credentials = new HashSet<>();
Collection<Saml2X509Credential> keys = getCertificatesFromMetadata(metadata);
for (Saml2X509Credential key : keys) {
var cred = new BasicX509Credential(key.getCertificate());
cred.setUsageType(UsageType.SIGNING);
cred.setEntityId(idpEntityId);
credentials.add(cred);
}
CredentialResolver credentialsResolver = new CollectionCredentialResolver(credentials);
return new ExplicitKeySignatureTrustEngine(credentialsResolver,
DefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver());
}
private CriteriaSet getVerificationCriteria(String idpEntityId) {
var criteria = new CriteriaSet();
criteria.add(new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(idpEntityId)));
criteria.add(new EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion("urn:oasis:names:tc:SAML:2.0:protocol")));
criteria.add(new EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING)));
return criteria;
}
private List<Saml2X509Credential> getCertificatesFromMetadata(Resource metadataResource) {
try (var metadata = metadataResource.getInputStream()) {
var xmlObject = xmlObject(metadata);
var descriptorOptional = findEntityDescriptor(xmlObject);
if (descriptorOptional.isPresent()) {
return SamlTokenUtils.getVerificationCertificates(descriptorOptional.get());
}
} catch (IOException e) {
throw new Saml2Exception("Error reading idp metadata.", e);
} catch (XMLParserException e) {
throw new Saml2Exception("Error initializing parser pool.", e);
}
throw new Saml2Exception("No IDPSSO Descriptors found");
}
private XMLObject xmlObject(InputStream inputStream) throws XMLParserException {
var document = parserPool.parse(inputStream);
var element = document.getDocumentElement();
var unmarshaller = registry.getUnmarshallerFactory().getUnmarshaller(element);
if (unmarshaller == null) {
throw new Saml2Exception("Unsupported element of type " + element.getTagName());
}
try {
return unmarshaller.unmarshall(element);
} catch (Exception ex) {
throw new Saml2Exception(ex);
}
}
private Decrypter initDecrypter(ConfigurationEntity entity) {
var credentials = new ArrayList<Credential>();
var decryptionX509Credential = SamlTokenUtils.getDecryptionCredential(entity.getKey(), entity.getCertificate());
var cred = CredentialSupport.getSimpleCredential(decryptionX509Credential.getCertificate(), decryptionX509Credential.getPrivateKey());
credentials.add(cred);
var resolver = new CollectionKeyInfoCredentialResolver(credentials);
var setupDecrypter = new Decrypter(null, resolver, encryptedKeyResolver);
setupDecrypter.setRootInNewDocument(true);
return setupDecrypter;
}
}
/*
* Copyright (c) 2024.
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
package de.ozgcloud.token;
import java.util.Map;
import org.opensaml.core.config.ConfigurationService;
import org.opensaml.core.config.InitializationException;
import org.opensaml.core.config.InitializationService;
import org.opensaml.core.xml.config.XMLObjectProviderRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import lombok.RequiredArgsConstructor;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.xml.BasicParserPool;
import net.shibboleth.utilities.java.support.xml.ParserPool;
@Component
@Configuration
@RequiredArgsConstructor
public class TokenCheckerConfiguration {
public static final String FEATURES_EXTERNAL_GENERAL_ENTITIES = "http://xml.org/sax/features/external-general-entities";
public static final String FEATURES_EXTERNAL_PARAMETER_ENTITIES = "http://xml.org/sax/features/external-parameter-entities";
public static final String FEATURES_DISALLOW_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl";
public static final String VALIDATION_SCHEMA_NORMALIZED_VALUE = "http://apache.org/xml/features/validation/schema/normalized-value";
public static final String FEATURE_SECURE_PROCESSING = "http://javax.xml.XMLConstants/feature/secure-processing";
@Bean
ParserPool parserPool() throws ComponentInitializationException {
var localParserPool = new BasicParserPool();
localParserPool.setBuilderFeatures(createFeatureMap());
localParserPool.initialize();
return localParserPool;
}
Map<String, Boolean> createFeatureMap() {
return Map.of(
FEATURES_EXTERNAL_GENERAL_ENTITIES, Boolean.FALSE,
FEATURES_EXTERNAL_PARAMETER_ENTITIES, Boolean.FALSE,
FEATURES_DISALLOW_DOCTYPE_DECL, Boolean.TRUE,
VALIDATION_SCHEMA_NORMALIZED_VALUE, Boolean.FALSE,
FEATURE_SECURE_PROCESSING, Boolean.TRUE);
}
@Bean
XMLObjectProviderRegistry xmlObjectProviderRegistry(ParserPool parserPool) throws InitializationException {
var registry = new XMLObjectProviderRegistry();
registry.setParserPool(parserPool);
ConfigurationService.register(XMLObjectProviderRegistry.class, registry);
InitializationService.initialize();
return registry;
}
}
package de.ozgcloud.token.saml; package de.ozgcloud.token.saml;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.opensaml.core.criterion.EntityIdCriterion;
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
import org.opensaml.saml.criterion.ProtocolCriterion;
import org.opensaml.saml.metadata.criteria.role.impl.EvaluableProtocolRoleDescriptorCriterion;
import org.opensaml.saml.saml2.core.Response; import org.opensaml.saml.saml2.core.Response;
import org.opensaml.saml.saml2.core.impl.ResponseUnmarshaller; import org.opensaml.saml.saml2.core.impl.ResponseUnmarshaller;
import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator; import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator;
import org.opensaml.security.credential.UsageType;
import org.opensaml.security.credential.criteria.impl.EvaluableEntityIDCredentialCriterion;
import org.opensaml.security.credential.criteria.impl.EvaluableUsageCredentialCriterion;
import org.opensaml.security.criteria.UsageCriterion;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import de.ozgcloud.token.TokenValidationProperties;
import lombok.RequiredArgsConstructor;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
@Configuration @Configuration
@RequiredArgsConstructor
public class SamlConfiguration { public class SamlConfiguration {
private final SamlTrustEngineFactory samlTrustEngineFactory;
private final SamlDecrypterFactory samlDecrypterFactory;
@Bean @Bean
SAMLSignatureProfileValidator sAMLSignatureProfileValidator() { SAMLSignatureProfileValidator samlSignatureProfileValidator() {
return new SAMLSignatureProfileValidator(); return new SAMLSignatureProfileValidator();
} }
...@@ -19,4 +37,31 @@ public class SamlConfiguration { ...@@ -19,4 +37,31 @@ public class SamlConfiguration {
ResponseUnmarshaller responseUnmarshaller() { ResponseUnmarshaller responseUnmarshaller() {
return (ResponseUnmarshaller) XMLObjectProviderRegistrySupport.getUnmarshallerFactory().getUnmarshaller(Response.DEFAULT_ELEMENT_NAME); return (ResponseUnmarshaller) XMLObjectProviderRegistrySupport.getUnmarshallerFactory().getUnmarshaller(Response.DEFAULT_ELEMENT_NAME);
} }
@Bean
SamlServiceRegistry samSettingsRegistry(TokenValidationProperties tokenValidationProperties) {
var registryBuilder = SamlServiceRegistry.builder();
for (var tokenEntity : tokenValidationProperties.getEntities()) {
registryBuilder.samlService(tokenEntity.getIdpEntityId(), samlTokenService(tokenEntity));
}
return registryBuilder.build();
}
SamlTokenService samlTokenService(TokenValidationProperties.TokenValidationProperty tokenValidationProperty) {
return SamlTokenService.builder()
.signatureTrustEngine(samlTrustEngineFactory.buildSamlTrustEngine(tokenValidationProperty))
.decrypter(samlDecrypterFactory.buildDecrypter(tokenValidationProperty))
.verificationCriteria(buildVerificationCriteria(tokenValidationProperty.getIdpEntityId()))
.userIdAsPostfachId(tokenValidationProperty.isUserIdAsPostfachId())
.build();
}
CriteriaSet buildVerificationCriteria(String idpEntityId) {
return Stream.of(
new EvaluableEntityIDCredentialCriterion(new EntityIdCriterion(idpEntityId)),
new EvaluableProtocolRoleDescriptorCriterion(new ProtocolCriterion("urn:oasis:names:tc:SAML:2.0:protocol")),
new EvaluableUsageCredentialCriterion(new UsageCriterion(UsageType.SIGNING))
).collect(Collectors.toCollection(CriteriaSet::new));
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment