diff options
Diffstat (limited to 'id/server/modules/moa-id-module-eIDAS/src/main/java/at')
26 files changed, 1749 insertions, 720 deletions
diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/Constants.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/Constants.java index d93d739b1..b0d27cc32 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/Constants.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/Constants.java @@ -22,15 +22,12 @@ */ package at.gv.egovernment.moa.id.auth.modules.eidas; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - import org.opensaml.xml.encryption.EncryptionConstants; import org.opensaml.xml.signature.SignatureConstants; +//import eu.eidas.auth.engine.core.validator.eidas.EIDASAttributes; -import eu.eidas.auth.engine.core.eidas.EidasAttributesTypes; -import eu.eidas.auth.engine.core.validator.eidas.EIDASAttributes; +import eu.eidas.auth.commons.attribute.AttributeRegistries; +import eu.eidas.auth.commons.attribute.AttributeRegistry; /** * @author tlenz @@ -68,19 +65,22 @@ public class Constants { public static final long CONFIG_PROPS_SKEWTIME = 2 * 60 * 1000; //2 minutes skew time for response validation public static final int CONFIG_PROPS_METADATA_SOCKED_TIMEOUT = 20 * 1000; //20 seconds metadata socked timeout public static final long CONFIG_PROPS_METADATA_GARBAGE_TIMEOUT = 7 * 24 * 60 * 60 * 1000; //remove unused eIDAS metadata after 7 days - - //eIDAS attribute names - public static final String eIDAS_ATTR_PERSONALIDENTIFIER = EIDASAttributes.ATTRIBUTE_NAME_SUFFIX_PERSONIDENTIFIER; - public static final String eIDAS_ATTR_DATEOFBIRTH = EIDASAttributes.ATTRIBUTE_NAME_SUFFIX_DATEOFBIRTH; - public static final String eIDAS_ATTR_CURRENTGIVENNAME = EIDASAttributes.ATTRIBUTE_NAME_SUFFIX_FIRSTNAME; - public static final String eIDAS_ATTR_CURRENTFAMILYNAME = EIDASAttributes.ATTRIBUTE_NAME_SUFFIX_GIVENNAME; + + //eIDAS request parameters + public static final String eIDAS_REQ_NAMEID_FORMAT = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"; + + //eIDAS attribute names + public static final String eIDAS_ATTR_PERSONALIDENTIFIER = "PersonIdentifier"; + public static final String eIDAS_ATTR_DATEOFBIRTH = "DateOfBirth"; + public static final String eIDAS_ATTR_CURRENTGIVENNAME = "FirstName"; + public static final String eIDAS_ATTR_CURRENTFAMILYNAME = "FamilyName"; //http endpoint descriptions public static final String eIDAS_HTTP_ENDPOINT_SP_POST = "/eidas/sp/post"; public static final String eIDAS_HTTP_ENDPOINT_SP_REDIRECT = "/eidas/sp/redirect"; - public static final String eIDAS_HTTP_ENDPOINT_IDP_POST = "/eidas/idp/post"; + //public static final String eIDAS_HTTP_ENDPOINT_IDP_POST = "/eidas/idp/post"; + //public static final String eIDAS_HTTP_ENDPOINT_IDP_REDIRECT = "/eidas/idp/redirect"; public static final String eIDAS_HTTP_ENDPOINT_IDP_COLLEAGUEREQUEST = "/eidas/ColleagueRequest"; - public static final String eIDAS_HTTP_ENDPOINT_IDP_REDIRECT = "/eidas/idp/redirect"; public static final String eIDAS_HTTP_ENDPOINT_METADATA = "/eidas/metadata"; @@ -92,22 +92,38 @@ public class Constants { public static final int eIDAS_REVERSIONSLOG_SP_AUTHRESPONSE= 3404; //metadata constants - public final static Map<String, EidasAttributesTypes> METADATA_POSSIBLE_ATTRIBUTES = Collections.unmodifiableMap( - new HashMap<String, EidasAttributesTypes>(){ - private static final long serialVersionUID = 1L; - { - put(EIDASAttributes.ATTRIBUTE_GIVENNAME, EidasAttributesTypes.NATURAL_PERSON_MANDATORY); - put(EIDASAttributes.ATTRIBUTE_FIRSTNAME, EidasAttributesTypes.NATURAL_PERSON_MANDATORY); - put(EIDASAttributes.ATTRIBUTE_DATEOFBIRTH, EidasAttributesTypes.NATURAL_PERSON_MANDATORY); - put(EIDASAttributes.ATTRIBUTE_PERSONIDENTIFIER, EidasAttributesTypes.NATURAL_PERSON_MANDATORY); - - //TODO: add additional attributes for eIDAS with mandates - //put(EIDASAttributes.ATTRIBUTE_LEGALIDENTIFIER, EidasAttributesTypes.LEGAL_PERSON_MANDATORY); - //put(EIDASAttributes.ATTRIBUTE_LEGALNAME, EidasAttributesTypes.LEGAL_PERSON_MANDATORY); - } - } - ); +// public final static Map<String, EidasAttributesTypes> METADATA_POSSIBLE_ATTRIBUTES = Collections.unmodifiableMap( +// new HashMap<String, EidasAttributesTypes>(){ +// private static final long serialVersionUID = 1L; +// { +// put(EIDASAttributes.ATTRIBUTE_GIVENNAME, EidasAttributesTypes.NATURAL_PERSON_MANDATORY); +// put(EIDASAttributes.ATTRIBUTE_FIRSTNAME, EidasAttributesTypes.NATURAL_PERSON_MANDATORY); +// put(EIDASAttributes.ATTRIBUTE_DATEOFBIRTH, EidasAttributesTypes.NATURAL_PERSON_MANDATORY); +// put(EIDASAttributes.ATTRIBUTE_PERSONIDENTIFIER, EidasAttributesTypes.NATURAL_PERSON_MANDATORY); +// +// //TODO: add additional attributes for eIDAS with mandates +// //put(EIDASAttributes.ATTRIBUTE_LEGALIDENTIFIER, EidasAttributesTypes.LEGAL_PERSON_MANDATORY); +// //put(EIDASAttributes.ATTRIBUTE_LEGALNAME, EidasAttributesTypes.LEGAL_PERSON_MANDATORY); +// } +// } +// ); + public static final AttributeRegistry NAT_ATTR = + AttributeRegistries.of( eu.eidas.auth.engine.core.eidas.spec.NaturalPersonSpec.Definitions.PERSON_IDENTIFIER, + eu.eidas.auth.engine.core.eidas.spec.NaturalPersonSpec.Definitions.CURRENT_FAMILY_NAME, + eu.eidas.auth.engine.core.eidas.spec.NaturalPersonSpec.Definitions.CURRENT_GIVEN_NAME, + eu.eidas.auth.engine.core.eidas.spec.NaturalPersonSpec.Definitions.DATE_OF_BIRTH + ); + + public static final AttributeRegistry LEGAL_ATTR = + AttributeRegistries.of( eu.eidas.auth.engine.core.eidas.spec.LegalPersonSpec.Definitions.LEGAL_PERSON_IDENTIFIER, + eu.eidas.auth.engine.core.eidas.spec.LegalPersonSpec.Definitions.LEGAL_NAME + ); + + public static final AttributeRegistry MOA_IDP_ATTR_REGISTRY = + AttributeRegistries.copyOf(NAT_ATTR, LEGAL_ATTR); + + public static final String METADATA_ALLOWED_ALG_DIGIST = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256 + ";" + SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512 ; diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDAsExtensionProcessor.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOASWSigner.java index 5837d7dbf..302c12aaa 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDAsExtensionProcessor.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOASWSigner.java @@ -20,29 +20,37 @@ * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. */ -package at.gv.egovernment.moa.id.auth.modules.eidas.engine; +package at.gv.egovernment.moa.id.auth.modules.eidas.config; -import java.util.HashSet; -import java.util.Set; +import java.util.Map; import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; -import eu.eidas.auth.engine.core.ExtensionProcessorI; -import eu.eidas.auth.engine.core.eidas.EidasExtensionProcessor; +import eu.eidas.auth.engine.configuration.SamlEngineConfigurationException; +import eu.eidas.auth.engine.configuration.dom.ConfigurationAdapter; +import eu.eidas.auth.engine.configuration.dom.ConfigurationKey; +import eu.eidas.auth.engine.core.impl.KeyStoreProtocolSigner; +import eu.eidas.samlengineconfig.CertificateConfigurationManager; /** * @author tlenz * */ -public class MOAeIDAsExtensionProcessor extends EidasExtensionProcessor implements ExtensionProcessorI { +public class MOASWSigner extends KeyStoreProtocolSigner { + public MOASWSigner(Map<String, String> properties) throws SamlEngineConfigurationException { + super(properties); + + } + /** - * Add only eIDAS attributes which are supported by Austrian eIDAS node - * + * @param configManager + * @throws SamlEngineConfigurationException */ - @Override - public Set<String> getSupportedAttributes(){ - Set<String> supportedAttributes=new HashSet<String>( Constants.METADATA_POSSIBLE_ATTRIBUTES.keySet()); - - return supportedAttributes; - } + public MOASWSigner(CertificateConfigurationManager configManager) throws SamlEngineConfigurationException { + super(ConfigurationAdapter.adapt(configManager).getInstances().get(Constants.eIDAS_SAML_ENGINE_NAME).getConfigurationEntries().get(ConfigurationKey.SIGNATURE_CONFIGURATION.getKey()).getParameters()); + + } + + + } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAeIDASSAMLEngineConfigurationImpl.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAeIDASSAMLEngineConfigurationImpl.java index 5d1874157..9f6ba4af4 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAeIDASSAMLEngineConfigurationImpl.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAeIDASSAMLEngineConfigurationImpl.java @@ -42,9 +42,7 @@ import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProviderFactory; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.FileUtils; import at.gv.egovernment.moa.util.MiscUtil; - import eu.eidas.samlengineconfig.BinaryParameter; -import eu.eidas.samlengineconfig.ConfigurationParameter; import eu.eidas.samlengineconfig.EngineInstance; import eu.eidas.samlengineconfig.InstanceConfiguration; import eu.eidas.samlengineconfig.PropsParameter; @@ -57,10 +55,10 @@ import eu.eidas.samlengineconfig.SamlEngineConfiguration; public class MOAeIDASSAMLEngineConfigurationImpl extends SamlEngineConfiguration { - private static final String KEYSTORE_PATH="keystorePath"; - private static final String METADATA_KEYSTORE_PATH="metadata.keystorePath"; + private static final String KEYSTORE_PATH="keyStorePath"; + private static final String METADATA_KEYSTORE_PATH="metadata.keyStorePath"; private static final String ENCRYPTION_ACTIVATION="encryptionActivation"; - private static final String[] BINARY_PARAMETERS={KEYSTORE_PATH, ENCRYPTION_ACTIVATION,METADATA_KEYSTORE_PATH}; + public static final String[] BINARY_PARAMETERS={KEYSTORE_PATH, ENCRYPTION_ACTIVATION,METADATA_KEYSTORE_PATH}; public List<EngineInstance> getInstances(){ return super.getInstances(); @@ -95,7 +93,7 @@ public class MOAeIDASSAMLEngineConfigurationImpl extends //add basic eIDAS SAML-engine configuration MOAeIDASSAMLInstanceConfigurationImpl samlBaseConfig = new MOAeIDASSAMLInstanceConfigurationImpl(); samlBaseConfig.setName(Constants.eIDAS_SAML_ENGINE_NAME_ID_BASICCONFIG); - samlBaseConfig.addParameter(loadConfigurationFromExternalFile(Constants.CONIG_PROPS_EIDAS_SAMLENGINE_BASIC_CONFIGFILE)); + samlBaseConfig.addParameter(buildPropsParameter(Constants.CONIG_PROPS_EIDAS_SAMLENGINE_BASIC_CONFIGFILE)); engineConfigs.add(samlBaseConfig); //add signing eIDAS SAML-engine configuration @@ -103,7 +101,7 @@ public class MOAeIDASSAMLEngineConfigurationImpl extends samlSignConfig.setName(Constants.eIDAS_SAML_ENGINE_NAME_ID_SIGNATURECONFIG); samlSignConfig.addParameter(Constants.eIDAS_SAML_ENGINE_NAME_ID_CLASS, Constants.SAML_SIGNING_IMPLENTATION); - + //TODO: load signing keys directly from MOA-ID configuration in finale version samlSignConfig.addParameter(loadConfigurationFromExternalFile(Constants.CONIG_PROPS_EIDAS_SAMLENGINE_SIGN_CONFIGFILE)); engineConfigs.add(samlSignConfig); @@ -122,16 +120,16 @@ public class MOAeIDASSAMLEngineConfigurationImpl extends super.addInstance(engineInst); } - + /** * Load an external eIDAS SAML-engine configuration file, which is referenced from MOA-ID configuration * * @param key Configuration key, which is used in property based MOA-ID configuration file - * @return eIDAS SAML-engine configuration object + * @return eIDAS SAML-engine configuration Properties * @throws ConfigurationException */ - private ConfigurationParameter loadConfigurationFromExternalFile(String key) throws ConfigurationException { + private Properties loadConfigurationFromExternalFile(String key) throws ConfigurationException { String configFile = AuthConfigurationProviderFactory.getInstance().getBasicMOAIDConfiguration(key); if (MiscUtil.isEmpty(configFile)) { @@ -141,15 +139,21 @@ public class MOAeIDASSAMLEngineConfigurationImpl extends return null; } - Properties inputProps = loadPropsFromXml(configFile); - return buildPropsParameter(inputProps, configFile); + Properties inputProps = loadPropsFromXml(configFile); + return inputProps; + //return buildPropsParameter(inputProps, configFile); } - private PropsParameter buildPropsParameter(Properties inputProps, String fileName) throws EIDASEngineConfigurationException { + private PropsParameter buildPropsParameter(String configKey) throws ConfigurationException { + Properties inputProps = loadConfigurationFromExternalFile(configKey); + + String configFile = + AuthConfigurationProviderFactory.getInstance().getBasicMOAIDConfiguration(configKey); + PropsParameter outputProps = new PropsParameter(); - outputProps.setFileName(fileName); + outputProps.setFileName(configFile); //original eIDAS SAML-engine use this identifier outputProps.setName("fileConfiguration"); diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAeIDASSAMLInstanceConfigurationImpl.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAeIDASSAMLInstanceConfigurationImpl.java index dccd39905..2d76be206 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAeIDASSAMLInstanceConfigurationImpl.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAeIDASSAMLInstanceConfigurationImpl.java @@ -22,9 +22,20 @@ */ package at.gv.egovernment.moa.id.auth.modules.eidas.config; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; import java.util.List; +import java.util.Map.Entry; +import java.util.Properties; +import at.gv.egovernment.moa.id.commons.api.exceptions.ConfigurationException; +import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProviderFactory; +import at.gv.egovernment.moa.util.FileUtils; import eu.eidas.samlengineconfig.ConfigurationParameter; import eu.eidas.samlengineconfig.InstanceConfiguration; import eu.eidas.samlengineconfig.StringParameter; @@ -56,5 +67,39 @@ public class MOAeIDASSAMLInstanceConfigurationImpl extends addParameter(param); } + + public void addParameter(Properties parameters) { + Iterator<Entry<Object, Object>> paramInterator = parameters.entrySet().iterator(); + while (paramInterator.hasNext()) { + Entry<Object, Object> next = paramInterator.next(); + + StringParameter param = new StringParameter(); + String keyName = (String) next.getKey(); + param.setName(keyName); + + //make path to binary files absolute + if (Arrays.asList(MOAeIDASSAMLEngineConfigurationImpl.BINARY_PARAMETERS).contains(keyName)) + try { + String absoluteConfigFile = FileUtils.makeAbsoluteURL( + (String)next.getValue(), + AuthConfigurationProviderFactory.getInstance().getRootConfigFileDir()); + + URI uri = new URL(absoluteConfigFile).toURI(); + param.setValue(uri.getRawPath().substring(1)); + + } catch (ConfigurationException | MalformedURLException | URISyntaxException e) { + //TODO: make final!!!! + e.printStackTrace(); + param.setValue(next.getValue()); + + } + else + param.setValue(next.getValue()); + + addParameter(param); + + } + + } } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/ModifiedEncryptionSW.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/ModifiedEncryptionSW.java index 1ba344fd1..9ad5f0db3 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/ModifiedEncryptionSW.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/ModifiedEncryptionSW.java @@ -1,18 +1,95 @@ package at.gv.egovernment.moa.id.auth.modules.eidas.config; +import java.security.cert.X509Certificate; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; + +import com.google.common.collect.ImmutableMap; +import com.sun.istack.Nullable; + import at.gv.egovernment.moa.id.commons.api.AuthConfiguration; import at.gv.egovernment.moa.id.commons.api.exceptions.ConfigurationException; import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProviderFactory; import at.gv.egovernment.moa.logging.Logger; -import eu.eidas.auth.engine.core.impl.EncryptionSW; +import eu.eidas.auth.commons.EidasErrorKey; +import eu.eidas.auth.commons.io.ReloadableProperties; +import eu.eidas.auth.engine.configuration.SamlEngineConfigurationException; +import eu.eidas.auth.engine.configuration.dom.EncryptionKey; +import eu.eidas.auth.engine.core.impl.CertificateValidator; +import eu.eidas.auth.engine.core.impl.KeyStoreSamlEngineEncryption; +import eu.eidas.auth.engine.xml.opensaml.CertificateUtil; +import eu.eidas.engine.exceptions.EIDASSAMLEngineException; /** * This encryption module asks the moa configuration on whether to encrypt the response or not. In doubt, encryption is enforced. */ -public class ModifiedEncryptionSW extends EncryptionSW { +public class ModifiedEncryptionSW extends KeyStoreSamlEngineEncryption { + + private final ImmutableMap<String, String> properties; + + private final ReloadableProperties encryptionActivationProperties; + + private static ReloadableProperties initActivationConf(Map<String, String> properties) { + String activationConfigurationFile = EncryptionKey.ENCRYPTION_ACTIVATION.getAsString(properties); + Logger.debug("File containing encryption configuration: \"" + activationConfigurationFile + "\""); + return new ReloadableProperties(activationConfigurationFile); + } + + /** + * @param properties + * @throws SamlEngineConfigurationException + */ + public ModifiedEncryptionSW(Map<String, String> properties) throws SamlEngineConfigurationException { + super(properties); + this.properties = ImmutableMap.copyOf(properties); + encryptionActivationProperties = initActivationConf(properties); + } + + /* (non-Javadoc) + * @see eu.eidas.auth.engine.core.ProtocolEncrypterI#getEncryptionCertificate(java.lang.String) + */ + @Override + @Nullable + public X509Certificate getEncryptionCertificate(@Nullable String destinationCountryCode) + throws EIDASSAMLEngineException { + if (isEncryptionEnabled(destinationCountryCode)) { + String issuerKey = new StringBuilder(EncryptionKey.RESPONSE_TO_POINT_ISSUER_PREFIX.getKey()).append( + destinationCountryCode).toString(); + String serialNumberKey = + new StringBuilder(EncryptionKey.RESPONSE_TO_POINT_SERIAL_NUMBER_PREFIX.getKey()).append( + destinationCountryCode).toString(); + String serialNumber = properties.get(serialNumberKey); + String responseToPointIssuer = properties.get(issuerKey); + if (StringUtils.isNotBlank(responseToPointIssuer)) { + for (final X509Certificate certificate : getEncryptionCertificates()) { + if (CertificateUtil.matchesCertificate(serialNumber, responseToPointIssuer, certificate)) { + + if (isDisallowedSelfSignedCertificate()) { + CertificateValidator.checkCertificateIssuer(certificate); + } + if (isCheckedValidityPeriod()) { + CertificateValidator.checkCertificateValidityPeriod(certificate); + } + + return certificate; + } + } + throw new EIDASSAMLEngineException(EidasErrorKey.SAML_ENGINE_INVALID_CERTIFICATE.errorCode(), + EidasErrorKey.SAML_ENGINE_INVALID_CERTIFICATE.errorMessage()); + } else { + Logger.error("Encryption of SAML Response NOT done, because no \"" + issuerKey + + "\" configured!"); + } + } + return null; + } + /* (non-Javadoc) + * @see eu.eidas.auth.engine.core.ProtocolEncrypterI#isEncryptionEnabled(java.lang.String) + */ @Override - public boolean isEncryptionEnable(String countryCode) { + public boolean isEncryptionEnabled(String countryCode) { // - encrypt if so configured try { AuthConfiguration moaconfig = AuthConfigurationProviderFactory.getInstance(); diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASChainingMetadataProvider.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASChainingMetadataProvider.java index f062ad3c2..ca36b5ee5 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASChainingMetadataProvider.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASChainingMetadataProvider.java @@ -40,7 +40,7 @@ import at.gv.egovernment.moa.id.protocols.pvp2x.verification.metadata.MOASPMetad import at.gv.egovernment.moa.id.saml2.MetadataFilterChain; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.MiscUtil; -import eu.eidas.auth.engine.AbstractSAMLEngine; +import eu.eidas.auth.engine.AbstractProtocolEngine; @Service("eIDASMetadataProvider") public class MOAeIDASChainingMetadataProvider implements ObservableMetadataProvider, @@ -222,7 +222,7 @@ public class MOAeIDASChainingMetadataProvider implements ObservableMetadataProvi timer = new Timer(true); httpProvider = new HTTPMetadataProvider(timer, httpClient, metadataURL); - httpProvider.setParserPool(AbstractSAMLEngine.getNewBasicSecuredParserPool()); + httpProvider.setParserPool(AbstractProtocolEngine.getSecuredParserPool()); httpProvider.setRequireValidMetadata(true); httpProvider.setMinRefreshDelay(1000*60*15); //15 minutes httpProvider.setMaxRefreshDelay(1000*60*60*24); //24 hours diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASMetadataProviderDecorator.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASMetadataProviderDecorator.java index 7537c4d84..c5e56502b 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASMetadataProviderDecorator.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASMetadataProviderDecorator.java @@ -31,15 +31,17 @@ import org.opensaml.saml2.metadata.SPSSODescriptor; import org.opensaml.saml2.metadata.provider.MetadataProvider; import org.opensaml.saml2.metadata.provider.MetadataProviderException; -import eu.eidas.auth.engine.EIDASSAMLEngine; -import eu.eidas.auth.engine.metadata.MetadataProcessorI; +import eu.eidas.auth.engine.ProtocolEngineI; +import eu.eidas.auth.engine.metadata.MetadataFetcherI; +import eu.eidas.auth.engine.metadata.MetadataSignerI; +import eu.eidas.engine.exceptions.EIDASSAMLEngineException; import eu.eidas.engine.exceptions.SAMLEngineException; /** * @author tlenz * */ -public class MOAeIDASMetadataProviderDecorator implements MetadataProcessorI { +public class MOAeIDASMetadataProviderDecorator implements MetadataFetcherI { private MetadataProvider metadataprovider = null; @@ -51,10 +53,31 @@ public class MOAeIDASMetadataProviderDecorator implements MetadataProcessorI { } + /* (non-Javadoc) - * @see eu.eidas.auth.engine.metadata.MetadataProcessorI#getEntityDescriptor(java.lang.String) + * @see eu.eidas.auth.engine.metadata.MetadataFetcherI#getEntityDescriptor(java.lang.String, eu.eidas.auth.engine.metadata.MetadataSignerI) */ @Override + public EntityDescriptor getEntityDescriptor(String url, MetadataSignerI paramMetadataSignerI) + throws EIDASSAMLEngineException { + try { + /*TODO: maybe implement metadata signature validation on every request, + * but it is not needed in case of cached metadata provider, + * because signature must be only validated in case of cache reload operation + */ + return this.metadataprovider.getEntityDescriptor(url); + + } catch (MetadataProviderException e) { + throw new EIDASSAMLEngineException("eIDAS Metadata processing FAILED.", e); + + } + } + + + /* (non-Javadoc) + * @see eu.eidas.auth.engine.metadata.MetadataProcessorI#getEntityDescriptor(java.lang.String) + */ + @Deprecated public EntityDescriptor getEntityDescriptor(String url) throws SAMLEngineException { try { @@ -69,7 +92,7 @@ public class MOAeIDASMetadataProviderDecorator implements MetadataProcessorI { /* (non-Javadoc) * @see eu.eidas.auth.engine.metadata.MetadataProcessorI#getSPSSODescriptor(java.lang.String) */ - @Override + @Deprecated public SPSSODescriptor getSPSSODescriptor(String url) throws SAMLEngineException { return getFirstRoleDescriptor(getEntityDescriptor(url), SPSSODescriptor.class); @@ -79,7 +102,7 @@ public class MOAeIDASMetadataProviderDecorator implements MetadataProcessorI { /* (non-Javadoc) * @see eu.eidas.auth.engine.metadata.MetadataProcessorI#getIDPSSODescriptor(java.lang.String) */ - @Override + @Deprecated public IDPSSODescriptor getIDPSSODescriptor(String url) throws SAMLEngineException { return getFirstRoleDescriptor(getEntityDescriptor(url), IDPSSODescriptor.class); @@ -89,8 +112,8 @@ public class MOAeIDASMetadataProviderDecorator implements MetadataProcessorI { /* (non-Javadoc) * @see eu.eidas.auth.engine.metadata.MetadataProcessorI#checkValidMetadataSignature(java.lang.String, eu.eidas.auth.engine.EIDASSAMLEngine) */ - @Override - public void checkValidMetadataSignature(String url, EIDASSAMLEngine engine) + @Deprecated + public void checkValidMetadataSignature(String url, ProtocolEngineI engine) throws SAMLEngineException { //Do nothing, because metadata signature is already validated during //metadata provider initialization @@ -102,7 +125,7 @@ public class MOAeIDASMetadataProviderDecorator implements MetadataProcessorI { /* (non-Javadoc) * @see eu.eidas.auth.engine.metadata.MetadataProcessorI#checkValidMetadataSignature(java.lang.String, java.security.KeyStore) */ - @Override + @Deprecated public void checkValidMetadataSignature(String url, KeyStore trustStore) throws SAMLEngineException { //Do nothing, because metadata signature is already validated during @@ -110,6 +133,7 @@ public class MOAeIDASMetadataProviderDecorator implements MetadataProcessorI { } + @Deprecated protected <T extends RoleDescriptor> T getFirstRoleDescriptor(EntityDescriptor entityDescriptor, final Class<T> clazz){ for(RoleDescriptor rd:entityDescriptor.getRoleDescriptors()){ if(clazz.isInstance(rd)){ @@ -119,4 +143,6 @@ public class MOAeIDASMetadataProviderDecorator implements MetadataProcessorI { return null; } + + } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASAuthnRequestProcessingException.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASAuthnRequestProcessingException.java index c96af37ef..d51629d9e 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASAuthnRequestProcessingException.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASAuthnRequestProcessingException.java @@ -30,7 +30,7 @@ import at.gv.egovernment.moa.util.MiscUtil; * @author tlenz * */ -public class eIDASAuthnRequestProcessingException extends eIDASException { +public class EIDASAuthnRequestProcessingException extends EIDASException { private String subStatusCode = null; @@ -43,20 +43,20 @@ public class eIDASAuthnRequestProcessingException extends eIDASException { * @param messageId * @param parameters */ - public eIDASAuthnRequestProcessingException(String messageId, Object[] parameters) { + public EIDASAuthnRequestProcessingException(String messageId, Object[] parameters) { super(messageId, parameters); } - public eIDASAuthnRequestProcessingException(String subStatusCode, String messageId, Object[] parameters) { + public EIDASAuthnRequestProcessingException(String subStatusCode, String messageId, Object[] parameters) { super(messageId, parameters); this.subStatusCode = subStatusCode; } - public eIDASAuthnRequestProcessingException(String messageId, Object[] parameters, Throwable e) { + public EIDASAuthnRequestProcessingException(String messageId, Object[] parameters, Throwable e) { super(messageId, parameters, e ); } - public eIDASAuthnRequestProcessingException(String subStatusCode, String messageId, Object[] parameters, Throwable e) { + public EIDASAuthnRequestProcessingException(String subStatusCode, String messageId, Object[] parameters, Throwable e) { super(messageId, parameters, e ); this.subStatusCode = subStatusCode; } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASAuthnRequestValidationException.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASAuthnRequestValidationException.java index 2a15ee18a..a6da769b7 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASAuthnRequestValidationException.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASAuthnRequestValidationException.java @@ -28,7 +28,7 @@ import org.opensaml.saml2.core.StatusCode; * @author tlenz * */ -public class eIDASAuthnRequestValidationException extends eIDASException { +public class EIDASAuthnRequestValidationException extends EIDASException { /** * @@ -39,7 +39,7 @@ public class eIDASAuthnRequestValidationException extends eIDASException { * @param messageId * @param parameters */ - public eIDASAuthnRequestValidationException(String messageId, Object[] parameters) { + public EIDASAuthnRequestValidationException(String messageId, Object[] parameters) { super(messageId, parameters); } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASEngineException.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASEngineException.java index 234c4e038..8bf7f7452 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASEngineException.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASEngineException.java @@ -28,7 +28,7 @@ import org.opensaml.saml2.core.StatusCode; * @author tlenz * */ -public class EIDASEngineException extends eIDASException { +public class EIDASEngineException extends EIDASException { /** * @param objects diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASException.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASException.java index f42004abc..e3d6c5a2e 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASException.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASException.java @@ -28,7 +28,7 @@ import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; * @author tlenz * */ -public abstract class eIDASException extends MOAIDException { +public abstract class EIDASException extends MOAIDException { /** * @@ -44,7 +44,7 @@ public abstract class eIDASException extends MOAIDException { * @param messageId * @param parameters */ - public eIDASException(String messageId, Object[] parameters) { + public EIDASException(String messageId, Object[] parameters) { super(messageId, parameters); } @@ -52,7 +52,7 @@ public abstract class eIDASException extends MOAIDException { * @param messageId * @param parameters */ - public eIDASException(String messageId, Object[] parameters, Throwable e) { + public EIDASException(String messageId, Object[] parameters, Throwable e) { super(messageId, parameters, e); } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASResponseBuildException.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASResponseBuildException.java index 0ffcf11ef..5e6b87b39 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASResponseBuildException.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASResponseBuildException.java @@ -28,7 +28,7 @@ import org.opensaml.saml2.core.StatusCode; * @author tlenz * */ -public class eIDASResponseBuildException extends eIDASException { +public class EIDASResponseBuildException extends EIDASException { /** * @@ -39,11 +39,11 @@ public class eIDASResponseBuildException extends eIDASException { * @param messageId * @param parameters */ - public eIDASResponseBuildException(String messageId, Object[] parameters) { + public EIDASResponseBuildException(String messageId, Object[] parameters) { super(messageId, parameters); } - public eIDASResponseBuildException(String messageId, Object[] parameters, Throwable e) { + public EIDASResponseBuildException(String messageId, Object[] parameters, Throwable e) { super(messageId, parameters, e); } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASResponseNotSuccessException.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASResponseNotSuccessException.java index d10ca1c88..460561eb3 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASResponseNotSuccessException.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASResponseNotSuccessException.java @@ -28,14 +28,14 @@ import org.opensaml.saml2.core.StatusCode; * @author tlenz * */ -public class eIDASResponseNotSuccessException extends eIDASException { +public class EIDASResponseNotSuccessException extends EIDASException { /** * */ private static final long serialVersionUID = 6145402939313568907L; - public eIDASResponseNotSuccessException(String messageId, Object[] parameters) { + public EIDASResponseNotSuccessException(String messageId, Object[] parameters) { super(messageId, parameters); } @@ -44,7 +44,7 @@ public class eIDASResponseNotSuccessException extends eIDASException { * @param parameters * @param e */ - public eIDASResponseNotSuccessException(String messageId, Object[] parameters, Throwable e) { + public EIDASResponseNotSuccessException(String messageId, Object[] parameters, Throwable e) { super(messageId, parameters, e); } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASAttributeException.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASAttributeException.java index b25895eca..17f0a9b72 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASAttributeException.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASAttributeException.java @@ -28,7 +28,7 @@ import org.opensaml.saml2.core.StatusCode; * @author tlenz * */ -public class eIDASAttributeException extends eIDASException { +public class eIDASAttributeException extends EIDASException { private static final long serialVersionUID = 1L; diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/CreateIdentityLinkTask.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/CreateIdentityLinkTask.java index a56e6c3cd..6be64ba72 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/CreateIdentityLinkTask.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/CreateIdentityLinkTask.java @@ -28,6 +28,7 @@ import java.text.SimpleDateFormat; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.joda.time.DateTime; import org.springframework.stereotype.Component; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -39,6 +40,7 @@ import at.gv.egovernment.moa.id.auth.modules.AbstractAuthServletTask; import at.gv.egovernment.moa.id.auth.modules.TaskExecutionException; import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.eIDASAttributeException; +import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils; import at.gv.egovernment.moa.id.auth.parser.IdentityLinkAssertionParser; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.commons.db.ex.MOADatabaseException; @@ -47,7 +49,7 @@ import at.gv.egovernment.moa.id.util.IdentityLinkReSigner; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.DOMUtils; import at.gv.egovernment.moa.util.XPathUtils; -import eu.eidas.auth.commons.IPersonalAttributeList; +import eu.eidas.auth.commons.attribute.ImmutableAttributeMap; /** * @author tlenz @@ -67,9 +69,9 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask { defaultTaskInitialization(request, executionContext); //get eIDAS attributes from MOA-Session - IPersonalAttributeList eIDASAttributes = moasession.getGenericDataFromSession( + ImmutableAttributeMap eIDASAttributes = moasession.getGenericDataFromSession( AuthenticationSessionStorageConstants.eIDAS_ATTRIBUTELIST, - IPersonalAttributeList.class); + ImmutableAttributeMap.class); IdentityLink identityLink = null; @@ -86,13 +88,17 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask { // replace data Element idlassertion = identityLink.getSamlAssertion(); - + // - set fake baseID; Node prIdentification = XPathUtils.selectSingleNode(idlassertion, IdentityLinkAssertionParser.PERSON_IDENT_VALUE_XPATH); - if(!eIDASAttributes.containsKey(Constants.eIDAS_ATTR_PERSONALIDENTIFIER)) - throw new eIDASAttributeException(Constants.eIDAS_ATTR_PERSONALIDENTIFIER); - String eIdentifier = eIDASAttributes.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER).getValue().get(0); - prIdentification.getFirstChild().setNodeValue(eIdentifier); + + + Object eIdentifier = eIDASAttributes.getFirstValue( + SAMLEngineUtils.getMapOfAllAvailableAttributes().get( + Constants.eIDAS_ATTR_PERSONALIDENTIFIER)); + if (eIdentifier == null || !(eIdentifier instanceof String)) + throw new eIDASAttributeException(Constants.eIDAS_ATTR_PERSONALIDENTIFIER); + prIdentification.getFirstChild().setNodeValue((String) eIdentifier); //build personal identifier which looks like a baseID // String fakeBaseID = new BPKBuilder().buildBPK(eIdentifier, "baseID"); @@ -100,26 +106,33 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask { // prIdentification.getFirstChild().setNodeValue(fakeBaseID); // - set last name - Node prFamilyName = XPathUtils.selectSingleNode(idlassertion, IdentityLinkAssertionParser.PERSON_FAMILY_NAME_XPATH); - if(!eIDASAttributes.containsKey(Constants.eIDAS_ATTR_CURRENTFAMILYNAME)) + Node prFamilyName = XPathUtils.selectSingleNode(idlassertion, IdentityLinkAssertionParser.PERSON_FAMILY_NAME_XPATH); + Object familyName = eIDASAttributes.getFirstValue( + SAMLEngineUtils.getMapOfAllAvailableAttributes().get( + Constants.eIDAS_ATTR_CURRENTFAMILYNAME)); + if (familyName == null || !(familyName instanceof String)) throw new eIDASAttributeException(Constants.eIDAS_ATTR_CURRENTFAMILYNAME); - String familyName = eIDASAttributes.get(Constants.eIDAS_ATTR_CURRENTFAMILYNAME).getValue().get(0); - prFamilyName.getFirstChild().setNodeValue(familyName); + prFamilyName.getFirstChild().setNodeValue((String) familyName); // - set first name Node prGivenName = XPathUtils.selectSingleNode(idlassertion, IdentityLinkAssertionParser.PERSON_GIVEN_NAME_XPATH); - if(!eIDASAttributes.containsKey(Constants.eIDAS_ATTR_CURRENTGIVENNAME)) + Object givenName = eIDASAttributes.getFirstValue( + SAMLEngineUtils.getMapOfAllAvailableAttributes().get( + Constants.eIDAS_ATTR_CURRENTGIVENNAME)); + if (givenName == null || !(givenName instanceof String)) throw new eIDASAttributeException(Constants.eIDAS_ATTR_CURRENTGIVENNAME); - String givenName = eIDASAttributes.get(Constants.eIDAS_ATTR_CURRENTGIVENNAME).getValue().get(0); - prGivenName.getFirstChild().setNodeValue(givenName); + prGivenName.getFirstChild().setNodeValue((String) givenName); // - set date of birth - Node prDateOfBirth = XPathUtils.selectSingleNode(idlassertion, IdentityLinkAssertionParser.PERSON_DATE_OF_BIRTH_XPATH); - if(!eIDASAttributes.containsKey(Constants.eIDAS_ATTR_DATEOFBIRTH)) + Node prDateOfBirth = XPathUtils.selectSingleNode(idlassertion, IdentityLinkAssertionParser.PERSON_DATE_OF_BIRTH_XPATH); + Object dateOfBirth = eIDASAttributes.getFirstValue( + SAMLEngineUtils.getMapOfAllAvailableAttributes().get( + Constants.eIDAS_ATTR_DATEOFBIRTH)); + if (dateOfBirth == null || !(dateOfBirth instanceof DateTime)) throw new eIDASAttributeException(Constants.eIDAS_ATTR_DATEOFBIRTH); - String dateOfBirth = eIDASAttributes.get(Constants.eIDAS_ATTR_DATEOFBIRTH).getValue().get(0); - dateOfBirth = new SimpleDateFormat("yyyy-MM-dd").format(new SimpleDateFormat("yyyyMMdd").parse(dateOfBirth)); - prDateOfBirth.getFirstChild().setNodeValue(dateOfBirth); + + String formatedDateOfBirth = new SimpleDateFormat("yyyy-MM-dd").format(((DateTime)dateOfBirth).toDate()); + prDateOfBirth.getFirstChild().setNodeValue(formatedDateOfBirth); identityLink = new IdentityLinkAssertionParser(idlassertion).parseIdentityLink(); diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java index 2f10df540..ee71e8e6b 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java @@ -23,7 +23,9 @@ package at.gv.egovernment.moa.id.auth.modules.eidas.tasks; import java.io.StringWriter; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -33,6 +35,10 @@ import org.apache.commons.lang3.StringUtils; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; +import org.opensaml.common.xml.SAMLConstants; +import org.opensaml.saml2.metadata.EntityDescriptor; +import org.opensaml.saml2.metadata.SingleSignOnService; +import org.opensaml.saml2.metadata.provider.MetadataProviderException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -54,15 +60,17 @@ import at.gv.egovernment.moa.id.commons.api.data.StorkAttribute; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.process.api.ExecutionContext; import at.gv.egovernment.moa.logging.Logger; -import eu.eidas.auth.commons.EIDASAuthnRequest; -import eu.eidas.auth.commons.EIDASUtil; -import eu.eidas.auth.commons.EidasLoaCompareType; -import eu.eidas.auth.commons.EidasLoaLevels; -import eu.eidas.auth.commons.IPersonalAttributeList; -import eu.eidas.auth.commons.PersonalAttribute; -import eu.eidas.auth.commons.PersonalAttributeList; -import eu.eidas.auth.engine.EIDASSAMLEngine; -import eu.eidas.auth.engine.core.eidas.SPType; +import at.gv.egovernment.moa.util.MiscUtil; +import eu.eidas.auth.commons.EidasStringUtil; +import eu.eidas.auth.commons.attribute.AttributeDefinition; +import eu.eidas.auth.commons.attribute.AttributeDefinition.Builder; +import eu.eidas.auth.commons.attribute.ImmutableAttributeMap; +import eu.eidas.auth.commons.protocol.IRequestMessage; +import eu.eidas.auth.commons.protocol.eidas.LevelOfAssurance; +import eu.eidas.auth.commons.protocol.eidas.LevelOfAssuranceComparison; +import eu.eidas.auth.commons.protocol.eidas.SpType; +import eu.eidas.auth.commons.protocol.eidas.impl.EidasAuthenticationRequest; +import eu.eidas.auth.engine.ProtocolEngineI; import eu.eidas.engine.exceptions.EIDASSAMLEngineException; /** @@ -100,9 +108,42 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { throw new AuthenticationException("eIDAS.04", new Object[] {citizenCountryCode}); } Logger.debug("Found eIDaS Node/C-PEPS configuration for citizen of country: " + citizenCountryCode); - String destination = cpeps.getPepsURL().toString().split(";")[1].trim(); // FIXME convenience for metadata url and assertion destination + String destination = null; String metadataUrl = cpeps.getPepsURL().toString().split(";")[0].trim(); + try { + EntityDescriptor eIDASNodeMetadata = eIDASMetadataProvider.getEntityDescriptor(metadataUrl); + if (eIDASNodeMetadata != null) { + SingleSignOnService ssoDescr = selectSingleSignOnServiceFromMetadata(eIDASNodeMetadata); + if (ssoDescr != null) { + destination = ssoDescr.getLocation(); + Logger.debug("Use destination URL:" + destination + " from eIDAS metadata:" + metadataUrl); + + } else + Logger.warn("eIDAS metadata for node:" + metadataUrl + " has no IDPSSODescriptor or no SingleSignOnService information."); + + } else + Logger.warn("No eIDAS metadata for node:" + metadataUrl + " "); + + } catch (MetadataProviderException e) { + Logger.warn("Load eIDAS metadata from node:" + metadataUrl + " FAILED with an error.", e); + + } + + + if (MiscUtil.isEmpty(destination)) { + destination = cpeps.getPepsURL().toString().split(";")[1].trim(); // FIXME convenience for metadata url and assertion destination + + if (MiscUtil.isNotEmpty(destination)) + Logger.debug("Use eIDAS node destination URL:" + destination + " from configuration"); + + else { + Logger.error("No eIDAS-node destination URL FOUND. Request eIDAS node not possible."); + throw new MOAIDException("eIDAS.02", new Object[]{"No eIDAS-node Destination-URL FOUND"}); + + } + + } //TODO: switch to entityID revisionsLogger.logEvent(oaConfig, pendingReq, @@ -113,50 +154,69 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { Collection<StorkAttribute> attributesFromConfig = oaConfig.getRequestedSTORKAttributes(); // - prepare attribute list - IPersonalAttributeList pAttList = new PersonalAttributeList(); - + ProtocolEngineI engine = SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); + // - fill container - for (StorkAttribute current : attributesFromConfig) { - PersonalAttribute newAttribute = new PersonalAttribute(); - newAttribute.setName(current.getName()); - - boolean globallyMandatory = false; - for (StorkAttribute currentGlobalAttribute : authConfig.getStorkConfig().getStorkAttributes()) - if (current.getName().equals(currentGlobalAttribute.getName())) { - globallyMandatory = BooleanUtils.isTrue(currentGlobalAttribute.getMandatory()); - break; - } - - newAttribute.setIsRequired(current.getMandatory() || globallyMandatory); - pAttList.add(newAttribute); + List<AttributeDefinition<?>> reqAttrList = new ArrayList<AttributeDefinition<?>>(); + for (StorkAttribute current : attributesFromConfig) { + AttributeDefinition<?> newAttribute = SAMLEngineUtils.getMapOfAllAvailableAttributes().get(current.getName()); + + if (newAttribute == null) { + Logger.warn("eIDAS attribute with friendlyName:" + current.getName() + " is not supported."); + + } else { + boolean globallyMandatory = false; + for (StorkAttribute currentGlobalAttribute : authConfig.getStorkConfig().getStorkAttributes()) + if (current.getName().equals(currentGlobalAttribute.getName())) { + globallyMandatory = BooleanUtils.isTrue(currentGlobalAttribute.getMandatory()); + break; + } + + Builder<?> attrBuilder = AttributeDefinition.builder(newAttribute).required(current.getMandatory() || globallyMandatory); + reqAttrList.add(attrBuilder.build()); + + } } - - EIDASSAMLEngine engine = SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); - - //build eIDAS AuthnRequest - EIDASAuthnRequest authnRequest = new EIDASAuthnRequest(); - authnRequest.setProviderName(pendingReq.getAuthURL()); - authnRequest.setPersonalAttributeList(pAttList); - authnRequest.setIssuer(pendingReq.getAuthURL() + Constants.eIDAS_HTTP_ENDPOINT_METADATA); + //build requested attribute set + ImmutableAttributeMap reqAttrMap = new ImmutableAttributeMap.Builder().putAll(reqAttrList).build(); - authnRequest.setDestination(destination); - authnRequest.setEidasNameidFormat(EIDASAuthnRequest.NAMEID_FORMAT_UNSPECIFIED); - authnRequest.setEidasLoA(EidasLoaLevels.LOW.stringValue()); - authnRequest.setEidasLoACompareType(EidasLoaCompareType.MINIMUM.stringValue()); + //build eIDAS AuthnRequest + EidasAuthenticationRequest.Builder authnRequestBuilder = new EidasAuthenticationRequest.Builder(); + + authnRequestBuilder.id(eu.eidas.auth.engine.xml.opensaml.SAMLEngineUtils.generateNCName()); + authnRequestBuilder.providerName(pendingReq.getAuthURL()); + String issur = pendingReq.getAuthURL() + Constants.eIDAS_HTTP_ENDPOINT_METADATA; + authnRequestBuilder.issuer(issur); + authnRequestBuilder.destination(destination); + + authnRequestBuilder.levelOfAssuranceComparison(LevelOfAssuranceComparison.MINIMUM); + authnRequestBuilder.nameIdFormat(Constants.eIDAS_REQ_NAMEID_FORMAT); + //TODO: load from OA-Configuration + authnRequestBuilder.levelOfAssurance(LevelOfAssurance.LOW); //set correct SPType for this online application if (oaConfig.getBusinessService()) - authnRequest.setSPType("private"); + authnRequestBuilder.spType(SpType.PRIVATE); else - authnRequest.setSPType(SPType.DEFAULT_VALUE); - - engine.initRequestedAttributes(pAttList); - authnRequest = engine.generateEIDASAuthnRequest(authnRequest); + authnRequestBuilder.spType(SpType.PUBLIC); + + + //TODO: make it loadable from config + authnRequestBuilder.serviceProviderCountryCode("AT"); + + //set citizen country code for foreign uses + authnRequestBuilder.citizenCountryCode(cpeps.getCountryCode()); + + //add requested attributes + authnRequestBuilder.requestedAttributes(reqAttrMap); + + + IRequestMessage authnRequest = engine.generateRequestMessage(authnRequestBuilder.build(), issur); //encode AuthnRequest - byte[] token = authnRequest.getTokenSaml(); - String SAMLRequest = EIDASUtil.encodeSAMLToken(token); + byte[] token = authnRequest.getMessageBytes(); + String SAMLRequest = EidasStringUtil.encodeToBase64(token); //send @@ -188,7 +248,7 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { revisionsLogger.logEvent(oaConfig, pendingReq, MOAIDEventConstants.AUTHPROCESS_PEPS_REQUESTED, - authnRequest.getSamlId()); + authnRequest.getRequest().getId()); } catch (Exception e) { Logger.error("Velocity general error: " + e.getMessage()); @@ -210,4 +270,28 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { } } + private SingleSignOnService selectSingleSignOnServiceFromMetadata(EntityDescriptor idpEntity) { + //select SingleSignOn Service endpoint from IDP metadata + SingleSignOnService endpoint = null; + if (idpEntity.getIDPSSODescriptor(SAMLConstants.SAML20P_NS) == null) { + return null; + + } + + for (SingleSignOnService sss : + idpEntity.getIDPSSODescriptor(SAMLConstants.SAML20P_NS).getSingleSignOnServices()) { + + // use POST binding as default if it exists + if (sss.getBinding().equals(SAMLConstants.SAML2_POST_BINDING_URI)) + endpoint = sss; + +// else if ( sss.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI) +// && endpoint == null ) +// endpoint = sss; + + } + + return endpoint; + } + } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/ReceiveAuthnResponseTask.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/ReceiveAuthnResponseTask.java index daa4d8b02..fd7a19be8 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/ReceiveAuthnResponseTask.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/ReceiveAuthnResponseTask.java @@ -14,8 +14,7 @@ import at.gv.egovernment.moa.id.auth.modules.TaskExecutionException; import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; import at.gv.egovernment.moa.id.auth.modules.eidas.engine.MOAeIDASChainingMetadataProvider; import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.EIDASEngineException; -import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.eIDASResponseNotSuccessException; -import at.gv.egovernment.moa.id.auth.modules.eidas.utils.MOAPersonalAttributeList; +import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.EIDASResponseNotSuccessException; import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.commons.db.ex.MOADatabaseException; @@ -23,9 +22,9 @@ import at.gv.egovernment.moa.id.process.api.ExecutionContext; import at.gv.egovernment.moa.id.protocols.pvp2x.PVPConstants; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.MiscUtil; -import eu.eidas.auth.commons.EIDASAuthnResponse; -import eu.eidas.auth.commons.EIDASUtil; -import eu.eidas.auth.engine.EIDASSAMLEngine; +import eu.eidas.auth.commons.EidasStringUtil; +import eu.eidas.auth.commons.protocol.IAuthenticationResponse; +import eu.eidas.auth.engine.ProtocolEngineI; import eu.eidas.engine.exceptions.EIDASSAMLEngineException; @Component("ReceiveAuthnResponseTask") @@ -49,27 +48,29 @@ public class ReceiveAuthnResponseTask extends AbstractAuthServletTask { defaultTaskInitialization(request, executionContext); //decode SAML response - byte[] decSamlToken = EIDASUtil.decodeSAMLToken(base64SamlToken); + byte[] decSamlToken = EidasStringUtil.decodeBytesFromBase64(base64SamlToken); //get eIDAS SAML-engine - EIDASSAMLEngine engine = SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); - + ProtocolEngineI engine = SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); + //validate SAML token - EIDASAuthnResponse samlResp = engine.validateEIDASAuthnResponse(decSamlToken, - request.getRemoteHost(), Constants.CONFIG_PROPS_SKEWTIME); - - boolean encryptedResponse=engine.isEncryptedSamlResponse(decSamlToken); - if (encryptedResponse) { + IAuthenticationResponse samlResp = engine.unmarshallResponseAndValidate(decSamlToken, + request.getRemoteHost(), + Constants.CONFIG_PROPS_SKEWTIME, + pendingReq.getAuthURL() + Constants.eIDAS_HTTP_ENDPOINT_METADATA); + + if (samlResp.isEncrypted()) { Logger.info("Received encrypted eIDAS SAML-Response."); //TODO: check if additional decryption operation is required } + //check response StatusCode if (!samlResp.getStatusCode().equals(StatusCode.SUCCESS_URI)) { Logger.info("Receice eIDAS Response with StatusCode:" + samlResp.getStatusCode() - + " Subcode:" + samlResp.getSubStatusCode() + " Msg:" + samlResp.getMessage()); - throw new eIDASResponseNotSuccessException("eIDAS.11", new Object[]{samlResp.getMessage()}); + + " Subcode:" + samlResp.getSubStatusCode() + " Msg:" + samlResp.getStatusMessage()); + throw new EIDASResponseNotSuccessException("eIDAS.11", new Object[]{samlResp.getStatusMessage()}); } @@ -78,11 +79,12 @@ public class ReceiveAuthnResponseTask extends AbstractAuthServletTask { //update MOA-Session data with received information Logger.debug("Store eIDAS response information into MOA-session."); - moasession.setQAALevel(samlResp.getAssuranceLevel()); + + moasession.setQAALevel(samlResp.getLevelOfAssurance()); moasession.setGenericDataToSession( AuthenticationSessionStorageConstants.eIDAS_ATTRIBUTELIST, - new MOAPersonalAttributeList(samlResp.getPersonalAttributeList())); + samlResp.getAttributes()); moasession.setGenericDataToSession( AuthenticationSessionStorageConstants.eIDAS_RESPONSE, @@ -96,7 +98,7 @@ public class ReceiveAuthnResponseTask extends AbstractAuthServletTask { revisionsLogger.logEvent(pendingReq.getOnlineApplicationConfiguration(), pendingReq, MOAIDEventConstants.AUTHPROCESS_PEPS_RECEIVED, - samlResp.getSamlId()); + samlResp.getId()); }catch (EIDASSAMLEngineException e) { Logger.error("eIDAS AuthnRequest generation FAILED.", e); diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAOrderedAttributeIterator.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAOrderedAttributeIterator.java deleted file mode 100644 index 573163af0..000000000 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAOrderedAttributeIterator.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2014 Federal Chancellery Austria - * MOA-ID has been developed in a cooperation between BRZ, the Federal - * Chancellery Austria - ICT staff unit, and Graz University of Technology. - * - * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * http://www.osor.eu/eupl/ - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. - */ -package at.gv.egovernment.moa.id.auth.modules.eidas.utils; - -import java.util.Iterator; -import java.util.NoSuchElementException; - -import at.gv.egovernment.moa.logging.Logger; - -import eu.eidas.auth.commons.PersonalAttribute; -import eu.eidas.auth.commons.PersonalAttributeList; - - -/** - * @author tlenz - * - */ -public class MOAOrderedAttributeIterator implements Iterator<PersonalAttribute> { - - private MOAPersonalAttributeList pal; - private Iterator<String> keyIterator; - - public MOAOrderedAttributeIterator(MOAPersonalAttributeList palArg) { - this.pal = palArg; - keyIterator = palArg.getInsertOrder().iterator(); - } - - @Override - public boolean hasNext() { - return keyIterator.hasNext(); - } - - @Override - public PersonalAttribute next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - return pal.get(keyIterator.next()); - } - - @Override - public void remove() { - Logger.error("Not implemented"); - } - -} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAPersonalAttributeList.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAPersonalAttributeList.java index 5cc100b70..d559cdba7 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAPersonalAttributeList.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAPersonalAttributeList.java @@ -1,343 +1,343 @@ -/* - * Copyright 2014 Federal Chancellery Austria - * MOA-ID has been developed in a cooperation between BRZ, the Federal - * Chancellery Austria - ICT staff unit, and Graz University of Technology. - * - * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * http://www.osor.eu/eupl/ - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. - */ -package at.gv.egovernment.moa.id.auth.modules.eidas.utils; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.StringTokenizer; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.commons.lang.StringUtils; - -import at.gv.egovernment.moa.logging.Logger; - -import eu.eidas.auth.commons.AttributeConstants; -import eu.eidas.auth.commons.AttributeUtil; -import eu.eidas.auth.commons.EIDASErrors; -import eu.eidas.auth.commons.EIDASParameters; -import eu.eidas.auth.commons.EIDASUtil; -import eu.eidas.auth.commons.EIDASValues; -import eu.eidas.auth.commons.IPersonalAttributeList; -import eu.eidas.auth.commons.PersonalAttribute; -import eu.eidas.auth.commons.exceptions.InternalErrorEIDASException; - -/** - * @author tlenz - * - */ -public final class MOAPersonalAttributeList extends - ConcurrentHashMap<String, PersonalAttribute> implements IPersonalAttributeList { - - /** - * - */ - private static final long serialVersionUID = -4488124133022713089L; - - public MOAPersonalAttributeList(IPersonalAttributeList eIDASAttributeList) { - super(); - Iterator<PersonalAttribute> element = eIDASAttributeList.iterator(); - while(element.hasNext()) - add(element.next()); - - } - - /** - * Hash with the latest fetched attribute name alias. - */ - private Map<String, Integer> latestAttrAlias = - new HashMap<String, Integer>(); - - /** - * Hash with mapping number of alias or the attribute name. - */ - private Map<String, Integer> attrAliasNumber = - new HashMap<String, Integer>(); - private List<String> insertOrder = new ArrayList<String>(); - - /** - * Obtain the insertOrder Collection - * - * @return defensive copy of the collection - */ - List<String> getInsertOrder() { - return Collections.unmodifiableList(this.insertOrder); - } - - /** - * Default constructor. - */ - public MOAPersonalAttributeList() { - super(); - - } - - /** - * Constructor with initial capacity for the PersonalAttributeList size. - * - * @param capacity The initial capacity for the PersonalAttributeList. - */ - public MOAPersonalAttributeList(final int capacity) { - super(capacity); - } - - /** - * {@inheritDoc} - */ - public Iterator<PersonalAttribute> iterator() { - return new MOAOrderedAttributeIterator(this); - } - - /** - * {@inheritDoc} - */ - public PersonalAttribute get(final Object key) { - String attrName = (String) key; - - if (this.latestAttrAlias == null) - this.latestAttrAlias = new HashMap<String, Integer>(); - - if (this.attrAliasNumber == null) - this.attrAliasNumber = new HashMap<String, Integer>(); - - if (this.latestAttrAlias.containsKey(key)) { - attrName = attrName + this.latestAttrAlias.get(key); - } else { - if (this.attrAliasNumber.containsKey(key)) { - this.latestAttrAlias.put(attrName, this.attrAliasNumber.get(key)); - } - } - return super.get(attrName); - } - - /** - * {@inheritDoc} - */ - public void add(final PersonalAttribute value) { - if (value != null) { - this.put(value.getName(), value); - } - } - - /** - * {@inheritDoc} - */ - public PersonalAttribute put(final String key, final PersonalAttribute val) { - if (StringUtils.isNotEmpty(key) && val != null) { - // Validate if attribute name already exists! - String attrAlias = key; - if (this.containsKey(attrAlias)) { - if (this.attrAliasNumber == null) - this.attrAliasNumber = new HashMap<String, Integer>(); - if (!val.isEmptyValue() && StringUtils.isNumeric(val.getValue().get(0))) { - final String attrValue = val.getValue().get(0); - attrAlias = key + attrValue; - this.attrAliasNumber.put(key, Integer.valueOf(attrValue)); - } else { - final PersonalAttribute attr = super.get(key); - if (!attr.isEmptyValue() - && StringUtils.isNumeric(attr.getValue().get(0))) { - attrAlias = key + attr.getValue().get(0); - super.put(key, (PersonalAttribute) attr); - this.attrAliasNumber.put(key, null); - } - } - } else { - if (insertOrder == null) - insertOrder = new ArrayList<String>(); - - insertOrder.add(key); - } - return super.put(attrAlias, val); - } else { - return null; - } - } - - @Override - public PersonalAttribute remove(Object key) { - insertOrder.remove(key); - return super.remove(key); - } - - /** - * {@inheritDoc} - */ - public void populate(final String attrList) { - final StringTokenizer strToken = - new StringTokenizer(attrList, EIDASValues.ATTRIBUTE_SEP.toString()); - - while (strToken.hasMoreTokens()) { - final PersonalAttribute persAttr = new PersonalAttribute(); - String[] tuples = - strToken.nextToken().split(EIDASValues.ATTRIBUTE_TUPLE_SEP.toString(), - AttributeConstants.NUMBER_TUPLES.intValue()); - - // Convert to the new format if needed! - tuples = convertFormat(tuples); - - if (AttributeUtil.hasValidTuples(tuples)) { - final int attrValueIndex = - AttributeConstants.ATTR_VALUE_INDEX.intValue(); - final String tmpAttrValue = - tuples[attrValueIndex].substring(1, - tuples[attrValueIndex].length() - 1); - final String[] vals = - tmpAttrValue.split(EIDASValues.ATTRIBUTE_VALUE_SEP.toString()); - - persAttr.setName(tuples[AttributeConstants.ATTR_NAME_INDEX.intValue()]); - persAttr.setIsRequired(Boolean - .valueOf(tuples[AttributeConstants.ATTR_TYPE_INDEX.intValue()])); - // check if it is a complex value - if (tuples[AttributeConstants.ATTR_NAME_INDEX.intValue()] - .equals(EIDASParameters.COMPLEX_ADDRESS_VALUE.toString())) { - persAttr.setComplexValue(createComplexValue(vals)); - } else { - persAttr.setValue(createValues(vals)); - } - - if (tuples.length == AttributeConstants.NUMBER_TUPLES.intValue()) { - persAttr.setStatus(tuples[AttributeConstants.ATTR_STATUS_INDEX - .intValue()]); - } - this.put(tuples[AttributeConstants.ATTR_NAME_INDEX.intValue()], - persAttr); - - } else { - Logger.info("BUSINESS EXCEPTION : Invalid personal attribute list tuples"); - } - - } - } - - /** - * Returns a copy of this <tt>IPersonalAttributeList</tt> instance. - * - * @return The copy of this IPersonalAttributeList. - */ - public Object clone() { - try { - MOAPersonalAttributeList theClone= (MOAPersonalAttributeList)super.clone(); - theClone.insertOrder=new ArrayList<String>(insertOrder); - return theClone; - - } catch (CloneNotSupportedException e) { - throw new InternalErrorEIDASException( - EIDASUtil.getConfig(EIDASErrors.INTERNAL_ERROR.errorCode()), - EIDASUtil.getConfig(EIDASErrors.INTERNAL_ERROR.errorMessage()), e); - } - } - - /** - * Creates a string in the following format. - * - * attrName:attrType:[attrValue1,attrValue2=attrComplexValue]:attrStatus; - * - * @return {@inheritDoc} - */ - @Override - public String toString() { - final StringBuilder strBuilder = new StringBuilder(); - final Iterator<String> iteratorInsertOrder = insertOrder.iterator(); - while (iteratorInsertOrder.hasNext()) { - String key = iteratorInsertOrder.next(); - final PersonalAttribute attr = get(key); - strBuilder.append(attr.toString()); - if (isNumberAlias(key)) { - strBuilder.append(get(key).toString()); - } - } - return strBuilder.toString(); - } - - /** - * Validates and creates the attribute's complex values. - * - * @param values The complex values. - * @return The {@link Map} with the complex values. - * @see Map - */ - private Map<String, String> createComplexValue(final String[] values) { - final Map<String, String> complexValue = new HashMap<String, String>(); - for (final String val : values) { - final String[] tVal = val.split("="); - if (StringUtils.isNotEmpty(val) && tVal.length == 2) { - complexValue.put(tVal[0], AttributeUtil.unescape(tVal[1])); - } - } - return complexValue; - } - - /** - * Validates and creates the attribute values. - * - * @param vals The attribute values. - * @return The {@link List} with the attribute values. - * @see List - */ - private List<String> createValues(final String[] vals) { - final List<String> values = new ArrayList<String>(); - for (final String val : vals) { - if (StringUtils.isNotEmpty(val)) { - values.add(AttributeUtil.unescape(val)); - } - } - return values; - } - - ////////////////// - /** - * Converts the attribute tuple (attrName:attrType...) to the new format. - * - * @param tuples The attribute tuples to convert. - * @return The attribute tuples in the new format. - */ - private String[] convertFormat(final String[] tuples) { - final String[] newFormatTuples = - new String[AttributeConstants.NUMBER_TUPLES.intValue()]; - if (tuples != null) { - System.arraycopy(tuples, 0, newFormatTuples, 0, tuples.length); - - for (int i = tuples.length; i < newFormatTuples.length; i++) { - if (i == AttributeConstants.ATTR_VALUE_INDEX.intValue()) { - newFormatTuples[i] = "[]"; - } else { - newFormatTuples[i] = ""; - } - } - } - return newFormatTuples; - } - - public boolean isNumberAlias(String key) { - return this.attrAliasNumber.containsKey(key); - } - - - -} +///* +// * Copyright 2014 Federal Chancellery Austria +// * MOA-ID has been developed in a cooperation between BRZ, the Federal +// * Chancellery Austria - ICT staff unit, and Graz University of Technology. +// * +// * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by +// * the European Commission - subsequent versions of the EUPL (the "Licence"); +// * You may not use this work except in compliance with the Licence. +// * You may obtain a copy of the Licence at: +// * http://www.osor.eu/eupl/ +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the Licence is distributed on an "AS IS" basis, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the Licence for the specific language governing permissions and +// * limitations under the Licence. +// * +// * This product combines work with different licenses. See the "NOTICE" text +// * file for details on the various modules and licenses. +// * The "NOTICE" text file is part of the distribution. Any derivative works +// * that you distribute must include a readable copy of the "NOTICE" text file. +// */ +//package at.gv.egovernment.moa.id.auth.modules.eidas.utils; +// +//import java.io.IOException; +//import java.io.ObjectInputStream; +//import java.util.ArrayList; +//import java.util.Collections; +//import java.util.HashMap; +//import java.util.Iterator; +//import java.util.List; +//import java.util.Map; +//import java.util.StringTokenizer; +//import java.util.concurrent.ConcurrentHashMap; +// +//import org.apache.commons.lang.StringUtils; +// +//import at.gv.egovernment.moa.logging.Logger; +// +//import eu.eidas.auth.commons.AttributeConstants; +//import eu.eidas.auth.commons.AttributeUtil; +//import eu.eidas.auth.commons.EIDASErrors; +//import eu.eidas.auth.commons.EIDASParameters; +//import eu.eidas.auth.commons.EIDASUtil; +//import eu.eidas.auth.commons.EIDASValues; +//import eu.eidas.auth.commons.IPersonalAttributeList; +//import eu.eidas.auth.commons.PersonalAttribute; +//import eu.eidas.auth.commons.exceptions.InternalErrorEIDASException; +// +///** +// * @author tlenz +// * +// */ +//public final class MOAPersonalAttributeList extends +// ConcurrentHashMap<String, PersonalAttribute> implements IPersonalAttributeList { +// +// /** +// * +// */ +// private static final long serialVersionUID = -4488124133022713089L; +// +// public MOAPersonalAttributeList(IPersonalAttributeList eIDASAttributeList) { +// super(); +// Iterator<PersonalAttribute> element = eIDASAttributeList.iterator(); +// while(element.hasNext()) +// add(element.next()); +// +// } +// +// /** +// * Hash with the latest fetched attribute name alias. +// */ +// private Map<String, Integer> latestAttrAlias = +// new HashMap<String, Integer>(); +// +// /** +// * Hash with mapping number of alias or the attribute name. +// */ +// private Map<String, Integer> attrAliasNumber = +// new HashMap<String, Integer>(); +// private List<String> insertOrder = new ArrayList<String>(); +// +// /** +// * Obtain the insertOrder Collection +// * +// * @return defensive copy of the collection +// */ +// List<String> getInsertOrder() { +// return Collections.unmodifiableList(this.insertOrder); +// } +// +// /** +// * Default constructor. +// */ +// public MOAPersonalAttributeList() { +// super(); +// +// } +// +// /** +// * Constructor with initial capacity for the PersonalAttributeList size. +// * +// * @param capacity The initial capacity for the PersonalAttributeList. +// */ +// public MOAPersonalAttributeList(final int capacity) { +// super(capacity); +// } +// +// /** +// * {@inheritDoc} +// */ +// public Iterator<PersonalAttribute> iterator() { +// return new MOAOrderedAttributeIterator(this); +// } +// +// /** +// * {@inheritDoc} +// */ +// public PersonalAttribute get(final Object key) { +// String attrName = (String) key; +// +// if (this.latestAttrAlias == null) +// this.latestAttrAlias = new HashMap<String, Integer>(); +// +// if (this.attrAliasNumber == null) +// this.attrAliasNumber = new HashMap<String, Integer>(); +// +// if (this.latestAttrAlias.containsKey(key)) { +// attrName = attrName + this.latestAttrAlias.get(key); +// } else { +// if (this.attrAliasNumber.containsKey(key)) { +// this.latestAttrAlias.put(attrName, this.attrAliasNumber.get(key)); +// } +// } +// return super.get(attrName); +// } +// +// /** +// * {@inheritDoc} +// */ +// public void add(final PersonalAttribute value) { +// if (value != null) { +// this.put(value.getName(), value); +// } +// } +// +// /** +// * {@inheritDoc} +// */ +// public PersonalAttribute put(final String key, final PersonalAttribute val) { +// if (StringUtils.isNotEmpty(key) && val != null) { +// // Validate if attribute name already exists! +// String attrAlias = key; +// if (this.containsKey(attrAlias)) { +// if (this.attrAliasNumber == null) +// this.attrAliasNumber = new HashMap<String, Integer>(); +// if (!val.isEmptyValue() && StringUtils.isNumeric(val.getValue().get(0))) { +// final String attrValue = val.getValue().get(0); +// attrAlias = key + attrValue; +// this.attrAliasNumber.put(key, Integer.valueOf(attrValue)); +// } else { +// final PersonalAttribute attr = super.get(key); +// if (!attr.isEmptyValue() +// && StringUtils.isNumeric(attr.getValue().get(0))) { +// attrAlias = key + attr.getValue().get(0); +// super.put(key, (PersonalAttribute) attr); +// this.attrAliasNumber.put(key, null); +// } +// } +// } else { +// if (insertOrder == null) +// insertOrder = new ArrayList<String>(); +// +// insertOrder.add(key); +// } +// return super.put(attrAlias, val); +// } else { +// return null; +// } +// } +// +// @Override +// public PersonalAttribute remove(Object key) { +// insertOrder.remove(key); +// return super.remove(key); +// } +// +// /** +// * {@inheritDoc} +// */ +// public void populate(final String attrList) { +// final StringTokenizer strToken = +// new StringTokenizer(attrList, EIDASValues.ATTRIBUTE_SEP.toString()); +// +// while (strToken.hasMoreTokens()) { +// final PersonalAttribute persAttr = new PersonalAttribute(); +// String[] tuples = +// strToken.nextToken().split(EIDASValues.ATTRIBUTE_TUPLE_SEP.toString(), +// AttributeConstants.NUMBER_TUPLES.intValue()); +// +// // Convert to the new format if needed! +// tuples = convertFormat(tuples); +// +// if (AttributeUtil.hasValidTuples(tuples)) { +// final int attrValueIndex = +// AttributeConstants.ATTR_VALUE_INDEX.intValue(); +// final String tmpAttrValue = +// tuples[attrValueIndex].substring(1, +// tuples[attrValueIndex].length() - 1); +// final String[] vals = +// tmpAttrValue.split(EIDASValues.ATTRIBUTE_VALUE_SEP.toString()); +// +// persAttr.setName(tuples[AttributeConstants.ATTR_NAME_INDEX.intValue()]); +// persAttr.setIsRequired(Boolean +// .valueOf(tuples[AttributeConstants.ATTR_TYPE_INDEX.intValue()])); +// // check if it is a complex value +// if (tuples[AttributeConstants.ATTR_NAME_INDEX.intValue()] +// .equals(EIDASParameters.COMPLEX_ADDRESS_VALUE.toString())) { +// persAttr.setComplexValue(createComplexValue(vals)); +// } else { +// persAttr.setValue(createValues(vals)); +// } +// +// if (tuples.length == AttributeConstants.NUMBER_TUPLES.intValue()) { +// persAttr.setStatus(tuples[AttributeConstants.ATTR_STATUS_INDEX +// .intValue()]); +// } +// this.put(tuples[AttributeConstants.ATTR_NAME_INDEX.intValue()], +// persAttr); +// +// } else { +// Logger.info("BUSINESS EXCEPTION : Invalid personal attribute list tuples"); +// } +// +// } +// } +// +// /** +// * Returns a copy of this <tt>IPersonalAttributeList</tt> instance. +// * +// * @return The copy of this IPersonalAttributeList. +// */ +// public Object clone() { +// try { +// MOAPersonalAttributeList theClone= (MOAPersonalAttributeList)super.clone(); +// theClone.insertOrder=new ArrayList<String>(insertOrder); +// return theClone; +// +// } catch (CloneNotSupportedException e) { +// throw new InternalErrorEIDASException( +// EIDASUtil.getConfig(EIDASErrors.INTERNAL_ERROR.errorCode()), +// EIDASUtil.getConfig(EIDASErrors.INTERNAL_ERROR.errorMessage()), e); +// } +// } +// +// /** +// * Creates a string in the following format. +// * +// * attrName:attrType:[attrValue1,attrValue2=attrComplexValue]:attrStatus; +// * +// * @return {@inheritDoc} +// */ +// @Override +// public String toString() { +// final StringBuilder strBuilder = new StringBuilder(); +// final Iterator<String> iteratorInsertOrder = insertOrder.iterator(); +// while (iteratorInsertOrder.hasNext()) { +// String key = iteratorInsertOrder.next(); +// final PersonalAttribute attr = get(key); +// strBuilder.append(attr.toString()); +// if (isNumberAlias(key)) { +// strBuilder.append(get(key).toString()); +// } +// } +// return strBuilder.toString(); +// } +// +// /** +// * Validates and creates the attribute's complex values. +// * +// * @param values The complex values. +// * @return The {@link Map} with the complex values. +// * @see Map +// */ +// private Map<String, String> createComplexValue(final String[] values) { +// final Map<String, String> complexValue = new HashMap<String, String>(); +// for (final String val : values) { +// final String[] tVal = val.split("="); +// if (StringUtils.isNotEmpty(val) && tVal.length == 2) { +// complexValue.put(tVal[0], AttributeUtil.unescape(tVal[1])); +// } +// } +// return complexValue; +// } +// +// /** +// * Validates and creates the attribute values. +// * +// * @param vals The attribute values. +// * @return The {@link List} with the attribute values. +// * @see List +// */ +// private List<String> createValues(final String[] vals) { +// final List<String> values = new ArrayList<String>(); +// for (final String val : vals) { +// if (StringUtils.isNotEmpty(val)) { +// values.add(AttributeUtil.unescape(val)); +// } +// } +// return values; +// } +// +// ////////////////// +// /** +// * Converts the attribute tuple (attrName:attrType...) to the new format. +// * +// * @param tuples The attribute tuples to convert. +// * @return The attribute tuples in the new format. +// */ +// private String[] convertFormat(final String[] tuples) { +// final String[] newFormatTuples = +// new String[AttributeConstants.NUMBER_TUPLES.intValue()]; +// if (tuples != null) { +// System.arraycopy(tuples, 0, newFormatTuples, 0, tuples.length); +// +// for (int i = tuples.length; i < newFormatTuples.length; i++) { +// if (i == AttributeConstants.ATTR_VALUE_INDEX.intValue()) { +// newFormatTuples[i] = "[]"; +// } else { +// newFormatTuples[i] = ""; +// } +// } +// } +// return newFormatTuples; +// } +// +// public boolean isNumberAlias(String key) { +// return this.attrAliasNumber.containsKey(key); +// } +// +// +// +//} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAProtocolEngineFactory.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAProtocolEngineFactory.java new file mode 100644 index 000000000..f29d2bb65 --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAProtocolEngineFactory.java @@ -0,0 +1,99 @@ +/* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egovernment.moa.id.auth.modules.eidas.utils; + +import at.gv.egovernment.moa.id.auth.modules.eidas.config.MOAIDCertificateManagerConfigurationImpl; +import at.gv.egovernment.moa.logging.Logger; +import eu.eidas.auth.engine.ProtocolEngineFactory; +import eu.eidas.auth.engine.configuration.SamlEngineConfigurationException; +import eu.eidas.auth.engine.configuration.dom.ProtocolEngineConfigurationFactory; +import eu.eidas.samlengineconfig.CertificateConfigurationManager; + +/** + * @author tlenz + * + */ +public class MOAProtocolEngineFactory extends ProtocolEngineFactory { + + /** + * Initialization-on-demand holder idiom. + * <p/> + * See item 71 of Effective Java 2nd Edition. + * <p/> + * See http://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom. + */ + private static final class LazyHolder { + + private static final MOAProtocolEngineFactory DEFAULT_SAML_ENGINE_FACTORY; + + private static final Exception INITIALIZATION_EXCEPTION; + + static { + Exception initializationException = null; + MOAProtocolEngineFactory defaultProtocolEngineFactory = null; + try { + //get eIDAS SAMLengine configuration from MOA-ID configuration + CertificateConfigurationManager configManager = new MOAIDCertificateManagerConfigurationImpl(); + + ProtocolEngineConfigurationFactory engineConfigurationFactory = new ProtocolEngineConfigurationFactory(configManager); + defaultProtocolEngineFactory = new MOAProtocolEngineFactory(engineConfigurationFactory); + + } catch (Exception ex) { + initializationException = ex; + Logger.error("Unable to instantiate default SAML engines: " + ex, ex); + + } + + DEFAULT_SAML_ENGINE_FACTORY = defaultProtocolEngineFactory; + INITIALIZATION_EXCEPTION = initializationException; + } + + static MOAProtocolEngineFactory getDefaultSamlEngineFactory() { + if (null == INITIALIZATION_EXCEPTION) { + return DEFAULT_SAML_ENGINE_FACTORY; + + } else { + throw new IllegalStateException(INITIALIZATION_EXCEPTION); + + } + } + } + + + public static MOAProtocolEngineFactory getInstance() { + return LazyHolder.getDefaultSamlEngineFactory(); + + } + + /** + * @param configurationFactory + * @throws SamlEngineConfigurationException + */ + private MOAProtocolEngineFactory(ProtocolEngineConfigurationFactory configurationFactory) + throws SamlEngineConfigurationException { + super(configurationFactory); + + } + + +} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAeIDASMetadataGenerator.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAeIDASMetadataGenerator.java new file mode 100644 index 000000000..c614e6490 --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAeIDASMetadataGenerator.java @@ -0,0 +1,613 @@ +/* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egovernment.moa.id.auth.modules.eidas.utils; + +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.DateTime; +import org.joda.time.DurationFieldType; +import org.opensaml.Configuration; +import org.opensaml.common.xml.SAMLConstants; +import org.opensaml.saml2.common.Extensions; +import org.opensaml.saml2.core.Attribute; +import org.opensaml.saml2.core.AttributeValue; +import org.opensaml.saml2.metadata.AssertionConsumerService; +import org.opensaml.saml2.metadata.Company; +import org.opensaml.saml2.metadata.ContactPerson; +import org.opensaml.saml2.metadata.ContactPersonTypeEnumeration; +import org.opensaml.saml2.metadata.EmailAddress; +import org.opensaml.saml2.metadata.EncryptionMethod; +import org.opensaml.saml2.metadata.EntitiesDescriptor; +import org.opensaml.saml2.metadata.EntityDescriptor; +import org.opensaml.saml2.metadata.GivenName; +import org.opensaml.saml2.metadata.IDPSSODescriptor; +import org.opensaml.saml2.metadata.KeyDescriptor; +import org.opensaml.saml2.metadata.LocalizedString; +import org.opensaml.saml2.metadata.NameIDFormat; +import org.opensaml.saml2.metadata.Organization; +import org.opensaml.saml2.metadata.OrganizationDisplayName; +import org.opensaml.saml2.metadata.OrganizationURL; +import org.opensaml.saml2.metadata.SPSSODescriptor; +import org.opensaml.saml2.metadata.SSODescriptor; +import org.opensaml.saml2.metadata.SingleSignOnService; +import org.opensaml.saml2.metadata.SurName; +import org.opensaml.saml2.metadata.TelephoneNumber; +import org.opensaml.samlext.saml2mdattr.EntityAttributes; +import org.opensaml.xml.XMLObject; +import org.opensaml.xml.XMLObjectBuilderFactory; +import org.opensaml.xml.schema.XSString; +import org.opensaml.xml.schema.impl.XSStringBuilder; +import org.opensaml.xml.security.SecurityException; +import org.opensaml.xml.security.credential.Credential; +import org.opensaml.xml.security.credential.UsageType; +import org.opensaml.xml.security.keyinfo.KeyInfoGenerator; +import org.opensaml.xml.security.x509.X509KeyInfoGeneratorFactory; +import org.opensaml.xml.signature.KeyInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.Ordering; + +import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; +import eu.eidas.auth.commons.EIDASUtil; +import eu.eidas.auth.commons.EidasStringUtil; +import eu.eidas.auth.commons.attribute.AttributeDefinition; +import eu.eidas.auth.commons.protocol.impl.SamlNameIdFormat; +import eu.eidas.auth.commons.xml.opensaml.OpenSamlHelper; +import eu.eidas.auth.engine.ProtocolEngineI; +import eu.eidas.auth.engine.core.SAMLExtensionFormat; +import eu.eidas.auth.engine.core.eidas.DigestMethod; +import eu.eidas.auth.engine.core.eidas.EidasConstants; +import eu.eidas.auth.engine.core.eidas.SPType; +import eu.eidas.auth.engine.core.eidas.SigningMethod; +import eu.eidas.auth.engine.metadata.Contact; +import eu.eidas.auth.engine.metadata.EntityDescriptorContainer; +import eu.eidas.auth.engine.metadata.MetadataConfigParams; +import eu.eidas.auth.engine.metadata.MetadataGenerator; +import eu.eidas.auth.engine.metadata.MetadataSignerI; +import eu.eidas.auth.engine.xml.opensaml.BuilderFactoryUtil; +import eu.eidas.auth.engine.xml.opensaml.CertificateUtil; +import eu.eidas.encryption.exception.UnmarshallException; +import eu.eidas.engine.exceptions.EIDASSAMLEngineException; +import eu.eidas.engine.exceptions.SAMLEngineException; + +/** + * @author tlenz + * + */ +public class MOAeIDASMetadataGenerator extends MetadataGenerator { + private static final Logger LOGGER = LoggerFactory.getLogger(MetadataGenerator.class.getName()); + + MetadataConfigParams params; + + XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory(); + + SPSSODescriptor spSSODescriptor = null; + + IDPSSODescriptor idpSSODescriptor = null; + + private String ssoLocation; + + /** + * @return a String representation of the entityDescriptr built based on the attributes previously set + */ + public String generateMetadata() throws EIDASSAMLEngineException { + EntityDescriptor entityDescriptor; + try { + entityDescriptor = (EntityDescriptor) builderFactory.getBuilder(EntityDescriptor.DEFAULT_ELEMENT_NAME) + .buildObject(EntityDescriptor.DEFAULT_ELEMENT_NAME); + + entityDescriptor.setEntityID(params.getEntityID()); + entityDescriptor.setOrganization(buildOrganization()); + entityDescriptor.getContactPersons().add(buildContact(ContactPersonTypeEnumeration.SUPPORT)); + entityDescriptor.getContactPersons().add(buildContact(ContactPersonTypeEnumeration.TECHNICAL)); + entityDescriptor.setValidUntil(getExpireDate()); + + X509KeyInfoGeneratorFactory keyInfoGeneratorFactory = new X509KeyInfoGeneratorFactory(); + keyInfoGeneratorFactory.setEmitEntityCertificate(true); + Extensions e = generateExtensions(); + if (!e.getUnknownXMLObjects().isEmpty()) { + entityDescriptor.setExtensions(e); + } + if (spSSODescriptor != null) { + generateSPSSODescriptor(entityDescriptor, keyInfoGeneratorFactory); + } + if (idpSSODescriptor != null) { + generateIDPSSODescriptor(entityDescriptor, keyInfoGeneratorFactory); + } + if (params.getSpEngine() != null) { + ProtocolEngineI spEngine = params.getSpEngine(); + ((MetadataSignerI) spEngine.getSigner()).signMetadata(entityDescriptor); + } else if (params.getIdpEngine() != null) { + ProtocolEngineI idpEngine = params.getIdpEngine(); + ((MetadataSignerI) idpEngine.getSigner()).signMetadata(entityDescriptor); + } + return EidasStringUtil.toString(OpenSamlHelper.marshall(entityDescriptor, false)); + } catch (Exception ex) { + LOGGER.info("ERROR : SAMLException ", ex.getMessage()); + LOGGER.debug("ERROR : SAMLException ", ex); + throw new IllegalStateException(ex); + } + } + + private void generateSPSSODescriptor(final EntityDescriptor entityDescriptor, + final X509KeyInfoGeneratorFactory keyInfoGeneratorFactory) + throws org.opensaml.xml.security.SecurityException, IllegalAccessException, NoSuchFieldException, + SAMLEngineException, EIDASSAMLEngineException { + //the node has SP role + spSSODescriptor.setWantAssertionsSigned(params.isWantAssertionsSigned()); + spSSODescriptor.setAuthnRequestsSigned(true); + spSSODescriptor.setID(idpSSODescriptor == null ? params.getEntityID() + : ("SP" + params.getEntityID())); + if (params.getSPSignature() != null) { + spSSODescriptor.setSignature(params.getSPSignature()); + } + if (params.getSpSigningCredential() != null) { + spSSODescriptor.getKeyDescriptors() + .add(getKeyDescriptor(keyInfoGeneratorFactory, params.getSpSigningCredential(), UsageType.SIGNING)); + + } else if (params.getSigningCredential() != null) { + spSSODescriptor.getKeyDescriptors() + .add(getKeyDescriptor(keyInfoGeneratorFactory, params.getSigningCredential(), UsageType.SIGNING)); + } + + if (params.getSpEncryptionCredential() != null) { + spSSODescriptor.getKeyDescriptors() + .add(getKeyDescriptor(keyInfoGeneratorFactory, params.getSpEncryptionCredential(), + UsageType.ENCRYPTION)); + } else if (params.getEncryptionCredential() != null) { + spSSODescriptor.getKeyDescriptors() + .add(getKeyDescriptor(keyInfoGeneratorFactory, params.getEncryptionCredential(), UsageType.ENCRYPTION)); + } + spSSODescriptor.addSupportedProtocol(params.getSpSamlProtocol()); + if (!StringUtils.isEmpty(params.getAssertionConsumerUrl())) { + addAssertionConsumerService(); + } + fillNameIDFormat(spSSODescriptor); + if (params.getSpEngine() != null) { + ProtocolEngineI spEngine = params.getSpEngine(); + ((MetadataSignerI) spEngine.getSigner()).signMetadata(spSSODescriptor); + } + entityDescriptor.getRoleDescriptors().add(spSSODescriptor); + + } + + private void fillNameIDFormat(SSODescriptor ssoDescriptor) throws EIDASSAMLEngineException { + NameIDFormat persistentFormat = + (NameIDFormat) BuilderFactoryUtil.buildXmlObject(NameIDFormat.DEFAULT_ELEMENT_NAME); + persistentFormat.setFormat(SamlNameIdFormat.PERSISTENT.getNameIdFormat()); + ssoDescriptor.getNameIDFormats().add(persistentFormat); + NameIDFormat transientFormat = + (NameIDFormat) BuilderFactoryUtil.buildXmlObject(NameIDFormat.DEFAULT_ELEMENT_NAME); + transientFormat.setFormat(SamlNameIdFormat.TRANSIENT.getNameIdFormat()); + ssoDescriptor.getNameIDFormats().add(transientFormat); + NameIDFormat unspecifiedFormat = + (NameIDFormat) BuilderFactoryUtil.buildXmlObject(NameIDFormat.DEFAULT_ELEMENT_NAME); + unspecifiedFormat.setFormat(SamlNameIdFormat.UNSPECIFIED.getNameIdFormat()); + ssoDescriptor.getNameIDFormats().add(unspecifiedFormat); + } + + private void generateIDPSSODescriptor(final EntityDescriptor entityDescriptor, + final X509KeyInfoGeneratorFactory keyInfoGeneratorFactory) + throws org.opensaml.xml.security.SecurityException, IllegalAccessException, NoSuchFieldException, + SAMLEngineException, EIDASSAMLEngineException { + //the node has IDP role + idpSSODescriptor.setWantAuthnRequestsSigned(true); + idpSSODescriptor.setID(spSSODescriptor == null ? params.getEntityID() + : ("IDP" + params.getEntityID())); + if (params.getIDPSignature() != null) { + idpSSODescriptor.setSignature(params.getIDPSignature()); + } + if (params.getIdpSigningCredential() != null) { + idpSSODescriptor.getKeyDescriptors() + .add(getKeyDescriptor(keyInfoGeneratorFactory, params.getIdpSigningCredential(), UsageType.SIGNING)); + } else if (params.getSigningCredential() != null) { + idpSSODescriptor.getKeyDescriptors() + .add(getKeyDescriptor(keyInfoGeneratorFactory, params.getSigningCredential(), UsageType.SIGNING)); + } + if (params.getIdpEncryptionCredential() != null) { + idpSSODescriptor.getKeyDescriptors() + .add(getKeyDescriptor(keyInfoGeneratorFactory, params.getIdpEncryptionCredential(), + UsageType.ENCRYPTION)); + } else if (params.getEncryptionCredential() != null) { + idpSSODescriptor.getKeyDescriptors() + .add(getKeyDescriptor(keyInfoGeneratorFactory, params.getEncryptionCredential(), UsageType.ENCRYPTION)); + } + idpSSODescriptor.addSupportedProtocol(params.getIdpSamlProtocol()); + fillNameIDFormat(idpSSODescriptor); + if (params.getIdpEngine() != null) { + if (params.getIdpEngine().getProtocolProcessor() != null + && params.getIdpEngine().getProtocolProcessor().getFormat() == SAMLExtensionFormat.EIDAS10) { + + /*TODO: Only a work-around to add eIDAS attributes, which could be provided from MOA-ID, to IDP metadata + * If we restrict the eIDAS Engine attribute definitions then also additional incoming attributes can not processed any more. + * + * INFO: Maybe, this code can be removed in a future version of the eIDAS engine + */ + generateSupportedAttributes(idpSSODescriptor, getAllSupportedAttributes()); + } + ProtocolEngineI idpEngine = params.getIdpEngine(); + ((MetadataSignerI) idpEngine.getSigner()).signMetadata(idpSSODescriptor); + } + + idpSSODescriptor.getSingleSignOnServices().addAll(buildSingleSignOnServicesBindingLocations()); + + entityDescriptor.getRoleDescriptors().add(idpSSODescriptor); + + } + + /*TODO: Only a work-around to add eIDAS attributes, which could be provided from MOA-ID, to IDP metadata + * If we restrict the eIDAS Engine attribute definitions then also additional incoming attributes can not processed any more. + */ + public ImmutableSortedSet<AttributeDefinition<?>> getAllSupportedAttributes() { + ImmutableSortedSet.Builder<AttributeDefinition<?>> builder = + new ImmutableSortedSet.Builder<>(Ordering.<AttributeDefinition<?>>natural()); + builder.addAll(Constants.MOA_IDP_ATTR_REGISTRY.getAttributes()); + return builder.build(); + } + + private ArrayList<SingleSignOnService> buildSingleSignOnServicesBindingLocations() + throws NoSuchFieldException, IllegalAccessException { + ArrayList<SingleSignOnService> singleSignOnServices = new ArrayList<SingleSignOnService>(); + + HashMap<String, String> bindingLocations = params.getProtocolBindingLocation(); + for (String binding : bindingLocations.keySet()) { + SingleSignOnService ssos = BuilderFactoryUtil.buildXmlObject(SingleSignOnService.class); + ssos.setBinding(binding); + ssos.setLocation(bindingLocations.get(binding)); + singleSignOnServices.add(ssos); + } + + return singleSignOnServices; + } + + /** + * @param metadata + * @return an EntityDescriptor parsed from the given String or null + */ + // TODO (commented by donydgr) Move to a eu.eidas.auth.engine.metadata.MetadataUtil ? Throw an exception if the metadata is invalid instead of returning null ? + public static EntityDescriptorContainer deserializeEntityDescriptor(String metadata) { + EntityDescriptorContainer result = new EntityDescriptorContainer(); + try { + byte[] metaDataBytes = EidasStringUtil.getBytes(metadata); + XMLObject obj = OpenSamlHelper.unmarshall(metaDataBytes); + if (obj instanceof EntityDescriptor) { + result.addEntityDescriptor((EntityDescriptor) obj, metaDataBytes); + } else if (obj instanceof EntitiesDescriptor) { + EntitiesDescriptor ed = (EntitiesDescriptor) obj; + result.setEntitiesDescriptor(ed); + result.getEntityDescriptors().addAll(((EntitiesDescriptor) obj).getEntityDescriptors()); + result.setSerializedEntitesDescriptor(metaDataBytes); + } + } catch (UnmarshallException ue) { + LOGGER.info("ERROR : unmarshalling error", ue.getMessage()); + LOGGER.debug("ERROR : unmarshalling error", ue); + } + return result; + } + + private KeyDescriptor getKeyDescriptor(X509KeyInfoGeneratorFactory keyInfoGeneratorFactory, + Credential credential, + UsageType usage) + throws NoSuchFieldException, IllegalAccessException, SecurityException, EIDASSAMLEngineException { + KeyDescriptor keyDescriptor = null; + if (credential != null) { + keyDescriptor = BuilderFactoryUtil.buildXmlObject(KeyDescriptor.class); + KeyInfoGenerator keyInfoGenerator = keyInfoGeneratorFactory.newInstance(); + + KeyInfo keyInfo = keyInfoGenerator.generate(credential); + keyDescriptor.setUse(usage); + keyDescriptor.setKeyInfo(keyInfo); + if (usage == UsageType.ENCRYPTION && params.getEncryptionAlgorithms() != null) { + Set<String> encryptionAlgos = EIDASUtil.parseSemicolonSeparatedList(params.getEncryptionAlgorithms()); + for (String encryptionAlgo : encryptionAlgos) { + EncryptionMethod em = + (EncryptionMethod) BuilderFactoryUtil.buildXmlObject(EncryptionMethod.DEFAULT_ELEMENT_NAME); + em.setAlgorithm(encryptionAlgo); + keyDescriptor.getEncryptionMethods().add(em); + } + } + + } + return keyDescriptor; + } + + private Organization buildOrganization() { + Organization organization = null; + try { + organization = BuilderFactoryUtil.buildXmlObject(Organization.class); + OrganizationDisplayName odn = BuilderFactoryUtil.buildXmlObject(OrganizationDisplayName.class); + odn.setName(new LocalizedString(params.getCountryName(), "en")); + organization.getDisplayNames().add(odn); + OrganizationURL url = BuilderFactoryUtil.buildXmlObject(OrganizationURL.class); + url.setURL(new LocalizedString(params.getNodeUrl(), "en")); + organization.getURLs().add(url); + } catch (IllegalAccessException iae) { + LOGGER.info("ERROR : error generating the Organization: {}", iae.getMessage()); + LOGGER.debug("ERROR : error generating the Organization: {}", iae); + } catch (NoSuchFieldException nfe) { + LOGGER.info("ERROR : error generating the Organization: {}", nfe.getMessage()); + LOGGER.debug("ERROR : error generating the Organization: {}", nfe); + } + return organization; + } + + private ContactPerson buildContact(ContactPersonTypeEnumeration contactType) { + ContactPerson contact = null; + try { + Contact currentContact = null; + if (contactType == ContactPersonTypeEnumeration.SUPPORT) { + currentContact = params.getSupportContact(); + } else if (contactType == ContactPersonTypeEnumeration.TECHNICAL) { + currentContact = params.getTechnicalContact(); + } else { + LOGGER.error("ERROR: unsupported contact type"); + } + contact = BuilderFactoryUtil.buildXmlObject(ContactPerson.class); + if (currentContact == null) { + LOGGER.error("ERROR: cannot retrieve contact from the configuration"); + return contact; + } + + EmailAddress emailAddressObj = BuilderFactoryUtil.buildXmlObject(EmailAddress.class); + Company company = BuilderFactoryUtil.buildXmlObject(Company.class); + GivenName givenName = BuilderFactoryUtil.buildXmlObject(GivenName.class); + SurName surName = BuilderFactoryUtil.buildXmlObject(SurName.class); + TelephoneNumber phoneNumber = BuilderFactoryUtil.buildXmlObject(TelephoneNumber.class); + contact.setType(contactType); + emailAddressObj.setAddress(currentContact.getEmail()); + company.setName(currentContact.getCompany()); + givenName.setName(currentContact.getGivenName()); + surName.setName(currentContact.getSurName()); + phoneNumber.setNumber(currentContact.getPhone()); + + populateContact(contact, currentContact, emailAddressObj, company, givenName, surName, phoneNumber); + + } catch (IllegalAccessException iae) { + LOGGER.info("ERROR : error generating the Organization: {}", iae.getMessage()); + LOGGER.debug("ERROR : error generating the Organization: {}", iae); + } catch (NoSuchFieldException nfe) { + LOGGER.info("ERROR : error generating the Organization: {}", nfe.getMessage()); + LOGGER.debug("ERROR : error generating the Organization: {}", nfe); + } + return contact; + } + + private void populateContact(ContactPerson contact, + Contact currentContact, + EmailAddress emailAddressObj, + Company company, + GivenName givenName, + SurName surName, + TelephoneNumber phoneNumber) { + if (!StringUtils.isEmpty(currentContact.getEmail())) { + contact.getEmailAddresses().add(emailAddressObj); + } + if (!StringUtils.isEmpty(currentContact.getCompany())) { + contact.setCompany(company); + } + if (!StringUtils.isEmpty(currentContact.getGivenName())) { + contact.setGivenName(givenName); + } + if (!StringUtils.isEmpty(currentContact.getSurName())) { + contact.setSurName(surName); + } + if (!StringUtils.isEmpty(currentContact.getPhone())) { + contact.getTelephoneNumbers().add(phoneNumber); + } + + } + + /** + * @param engine a EIDASSamlEngine from which signing and encryption information is extracted + */ + + public void initialize(ProtocolEngineI engine) throws EIDASSAMLEngineException { + + X509Certificate decryptionCertificate = engine.getDecryptionCertificate(); + if (null != decryptionCertificate) { + params.setEncryptionCredential(CertificateUtil.toCredential(decryptionCertificate)); + } + params.setSigningCredential(CertificateUtil.toCredential(engine.getSigningCertificate())); + params.setIdpEngine(engine); + params.setSpEngine(engine); + } + + /** + * @param spEngine a EIDASSamlEngine for the + */ + + public void initialize(ProtocolEngineI spEngine, ProtocolEngineI idpEngine) throws EIDASSAMLEngineException { + if (idpEngine != null) { + idpEngine.getProtocolProcessor().configure(); + params.setIdpSigningCredential(CertificateUtil.toCredential(idpEngine.getSigningCertificate())); + + final X509Certificate idpEngineDecryptionCertificate = idpEngine.getDecryptionCertificate(); + if (idpEngineDecryptionCertificate != null) { + params.setIdpEncryptionCredential(CertificateUtil.toCredential(idpEngineDecryptionCertificate)); + } + + } + if (spEngine != null) { + spEngine.getProtocolProcessor().configure(); + params.setSpSigningCredential(CertificateUtil.toCredential(spEngine.getSigningCertificate())); + + final X509Certificate spEngineDecryptionCertificate = spEngine.getDecryptionCertificate(); + if (spEngineDecryptionCertificate != null) { + params.setSpEncryptionCredential(CertificateUtil.toCredential(spEngineDecryptionCertificate)); + } + } + + params.setIdpEngine(idpEngine); + params.setSpEngine(spEngine); + } + + public void addSPRole() throws EIDASSAMLEngineException { + try { + if (spSSODescriptor == null) { + spSSODescriptor = BuilderFactoryUtil.buildXmlObject(SPSSODescriptor.class); + } + } catch (IllegalAccessException iae) { + throw new EIDASSAMLEngineException(iae); + } catch (NoSuchFieldException nsfe) { + throw new EIDASSAMLEngineException(nsfe); + } + } + + public void addIDPRole() throws EIDASSAMLEngineException { + try { + if (idpSSODescriptor == null) { + idpSSODescriptor = BuilderFactoryUtil.buildXmlObject(IDPSSODescriptor.class); + } + } catch (IllegalAccessException iae) { + throw new EIDASSAMLEngineException(iae); + } catch (NoSuchFieldException nsfe) { + throw new EIDASSAMLEngineException(nsfe); + } + } + + private void generateDigest(Extensions eidasExtensions) throws EIDASSAMLEngineException { + if (!StringUtils.isEmpty(params.getDigestMethods())) { + Set<String> signatureMethods = EIDASUtil.parseSemicolonSeparatedList(params.getDigestMethods()); + Set<String> digestMethods = new HashSet<String>(); + for (String signatureMethod : signatureMethods) { + digestMethods.add(CertificateUtil.validateDigestAlgorithm(signatureMethod)); + } + for (String digestMethod : digestMethods) { + final DigestMethod dm = (DigestMethod) BuilderFactoryUtil.buildXmlObject(DigestMethod.DEF_ELEMENT_NAME); + if (dm != null) { + dm.setAlgorithm(digestMethod); + eidasExtensions.getUnknownXMLObjects().add(dm); + } else { + LOGGER.info("BUSINESS EXCEPTION error adding DigestMethod extension"); + } + } + } + + } + + private Extensions generateExtensions() throws EIDASSAMLEngineException { + Extensions eidasExtensions = BuilderFactoryUtil.generateExtension(); + if (params.getAssuranceLevel() != null) { + generateLoA(eidasExtensions); + } + if (!StringUtils.isEmpty(params.getSpType())) { + final SPType spTypeObj = (SPType) BuilderFactoryUtil.buildXmlObject(SPType.DEF_ELEMENT_NAME); + if (spTypeObj != null) { + spTypeObj.setSPType(params.getSpType()); + eidasExtensions.getUnknownXMLObjects().add(spTypeObj); + } else { + LOGGER.info("BUSINESS EXCEPTION error adding SPType extension"); + } + } + generateDigest(eidasExtensions); + + if (!StringUtils.isEmpty(params.getSigningMethods())) { + Set<String> signMethods = EIDASUtil.parseSemicolonSeparatedList(params.getDigestMethods()); + for (String signMethod : signMethods) { + final SigningMethod sm = + (SigningMethod) BuilderFactoryUtil.buildXmlObject(SigningMethod.DEF_ELEMENT_NAME); + if (sm != null) { + sm.setAlgorithm(signMethod); + eidasExtensions.getUnknownXMLObjects().add(sm); + } else { + LOGGER.info("BUSINESS EXCEPTION error adding SigningMethod extension"); + } + } + } + return eidasExtensions; + } + + private void generateLoA(Extensions eidasExtensions) throws EIDASSAMLEngineException { + EntityAttributes loa = + (EntityAttributes) BuilderFactoryUtil.buildXmlObject(EntityAttributes.DEFAULT_ELEMENT_NAME); + Attribute loaAttrib = (Attribute) BuilderFactoryUtil.buildXmlObject(Attribute.DEFAULT_ELEMENT_NAME); + loaAttrib.setName(EidasConstants.LEVEL_OF_ASSURANCE_NAME); + loaAttrib.setNameFormat(Attribute.URI_REFERENCE); + XSStringBuilder stringBuilder = + (XSStringBuilder) Configuration.getBuilderFactory().getBuilder(XSString.TYPE_NAME); + XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME); + stringValue.setValue(params.getAssuranceLevel()); + loaAttrib.getAttributeValues().add(stringValue); + loa.getAttributes().add(loaAttrib); + eidasExtensions.getUnknownXMLObjects().add(loa); + + } + + private static final Set<String> DEFAULT_BINDING = new HashSet<String>() {{ + this.add(SAMLConstants.SAML2_POST_BINDING_URI); + }}; + + private void addAssertionConsumerService() throws EIDASSAMLEngineException { + int index = 0; + Set<String> bindings = params.getProtocolBinding().isEmpty() ? DEFAULT_BINDING : params.getProtocolBinding(); + for (String binding : bindings) { + AssertionConsumerService asc = (AssertionConsumerService) BuilderFactoryUtil.buildXmlObject( + AssertionConsumerService.DEFAULT_ELEMENT_NAME); + asc.setLocation(params.getAssertionConsumerUrl()); + asc.setBinding(checkBinding(binding)); + asc.setIndex(index); + if (index == 0) { + asc.setIsDefault(true); + } + index++; + spSSODescriptor.getAssertionConsumerServices().add(asc); + } + } + + private String checkBinding(String binding) { + if (binding != null && (binding.equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI) || binding.equals( + SAMLConstants.SAML2_POST_BINDING_URI))) { + return binding; + } + return SAMLConstants.SAML2_POST_BINDING_URI; + } + + private DateTime getExpireDate() { + DateTime expiryDate = DateTime.now(); + expiryDate = + expiryDate.withFieldAdded(DurationFieldType.seconds(), (int) (getConfigParams().getValidityDuration())); + return expiryDate; + } + + private void generateSupportedAttributes(IDPSSODescriptor idpssoDescriptor, + ImmutableSortedSet<AttributeDefinition<?>> attributeDefinitions) + throws EIDASSAMLEngineException { + List<Attribute> attributes = idpssoDescriptor.getAttributes(); + for (AttributeDefinition<?> attributeDefinition : attributeDefinitions) { + Attribute a = (Attribute) BuilderFactoryUtil.buildXmlObject(Attribute.DEFAULT_ELEMENT_NAME); + a.setName(attributeDefinition.getNameUri().toASCIIString()); + a.setFriendlyName(attributeDefinition.getFriendlyName()); + a.setNameFormat(Attribute.URI_REFERENCE); + attributes.add(a); + } + } + +} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/SAMLEngineUtils.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/SAMLEngineUtils.java index 68640caf7..cdae92e58 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/SAMLEngineUtils.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/SAMLEngineUtils.java @@ -22,15 +22,22 @@ */ package at.gv.egovernment.moa.id.auth.modules.eidas.utils; +import java.util.HashMap; +import java.util.Map; + import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; import at.gv.egovernment.moa.id.auth.modules.eidas.config.MOAIDCertificateManagerConfigurationImpl; +import at.gv.egovernment.moa.id.auth.modules.eidas.config.MOASWSigner; import at.gv.egovernment.moa.id.auth.modules.eidas.engine.MOAeIDASChainingMetadataProvider; import at.gv.egovernment.moa.id.auth.modules.eidas.engine.MOAeIDASMetadataProviderDecorator; -import at.gv.egovernment.moa.id.auth.modules.eidas.engine.MOAeIDAsExtensionProcessor; import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.EIDASEngineException; import at.gv.egovernment.moa.logging.Logger; -import eu.eidas.auth.engine.EIDASSAMLEngine; -import eu.eidas.auth.engine.core.ExtensionProcessorI; +import eu.eidas.auth.commons.attribute.AttributeDefinition; +import eu.eidas.auth.engine.ProtocolEngineI; +import eu.eidas.auth.engine.SamlEngineSystemClock; +import eu.eidas.auth.engine.core.eidas.EidasProtocolProcessor; +import eu.eidas.auth.engine.metadata.MetadataFetcherI; +import eu.eidas.auth.engine.metadata.MetadataSignerI; import eu.eidas.engine.exceptions.EIDASSAMLEngineException; import eu.eidas.samlengineconfig.CertificateConfigurationManager; @@ -40,28 +47,36 @@ import eu.eidas.samlengineconfig.CertificateConfigurationManager; */ public class SAMLEngineUtils { - private static EIDASSAMLEngine eIDASEngine = null; + private static ProtocolEngineI eIDASEngine = null; + private static MetadataSignerI metadataSigner = null; + private static MetadataFetcherI metadataFetcher = null; + private static Map<String, AttributeDefinition<?>> allSupportedAttributeMap = + new HashMap<String, AttributeDefinition<?>>(); - public static synchronized EIDASSAMLEngine createSAMLEngine(MOAeIDASChainingMetadataProvider moaeIDASMetadataProvider) throws EIDASEngineException{ + public static synchronized ProtocolEngineI createSAMLEngine(MOAeIDASChainingMetadataProvider moaeIDASMetadataProvider) throws EIDASEngineException{ if (eIDASEngine == null) { try { //get eIDAS SAMLengine configuration from MOA-ID configuration CertificateConfigurationManager configManager = new MOAIDCertificateManagerConfigurationImpl(); + + //set metadata management to eIDAS SAMLengine + metadataFetcher = new MOAeIDASMetadataProviderDecorator(moaeIDASMetadataProvider); + + //set metadata signer + metadataSigner = new MOASWSigner(configManager); + + //build eIDAS SAML eninge + ProtocolEngineI engine = MOAProtocolEngineFactory.createProtocolEngine( + Constants.eIDAS_SAML_ENGINE_NAME, + configManager, + new EidasProtocolProcessor(metadataFetcher, metadataSigner), + new SamlEngineSystemClock()); - //initial eIDAS SAMLengine - EIDASSAMLEngine engine = EIDASSAMLEngine.createSAMLEngine(Constants.eIDAS_SAML_ENGINE_NAME, - configManager); - - //set metadata management to eIDAS SAMLengine - engine.setMetadataProcessor( - new MOAeIDASMetadataProviderDecorator( - moaeIDASMetadataProvider)); - - //set MOA specific extension processor - ExtensionProcessorI extensionProcessor = new MOAeIDAsExtensionProcessor(); - engine.setExtensionProcessor(extensionProcessor); - + //build a map with all actually supported attributes + for (AttributeDefinition<?> el : engine.getProtocolProcessor().getAllSupportedAttributes()) + allSupportedAttributeMap.put(el.getFriendlyName(), el); + eIDASEngine = engine; } catch (EIDASSAMLEngineException e) { @@ -73,5 +88,45 @@ public class SAMLEngineUtils { return eIDASEngine; } + + /** + * Get a map of all eIDAS attributes, which are actually supported by eIDAS engine + * + * @return Map<Attr. FriendlyName, AttributeDefinition> + */ + public static Map<String, AttributeDefinition<?>> getMapOfAllAvailableAttributes() { + return allSupportedAttributeMap; + + } + + /** + * @return the metadataSigner + */ + public static MetadataSignerI getMetadataSigner() { + if (eIDASEngine != null) + return metadataSigner; + + else { + Logger.error("eIDAS SAMLEngine is not initialized."); + return null; + + } + } + + /** + * @return the metadataFetcher + */ + public static MetadataFetcherI getMetadataFetcher() { + if (eIDASEngine != null) + return metadataFetcher; + + else { + Logger.error("eIDAS SAMLEngine is not initialized."); + return null; + + } + } + + } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASData.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASData.java index 4dffba575..7647b4cab 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASData.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASData.java @@ -7,9 +7,9 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; -import at.gv.egovernment.moa.id.auth.modules.eidas.utils.MOAPersonalAttributeList; import at.gv.egovernment.moa.id.moduls.RequestImpl; -import eu.eidas.auth.commons.EIDASAuthnRequest; +import eu.eidas.auth.commons.attribute.ImmutableAttributeMap; +import eu.eidas.auth.commons.protocol.IAuthenticationRequest; @Component("EIDASData") @Scope(value = BeanDefinition.SCOPE_PROTOTYPE) @@ -19,10 +19,10 @@ public class EIDASData extends RequestImpl { private static final long serialVersionUID = 8765755670214923910L; /** The attributes requested by the eIDaS. */ - private MOAPersonalAttributeList attributes; + private ImmutableAttributeMap attributes; /** The incoming eIDaS SAML2 AuthnRequest. */ - private EIDASAuthnRequest authnRequest; + private IAuthenticationRequest authnRequest; /** The ip address of the requester. */ private String remoteIPAddress; @@ -40,17 +40,17 @@ public class EIDASData extends RequestImpl { * * @return the requested attributes */ - public MOAPersonalAttributeList getEidasRequestedAttributes() { - return (MOAPersonalAttributeList) attributes.clone(); + public ImmutableAttributeMap getEidasRequestedAttributes() { + return attributes; } /** * Sets the eidas requested attributes. * - * @param personalAttributeList the requested attributes + * @param immutableAttributeMap the requested attributes */ - public void setEidasRequestedAttributes(MOAPersonalAttributeList personalAttributeList) { - attributes = personalAttributeList; + public void setEidasRequestedAttributes(ImmutableAttributeMap immutableAttributeMap) { + attributes = immutableAttributeMap; } /** @@ -58,7 +58,7 @@ public class EIDASData extends RequestImpl { * * @return the eidas request */ - public EIDASAuthnRequest getEidasRequest() { + public IAuthenticationRequest getEidasRequest() { return authnRequest; } @@ -67,7 +67,7 @@ public class EIDASData extends RequestImpl { * * @param request the new eidas request */ - public void setEidasRequest(EIDASAuthnRequest request) { + public void setEidasRequest(IAuthenticationRequest request) { authnRequest = request; } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java index fc935e2ef..cf9414a3b 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java @@ -45,10 +45,9 @@ import at.gv.egovernment.moa.id.auth.frontend.velocity.VelocityProvider; import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; import at.gv.egovernment.moa.id.auth.modules.eidas.engine.MOAeIDASChainingMetadataProvider; import at.gv.egovernment.moa.id.auth.modules.eidas.engine.MOAeIDASMetadataProviderDecorator; -import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.eIDASAuthnRequestProcessingException; -import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.eIDASAuthnRequestValidationException; -import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.eIDASException; -import at.gv.egovernment.moa.id.auth.modules.eidas.utils.MOAPersonalAttributeList; +import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.EIDASAuthnRequestProcessingException; +import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.EIDASAuthnRequestValidationException; +import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.EIDASException; import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils; import at.gv.egovernment.moa.id.commons.api.IOAAuthParameters; import at.gv.egovernment.moa.id.commons.api.IRequest; @@ -57,10 +56,12 @@ import at.gv.egovernment.moa.id.moduls.RequestImpl; import at.gv.egovernment.moa.id.protocols.AbstractAuthProtocolModulController; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.MiscUtil; -import eu.eidas.auth.commons.EIDASAuthnRequest; -import eu.eidas.auth.commons.EIDASAuthnResponse; -import eu.eidas.auth.commons.EIDASUtil; -import eu.eidas.auth.engine.EIDASSAMLEngine; +import eu.eidas.auth.commons.EidasStringUtil; +import eu.eidas.auth.commons.protocol.IAuthenticationRequest; +import eu.eidas.auth.commons.protocol.IResponseMessage; +import eu.eidas.auth.commons.protocol.impl.AuthenticationResponse; +import eu.eidas.auth.commons.protocol.impl.AuthenticationResponse.Builder; +import eu.eidas.auth.engine.ProtocolEngineI; import eu.eidas.auth.engine.metadata.MetadataUtil; import eu.eidas.engine.exceptions.EIDASSAMLEngineException; @@ -82,7 +83,8 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { Logger.debug("Registering servlet " + getClass().getName() + " with mappings '" + Constants.eIDAS_HTTP_ENDPOINT_METADATA + "' and '" + Constants.eIDAS_HTTP_ENDPOINT_IDP_COLLEAGUEREQUEST + - "' and '" + Constants.eIDAS_HTTP_ENDPOINT_IDP_POST +"'."); + //"' and '" + Constants.eIDAS_HTTP_ENDPOINT_IDP_POST + + "'."); } @@ -172,13 +174,15 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { try { //decode SAML2 token - byte[] decSamlToken = EIDASUtil.decodeSAMLToken(base64SamlToken); + byte[] decSamlToken = EidasStringUtil.decodeBytesFromBase64(base64SamlToken); //get eIDAS SAML-engine - EIDASSAMLEngine engine = SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); + ProtocolEngineI engine = SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); + //TODO: change to configurable COUNTRY-CODE + String cititzenCountryCode = "AT"; //validate SAML token - EIDASAuthnRequest samlReq = engine.validateEIDASAuthnRequest(decSamlToken); + IAuthenticationRequest samlReq = engine.unmarshallRequestAndValidate(decSamlToken, cititzenCountryCode ); // - memorize remote ip pendingReq.setRemoteAddress(request.getRemoteAddr()); @@ -189,13 +193,15 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { // - memorize country code of target country pendingReq.setGenericDataToSession( - RequestImpl.eIDAS_GENERIC_REQ_DATA_COUNTRY, samlReq.getCountry()); + RequestImpl.eIDAS_GENERIC_REQ_DATA_COUNTRY, samlReq.getOriginCountryCode()); - // - memorize requested attributes - pendingReq.setEidasRequestedAttributes(new MOAPersonalAttributeList(samlReq.getPersonalAttributeList())); + //TODO: store level of assurance + + + // - memorize requested attributes + pendingReq.setEidasRequestedAttributes(samlReq.getRequestedAttributes()); - // - memorize whole request - samlReq.setPersonalAttributeList(pendingReq.getEidasRequestedAttributes()); // circumvent non-serializable eidas personal attribute list + // - memorize whole request pendingReq.setEidasRequest(samlReq); //validate Destination against MOA-ID-Auth configuration @@ -203,7 +209,7 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { if (MiscUtil.isEmpty(reqDestination) || !reqDestination.startsWith(pendingReq.getAuthURL())) { Logger.info("eIDAS AuthnRequest contains a not valid 'Destination' attribute"); - throw new eIDASAuthnRequestValidationException("stork.01", + throw new EIDASAuthnRequestValidationException("stork.01", new Object[]{"eIDAS AuthnRequest contains a not valid 'Destination' attribute"}); } @@ -223,7 +229,7 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { if (!isValid) { Logger.info("eIDAS AuthnRequest contains a not valid 'AssertionConsumerServiceURL' attribute"); - throw new eIDASAuthnRequestValidationException("stork.01", + throw new EIDASAuthnRequestValidationException("stork.01", new Object[]{"eIDAS AuthnRequest contains a not valid 'AssertionConsumerServiceURL' attribute"}); } @@ -236,18 +242,19 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { // - memorize OA config IOAAuthParameters oaConfig = authConfig.getOnlineApplicationParameter(pendingReq.getOAURL()); if (oaConfig == null) - throw new eIDASAuthnRequestProcessingException("eIDAS.08", new Object[]{pendingReq.getOAURL()}); + throw new EIDASAuthnRequestProcessingException("eIDAS.08", new Object[]{pendingReq.getOAURL()}); pendingReq.setOnlineApplicationConfiguration(oaConfig); - String spType = samlReq.getSPType(); - if (MiscUtil.isEmpty(spType)) { - Logger.info("Load SPType from metadata ... IS NOT IMPLEMENTED YET!!!"); - //TODO: maybe implement this if required - - } - - Logger.debug("eIDAS request has SPType:" + spType); + //TODO: is not supported any more ?!? +// String spType = samlReq.getSPType(); +// if (MiscUtil.isEmpty(spType)) { +// Logger.info("Load SPType from metadata ... IS NOT IMPLEMENTED YET!!!"); +// //TODO: maybe implement this if required +// +// } +// +// Logger.debug("eIDAS request has SPType:" + spType); } catch (MOAIDException e) { Logger.info("eIDAS AuthnRequest preProcessing FAILED. Msg:" + e.getMessage()); @@ -255,11 +262,11 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { } catch (EIDASSAMLEngineException e) { Logger.info("eIDAS AuthnRequest preProcessing FAILED. Msg:" + e.getMessage()); - throw new eIDASAuthnRequestProcessingException("eIDAS.06", new Object[]{e.getMessage()}, e); + throw new EIDASAuthnRequestProcessingException("eIDAS.06", new Object[]{e.getMessage()}, e); } catch(Exception e) { Logger.warn("eIDAS AuthnRequest preProcessing FAILED. Msg:" + e.getMessage(), e); - throw new eIDASAuthnRequestProcessingException("eIDAS.06", new Object[]{e.getMessage()}, e); + throw new EIDASAuthnRequestProcessingException("eIDAS.06", new Object[]{e.getMessage()}, e); } } @@ -273,43 +280,50 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { } try { - EIDASAuthnResponse eIDASResp = new EIDASAuthnResponse(); - eIDASResp.setIssuer(pendingReq.getAuthURL() + Constants.eIDAS_HTTP_ENDPOINT_METADATA); - - if (e instanceof eIDASException) { - eIDASResp.setStatusCode(((eIDASException) e).getStatusCodeFirstLevel()); - eIDASResp.setSubStatusCode(((eIDASException) e).getStatusCodeSecondLevel()); - eIDASResp.setMessage(e.getMessage()); + Builder eIDASRespBuilder = new AuthenticationResponse.Builder(); + eIDASRespBuilder.issuer(pendingReq.getAuthURL() + Constants.eIDAS_HTTP_ENDPOINT_METADATA); + + if (e instanceof EIDASException) { + eIDASRespBuilder.statusCode(((EIDASException) e).getStatusCodeFirstLevel()); + eIDASRespBuilder.subStatusCode(((EIDASException) e).getStatusCodeSecondLevel()); + eIDASRespBuilder.statusMessage(e.getMessage()); } else if (e instanceof MOAIDException ) { - eIDASResp.setStatusCode(StatusCode.RESPONDER_URI); - eIDASResp.setSubStatusCode(StatusCode.AUTHN_FAILED_URI); - eIDASResp.setMessage(e.getMessage()); + eIDASRespBuilder.statusCode(StatusCode.RESPONDER_URI); + eIDASRespBuilder.subStatusCode(StatusCode.AUTHN_FAILED_URI); + eIDASRespBuilder.statusMessage(e.getMessage()); } else { - eIDASResp.setStatusCode(StatusCode.RESPONDER_URI); - eIDASResp.setSubStatusCode(StatusCode.AUTHN_FAILED_URI); - eIDASResp.setMessage(e.getMessage()); + eIDASRespBuilder.statusCode(StatusCode.RESPONDER_URI); + eIDASRespBuilder.subStatusCode(StatusCode.AUTHN_FAILED_URI); + eIDASRespBuilder.statusMessage(e.getMessage()); } - - - EIDASSAMLEngine engine = SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); - + if(null == eidasReq.getEidasRequest().getAssertionConsumerServiceURL()) { - String assertionConsumerUrl = MetadataUtil.getAssertionUrlFromMetadata( - new MOAeIDASMetadataProviderDecorator(eIDASMetadataProvider), - engine, + String assertionConsumerUrl = MetadataUtil.getAssertionConsumerUrlFromMetadata( + SAMLEngineUtils.getMetadataFetcher(), + SAMLEngineUtils.getMetadataSigner(), eidasReq.getEidasRequest()); - eidasReq.getEidasRequest().setAssertionConsumerServiceURL(assertionConsumerUrl); - } + //TODO: set AssertionConsumerService is required? + + } + + eIDASRespBuilder.id(eu.eidas.auth.engine.xml.opensaml.SAMLEngineUtils.generateNCName()); + eIDASRespBuilder.inResponseTo(eidasReq.getEidasRequest().getId()); + + //build response + AuthenticationResponse eIDASResp = eIDASRespBuilder.build(); + //get eIDAS SAML-engine + ProtocolEngineI engine = SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); + + //build response message + IResponseMessage eIDASRespMsg = engine.generateResponseErrorMessage(eidasReq.getEidasRequest(),eIDASResp, eidasReq.getRemoteAddress()); - eIDASResp = engine.generateEIDASAuthnResponseFail(eidasReq.getEidasRequest(), eIDASResp, - eidasReq.getRemoteAddress(), true); - String token = EIDASUtil.encodeSAMLToken(eIDASResp.getTokenSaml()); + String token = EidasStringUtil.encodeToBase64(eIDASRespMsg.getMessageBytes()); VelocityEngine velocityEngine = VelocityProvider.getClassPathVelocityEngine(); Template template = velocityEngine.getTemplate("/resources/templates/stork2_postbinding_template.html"); diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EidasMetaDataRequest.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EidasMetaDataRequest.java index 3fc13406c..a93dff3b3 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EidasMetaDataRequest.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EidasMetaDataRequest.java @@ -21,6 +21,7 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.opensaml.common.xml.SAMLConstants; import org.opensaml.saml2.metadata.ContactPerson; import org.opensaml.saml2.metadata.Organization; import org.springframework.beans.factory.annotation.Autowired; @@ -39,11 +40,11 @@ import at.gv.egovernment.moa.id.data.SLOInformationInterface; import at.gv.egovernment.moa.id.moduls.IAction; import at.gv.egovernment.moa.id.protocols.pvp2x.config.PVPConfiguration; import at.gv.egovernment.moa.logging.Logger; -import eu.eidas.auth.engine.EIDASSAMLEngine; +import eu.eidas.auth.engine.ProtocolEngineI; import eu.eidas.auth.engine.metadata.Contact; import eu.eidas.auth.engine.metadata.MetadataConfigParams; import eu.eidas.auth.engine.metadata.MetadataGenerator; -import eu.eidas.engine.exceptions.SAMLEngineException; +import eu.eidas.engine.exceptions.EIDASSAMLEngineException; /** @@ -65,10 +66,10 @@ public class EidasMetaDataRequest implements IAction { try { String pubURLPrefix = req.getAuthURL(); - String metadata_url = pubURLPrefix + Constants.eIDAS_HTTP_ENDPOINT_METADATA; + String metadata_url = pubURLPrefix + Constants.eIDAS_HTTP_ENDPOINT_METADATA; + String sp_return_url = pubURLPrefix + Constants.eIDAS_HTTP_ENDPOINT_SP_POST; - String sp_return_url = pubURLPrefix + Constants.eIDAS_HTTP_ENDPOINT_SP_POST; - String metaData = generateMetadata(metadata_url, sp_return_url); + String metaData = generateMetadata(req, metadata_url, sp_return_url); Logger.trace(metaData); @@ -104,10 +105,10 @@ public class EidasMetaDataRequest implements IAction { } - public String generateMetadata(String metadata_url, String sp_return_url) throws SAMLEngineException, EIDASEngineException{ + public String generateMetadata(IRequest pendingReq, String metadata_url, String sp_return_url) throws EIDASSAMLEngineException, EIDASEngineException{ String metadata="invalid metadata"; - EIDASSAMLEngine engine = SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); + ProtocolEngineI engine = SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); MetadataGenerator generator = new MetadataGenerator(); MetadataConfigParams mcp=new MetadataConfigParams(); @@ -116,6 +117,9 @@ public class EidasMetaDataRequest implements IAction { mcp.setEntityID(metadata_url); mcp.setAssertionConsumerUrl(sp_return_url); + mcp.getProtocolBindingLocation().put( + SAMLConstants.SAML2_POST_BINDING_URI, + pendingReq.getAuthURL() + Constants.eIDAS_HTTP_ENDPOINT_IDP_COLLEAGUEREQUEST); //TODO: make it configurable @@ -137,11 +141,17 @@ public class EidasMetaDataRequest implements IAction { Contact technicalContact = new Contact(); List<ContactPerson> contacts = PVPConfiguration.getInstance().getIDPContacts(); - if (contacts != null && contacts.size() >= 1) { - technicalContact.setEmail(contacts.get(0).getEmailAddresses().get(0).getAddress()); - technicalContact.setGivenName(contacts.get(0).getGivenName().getName()); - technicalContact.setSurName(contacts.get(0).getSurName().getName()); - technicalContact.setPhone(contacts.get(0).getTelephoneNumbers().get(0).getNumber()); + if (contacts != null && contacts.size() >= 1) { + ContactPerson contact = contacts.get(0); + technicalContact.setGivenName(contact.getGivenName().getName()); + technicalContact.setSurName(contact.getSurName().getName()); + + if (!contact.getEmailAddresses().isEmpty()) + technicalContact.setEmail(contact.getEmailAddresses().get(0).getAddress()); + + if (!contact.getTelephoneNumbers().isEmpty()) + technicalContact.setPhone(contact.getTelephoneNumbers().get(0).getNumber()); + mcp.setTechnicalContact(technicalContact ); } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/eIDASAuthenticationRequest.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/eIDASAuthenticationRequest.java index 2beb419fb..9e29c01f3 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/eIDASAuthenticationRequest.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/eIDASAuthenticationRequest.java @@ -24,7 +24,6 @@ package at.gv.egovernment.moa.id.protocols.eidas; import java.io.StringWriter; import java.text.SimpleDateFormat; -import java.util.Map.Entry; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -36,13 +35,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; +import com.google.common.collect.ImmutableSet; + import at.gv.egovernment.moa.id.advancedlogging.MOAReversionLogger; import at.gv.egovernment.moa.id.auth.frontend.velocity.VelocityProvider; import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; import at.gv.egovernment.moa.id.auth.modules.eidas.engine.MOAeIDASChainingMetadataProvider; -import at.gv.egovernment.moa.id.auth.modules.eidas.engine.MOAeIDASMetadataProviderDecorator; -import at.gv.egovernment.moa.id.auth.modules.eidas.utils.MOAPersonalAttributeList; -import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils; import at.gv.egovernment.moa.id.commons.api.IRequest; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.data.IAuthData; @@ -50,12 +48,16 @@ import at.gv.egovernment.moa.id.data.SLOInformationInterface; import at.gv.egovernment.moa.id.moduls.IAction; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.MiscUtil; -import eu.eidas.auth.commons.EIDASAuthnResponse; -import eu.eidas.auth.commons.EIDASStatusCode; -import eu.eidas.auth.commons.EIDASUtil; -import eu.eidas.auth.commons.PersonalAttribute; -import eu.eidas.auth.engine.EIDASSAMLEngine; -import eu.eidas.auth.engine.metadata.MetadataUtil; +import eu.eidas.auth.commons.EidasStringUtil; +import eu.eidas.auth.commons.attribute.AttributeDefinition; +import eu.eidas.auth.commons.attribute.AttributeValue; +import eu.eidas.auth.commons.attribute.AttributeValueMarshaller; +import eu.eidas.auth.commons.attribute.AttributeValueMarshallingException; +import eu.eidas.auth.commons.attribute.ImmutableAttributeMap; +import eu.eidas.auth.commons.protocol.IResponseMessage; +import eu.eidas.auth.commons.protocol.impl.AuthenticationResponse; +import eu.eidas.auth.engine.ProtocolEngineI; +import eu.eidas.auth.engine.xml.opensaml.SAMLEngineUtils; /** @@ -80,14 +82,16 @@ public class eIDASAuthenticationRequest implements IAction { throw new MOAIDException("got wrong IRequest type. is: {}, should be: {}", new String[] {req.getClass().toString(), EIDASData.class.toString()}); - // gather attributes - MOAPersonalAttributeList resultingAttributeList = (MOAPersonalAttributeList) eidasRequest.getEidasRequestedAttributes().clone(); + //gather attributes + ImmutableAttributeMap reqAttributeList = (ImmutableAttributeMap) eidasRequest.getEidasRequestedAttributes(); + + ImmutableAttributeMap.Builder attrMapBuilder = ImmutableAttributeMap.builder(); - for(Entry<String, PersonalAttribute> current : resultingAttributeList.entrySet()) { + for(AttributeDefinition<?> attr : reqAttributeList.getDefinitions()) { String newValue = ""; - + // TODO make use of proper builder - switch(current.getKey()) { + switch(attr.getFriendlyName()) { case Constants.eIDAS_ATTR_DATEOFBIRTH: newValue = new SimpleDateFormat("YYYY-MM-dd").format(authData.getDateOfBirth()); break; case Constants.eIDAS_ATTR_CURRENTFAMILYNAME: newValue = authData.getFamilyName();break; case Constants.eIDAS_ATTR_CURRENTGIVENNAME: newValue = authData.getGivenName();break; @@ -95,50 +99,75 @@ public class eIDASAuthenticationRequest implements IAction { } - if(MiscUtil.isEmpty(newValue)) - current.getValue().setStatus(EIDASStatusCode.STATUS_NOT_AVAILABLE.toString()); - - else { - current.getValue().getValue().clear(); - current.getValue().getValue().add(newValue); - current.getValue().setStatus(EIDASStatusCode.STATUS_AVAILABLE.toString()); + if(MiscUtil.isEmpty(newValue)) { +// current.getValue().setStatus(EIDASStatusCode.STATUS_NOT_AVAILABLE.toString()); + Logger.info("eIDAS Attr:" + attr.getNameUri() + " is not available."); + + } else { + AttributeValueMarshaller<?> attributeValueMarshaller = attr.getAttributeValueMarshaller(); + ImmutableSet.Builder<AttributeValue<?>> builder = ImmutableSet.builder(); + + AttributeValue<?> attributeValue = null; + try { + attributeValue = attributeValueMarshaller.unmarshal(newValue, false); + + } catch (AttributeValueMarshallingException e) { + throw new IllegalStateException(e); + + } + + //TODO: check for 'isAvailable flag'! + + attrMapBuilder.put((AttributeDefinition)attr, (ImmutableSet) builder.build()); } + + + } // construct eIDaS response - EIDASAuthnResponse response = new EIDASAuthnResponse(); - response.setPersonalAttributeList(resultingAttributeList); + AuthenticationResponse.Builder responseBuilder = new AuthenticationResponse.Builder(); - // - create metadata url - String pubURLPrefix = req.getAuthURL(); + responseBuilder.id(SAMLEngineUtils.generateNCName()); + responseBuilder.inResponseTo(eidasRequest.getEidasRequest().getId()); + + String pubURLPrefix = req.getAuthURL(); String metadata_url = pubURLPrefix + Constants.eIDAS_HTTP_ENDPOINT_METADATA; - response.setIssuer(metadata_url); - - response.setAssuranceLevel(authData.getEIDASQAALevel()); + responseBuilder.issuer(metadata_url); + + responseBuilder.levelOfAssurance(authData.getEIDASQAALevel()); + + //add attributes + responseBuilder.attributes(attrMapBuilder.build()); + + //build response + AuthenticationResponse response = responseBuilder.build(); String token = null; try { - EIDASSAMLEngine engine = SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); + ProtocolEngineI engine = at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); // encryption is done by the SamlEngine, i.e. by the module we provide in the config // but we need to set the appropriate request issuer - engine.setRequestIssuer(eidasRequest.getEidasRequest().getIssuer()); - - - if(null == eidasRequest.getEidasRequest().getAssertionConsumerServiceURL()) { - String assertionConsumerUrl = MetadataUtil.getAssertionUrlFromMetadata( - new MOAeIDASMetadataProviderDecorator(eIDASMetadataProvider), - engine, - eidasRequest.getEidasRequest()); - eidasRequest.getEidasRequest().setAssertionConsumerServiceURL(assertionConsumerUrl); - - } + //engine.setRequestIssuer(eidasRequest.getEidasRequest().getIssuer()); + + IResponseMessage eIDASRespMsg = engine.generateResponseMessage(eidasRequest.getEidasRequest(), + response, true, eidasRequest.getRemoteAddress()); + +// if(null == eidasRequest.getEidasRequest().getAssertionConsumerServiceURL()) { +// String assertionConsumerUrl = MetadataUtil.getAssertionUrlFromMetadata( +// new MOAeIDASMetadataProviderDecorator(eIDASMetadataProvider), +// engine, +// eidasRequest.getEidasRequest()); +// eidasRequest.getEidasRequest().setAssertionConsumerServiceURL(assertionConsumerUrl); +// +// } - response = engine.generateEIDASAuthnResponse(eidasRequest.getEidasRequest(), response, eidasRequest.getRemoteAddress(), true); +// response = engine.generateEIDASAuthnResponse(eidasRequest.getEidasRequest(), response, eidasRequest.getRemoteAddress(), true); - token = EIDASUtil.encodeSAMLToken(response.getTokenSaml()); + token = EidasStringUtil.encodeToBase64(eIDASRespMsg.getMessageBytes()); } catch(Exception e) { e.printStackTrace(); |