/******************************************************************************* * 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.configuration.config; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Arrays; import java.util.Properties; import java.util.Timer; import java.util.jar.Attributes; import java.util.jar.Manifest; import javax.servlet.http.HttpServletRequest; import org.apache.commons.httpclient.MOAHttpClient; import org.apache.log4j.Logger; import org.opensaml.DefaultBootstrap; import org.opensaml.saml2.metadata.provider.HTTPMetadataProvider; import org.opensaml.xml.parse.BasicParserPool; import org.opensaml.xml.security.x509.BasicX509Credential; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.GenericApplicationContext; import at.gv.egiz.eaaf.core.impl.utils.FileUtils; import at.gv.egovernment.moa.id.commons.db.NewConfigurationDBRead; import at.gv.egovernment.moa.id.commons.ex.MOAHttpProtocolSocketFactoryException; import at.gv.egovernment.moa.id.commons.utils.MOAHttpProtocolSocketFactory; import at.gv.egovernment.moa.id.config.webgui.MOAIDConfigurationModul; import at.gv.egovernment.moa.id.config.webgui.exception.ConfigurationException; import at.gv.egovernment.moa.id.configuration.Constants; import at.gv.egovernment.moa.id.configuration.auth.pvp2.MetaDataVerificationFilter; import at.gv.egovernment.moa.id.configuration.config.usermanagement.FileBasedUserConfiguration; import at.gv.egovernment.moa.id.configuration.utils.UserRequestCleaner; import at.gv.egovernment.moa.util.MiscUtil; import iaik.asn1.structures.AlgorithmID; import iaik.x509.X509Certificate; public class ConfigurationProvider { public static final String HTMLTEMPLATE_DIR = "/htmlTemplates"; public static final String HTMLTEMPLATE_FILE = "/loginFormFull.html"; private static final Logger log = Logger.getLogger(ConfigurationProvider.class); private static final String SYSTEM_PROP_CONFIG = "moa.id.webconfig"; private static ConfigurationProvider instance; private Properties props; private String configFileName; private String configRootDir; private HTTPMetadataProvider idpMetadataProvider = null; private KeyStore keyStore = null; private String publicURLPreFix = null; private boolean pvp2logininitialzied = false; private ClassPathXmlApplicationContext context = null; private MOAIDConfigurationModul configModule = null; private NewConfigurationDBRead deprecatedDBRead = null; private FileBasedUserConfiguration userManagement = null; private ArrayList activeProfiles = new ArrayList(); public static ConfigurationProvider getInstance() throws ConfigurationException { if (instance == null) { instance = new ConfigurationProvider(); instance.inizialize(); } return instance; } private void inizialize() throws ConfigurationException { log.info("Set SystemProperty for UTF-8 file.encoding as default"); System.setProperty("file.encoding", "UTF-8"); configFileName = System.getProperty(SYSTEM_PROP_CONFIG); if (configFileName == null) { throw new ConfigurationException("config.05"); } try { URI fileURI = new URI(configFileName); File propertiesFile = new File(fileURI); // determine the directory of the root config file String rootConfigFileDir = propertiesFile.getParent(); configRootDir = new File(rootConfigFileDir).toURI().toURL().toString();; log.info("Loading MOA-ID-AUTH configuration " + configFileName); //Initial Hibernate Framework log.trace("Initializing Hibernate framework."); //Load MOAID-2.0 properties file FileInputStream fis; props = new Properties(); fis = new FileInputStream(propertiesFile); props.load(fis); fis.close(); // //Workaround -> can be removed in next version // if (MiscUtil.isEmpty(System.getProperty("spring.profiles.active"))) { // log.info("Set System-Property to activate 'byteBased' config values"); // System.setProperty("spring.profiles.active", "byteBasedConfig"); // // } //initialize generic SpringContext to set profiles GenericApplicationContext rootContext = new GenericApplicationContext(); // if (Boolean.valueOf(props.getProperty("configuration.database.byteBasedValues", "false"))) // activeProfiles.add(SpringProfileConstants.BYTEBASEDCONFIG); // for (String el: activeProfiles) // rootContext.getEnvironment().addActiveProfile(el); //refresh generic context rootContext.refresh(); //initialize SpringContext context = new ClassPathXmlApplicationContext( new String[] { "configuration.beans.xml", "moaid.webgui.beans.xml", "moaid.migration.beans.xml", "moaid.configurationtool.beans.xml" }, rootContext); log.info("Spring-context was initialized with active profiles: " + Arrays.asList(context.getEnvironment().getActiveProfiles())); //Autowire beans in these context AutowireCapableBeanFactory acbFactory = context.getAutowireCapableBeanFactory(); acbFactory.autowireBean(this); log.info("Hibernate initialization finished."); DefaultBootstrap.bootstrap(); log.info("OPENSAML initialized"); UserRequestCleaner.start(); fixJava8_141ProblemWithSSLAlgorithms(); log.info("MOA-ID-Configuration initialization completed"); } catch (FileNotFoundException e) { throw new ConfigurationException("config.01", new Object[]{configFileName}, e); } catch (IOException e) { throw new ConfigurationException("config.02", new Object[]{configFileName}, e); } catch (org.opensaml.xml.ConfigurationException e) { throw new ConfigurationException("config.04", e); } catch (URISyntaxException e) { throw new ConfigurationException("config.01", new Object[]{configFileName}, e); } } private static void fixJava8_141ProblemWithSSLAlgorithms() { log.info("Change AlgorithmIDs to fix problems with Java8 >= 141 ..."); //new AlgorithmID("1.2.840.113549.1.1.4", "md5WithRSAEncryption", new String[] { "MD5withRSA", "MD5/RSA", }, null, true); new AlgorithmID("1.2.840.113549.1.1.5", "sha1WithRSAEncryption", new String[] { "SHA1withRSA" , "SHA1/RSA", "SHA-1/RSA", "SHA/RSA", }, null, true); new AlgorithmID("1.2.840.113549.1.1.14", "sha224WithRSAEncryption", new String[] { "SHA224withRSA", "SHA224/RSA", "SHA-224/RSA", }, null, true); new AlgorithmID("1.2.840.113549.1.1.11", "sha256WithRSAEncryption", new String[] { "SHA256withRSA", "SHA256/RSA", "SHA-256/RSA", }, null, true); new AlgorithmID("1.2.840.113549.1.1.12", "sha384WithRSAEncryption", new String[] { "SHA384withRSA", "SHA384/RSA", "SHA-384/RSA", }, null, true); new AlgorithmID("1.2.840.113549.1.1.13", "sha512WithRSAEncryption", new String[] { "SHA512withRSA", "SHA512/RSA", "SHA-512/RSA" }, null, true); log.info("Change AlgorithmIDs finished"); } @Autowired(required = true) public void setMOAIDConfigurationModul(MOAIDConfigurationModul module) { this.configModule = module; } /** * @param dbRead the dbRead to set */ @Autowired(required = true) public void setDbRead(NewConfigurationDBRead dbRead) { this.deprecatedDBRead = dbRead; } /** * @return the props */ public Properties getConfigurationProperties() { return props; } /** * @return the deprecatedDBWrite */ public FileBasedUserConfiguration getUserManagement() { return userManagement; } /** * @param deprecatedDBWrite the deprecatedDBWrite to set */ @Autowired(required = true) public void setUserManagement(FileBasedUserConfiguration userManagement) { this.userManagement = userManagement; } public String getPublicUrlPreFix(HttpServletRequest request) { publicURLPreFix = props.getProperty("general.publicURLContext"); if (MiscUtil.isEmpty(publicURLPreFix) && request != null) { String url = request.getRequestURL().toString(); String contextpath = request.getContextPath(); int index = url.indexOf(contextpath); publicURLPreFix = url.substring(0, index + contextpath.length() + 1); } return publicURLPreFix; } public int getUserRequestCleanUpDelay() { String delay = props.getProperty("general.userrequests.cleanup.delay"); return Integer.getInteger(delay, 12); } // public String getContactMailAddress() { // return props.getProperty("general.contact.mail"); // } public String getSSOLogOutURL() { return props.getProperty("general.login.pvp2.idp.sso.logout.url"); } public KeyStore getPVP2KeyStore() throws ConfigurationException, IOException, NoSuchAlgorithmException, CertificateException, KeyStoreException { if (keyStore == null) { String keystoretype = getPVP2MetadataKeystoreType(); if (MiscUtil.isEmpty(keystoretype)) { log.debug("No KeyStoreType defined. Using default KeyStoreType."); keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); } else { log.debug("Using " + keystoretype + " KeyStoreType."); keyStore = KeyStore.getInstance(keystoretype); } String fileURL = getPVP2MetadataKeystoreURL(); log.debug("Load KeyStore from URL " + fileURL); if (MiscUtil.isEmpty(fileURL)) { log.info("Metadata KeyStoreURL is empty"); throw new ConfigurationException("Metadata KeyStoreURL is empty"); } URL keystoreURL = new URL((FileUtils.makeAbsoluteURL(fileURL, getConfigRootDir()))); InputStream inputStream = keystoreURL.openStream(); keyStore.load(inputStream, getPVP2MetadataKeystorePassword().toCharArray()); inputStream.close(); } return keyStore; } public String getConfigFile() { return configFileName; } public String getConfigRootDir() { return configRootDir; } public String getMOAIDInstanceURL() { return props.getProperty("general.moaid.instance.url"); } public boolean isLoginDeaktivated() { String result = props.getProperty("general.login.deaktivate", "false"); return Boolean.parseBoolean(result); } public boolean isOATargetVerificationDeaktivated() { String result = props.getProperty("general.OATargetVerification.deaktivate", "false"); return Boolean.parseBoolean(result); } //PVP2 Login configuration public void initializePVP2Login() throws ConfigurationException { if (!pvp2logininitialzied) initalPVP2Login(); } public boolean isPVP2LoginActive() { return Boolean.parseBoolean(props.getProperty("general.login.pvp2.isactive", "false")); } public boolean isPVP2LoginBusinessService() { String result = props.getProperty("general.login.pvp2.isbusinessservice", "false"); return Boolean.parseBoolean(result); } public String getPVP2LoginTarget() { return props.getProperty("general.login.pvp2.target"); } public String getPVP2LoginIdenificationValue() { return props.getProperty("general.login.pvp2.identificationvalue"); } public String getPVP2MetadataEntitiesName() { return props.getProperty("general.login.pvp2.metadata.entities.name"); } public String getPVP2MetadataKeystoreURL() { return props.getProperty("general.login.pvp2.keystore.url"); } public String getPVP2MetadataKeystorePassword() { return props.getProperty("general.login.pvp2.keystore.password"); } public String getPVP2MetadataKeystoreType() { return props.getProperty("general.login.pvp2.keystore.type"); } public String getPVP2KeystoreMetadataKeyAlias() { return props.getProperty("general.login.pvp2.keystore.metadata.key.alias"); } public String getPVP2KeystoreMetadataKeyPassword() { return props.getProperty("general.login.pvp2.keystore.metadata.key.password"); } public String getPVP2KeystoreAuthRequestKeyAlias() { return props.getProperty("general.login.pvp2.keystore.authrequest.key.alias"); } public String getPVP2KeystoreAuthRequestKeyPassword() { return props.getProperty("general.login.pvp2.keystore.authrequest.key.password"); } public String getPVP2KeystoreAuthRequestEncryptionKeyAlias() { return props.getProperty("general.login.pvp2.keystore.authrequest.encryption.key.alias"); } public String getPVP2KeystoreAuthRequestEncryptionKeyPassword() { return props.getProperty("general.login.pvp2.keystore.authrequest.encryption.key.password"); } public String getPVP2IDPMetadataURL() { return props.getProperty("general.login.pvp2.idp.metadata.url"); } public String getPVP2IDPMetadataCertificate() { return props.getProperty("general.login.pvp2.idp.metadata.certificate"); } public String getPVP2IDPMetadataEntityName() { return props.getProperty("general.login.pvp2.idp.metadata.entityID"); } public HTTPMetadataProvider getMetaDataProvier() { return idpMetadataProvider; } //SMTP Server public String getSMTPMailHost() { return props.getProperty("general.mail.host"); } public String getSMTPMailPort() { return props.getProperty("general.mail.host.port"); } public String getSMTPMailUsername() { return props.getProperty("general.mail.host.username"); } public String getSMTPMailPassword() { return props.getProperty("general.mail.host.password"); } //Mail Configuration public String getMailFromName() { return props.getProperty("general.mail.from.name"); } public String getMailFromAddress() { return props.getProperty("general.mail.from.address"); } public String getMailUserAcountVerificationSubject() { return props.getProperty("general.mail.useraccountrequest.verification.subject"); } public String getMailUserAcountVerificationTemplate() throws ConfigurationException { String url = props.getProperty("general.mail.useraccountrequest.verification.template"); if (MiscUtil.isNotEmpty(url)) { return url; } else { log.warn("MailUserAcountVerificationTemplate is empty"); throw new ConfigurationException("MailUserAcountVerificationTemplate is empty"); } } public String getMailUserAcountActivationSubject() { return props.getProperty("general.mail.useraccountrequest.isactive.subject"); } public String getMailUserAcountActivationTemplate() throws ConfigurationException { String url = props.getProperty("general.mail.useraccountrequest.isactive.template"); if (MiscUtil.isNotEmpty(url)) { return url; } else { log.warn("MailUserAcountActivationTemplate is empty"); throw new ConfigurationException("MailUserAcountActivationTemplate is empty"); } } public String getMailOAActivationSubject() { return props.getProperty("general.mail.createOArequest.isactive.subject"); } public String getDefaultLanguage() { try { return props.getProperty("general.defaultlanguage", "de").toLowerCase(); } catch (Exception ex) { return "de"; } } public String getMailOAActivationTemplate() throws ConfigurationException { String url = props.getProperty("general.mail.createOArequest.isactive.template"); if (MiscUtil.isNotEmpty(url)) { return url; } else { log.warn("MailOAActivationTemplate is empty"); throw new ConfigurationException("MailOAActivationTemplate is empty"); } } public String getMailUserAcountRevocationTemplate() throws ConfigurationException { String url = props.getProperty("general.mail.useraccountrequest.rejected.template"); if (MiscUtil.isNotEmpty(url)) { return url; } else { log.warn("MailUserAcountVerificationTemplate is empty"); throw new ConfigurationException("MailUserAcountRevocationTemplate is empty"); } } public String getMailAdminSubject() { return props.getProperty("general.mail.admin.subject"); } public String getMailAdminTemplate() throws ConfigurationException { String url = props.getProperty("general.mail.admin.adresses.template"); if (MiscUtil.isNotEmpty(url)) { return url; } else { log.warn("MailUserAcountVerificationTemplate is empty"); throw new ConfigurationException("MailAdminTemplate is empty"); } } public String getMailAdminAddress() { return props.getProperty("general.mail.admin.adress"); } public String getConfigToolVersion() { return parseVersionFromManifest(); } public String getCertStoreDirectory() throws ConfigurationException { String dir = props.getProperty("general.ssl.certstore"); if (MiscUtil.isNotEmpty(dir)) return FileUtils.makeAbsoluteURL(dir, configRootDir); else throw new ConfigurationException("No SSLCertStore configured use default JAVA TrustStore."); } public String getTrustStoreDirectory() throws ConfigurationException { String dir = props.getProperty("general.ssl.truststore"); if (MiscUtil.isNotEmpty(dir)) return FileUtils.makeAbsoluteURL(dir, configRootDir); else throw new ConfigurationException("No SSLTrustStore configured use default JAVA TrustStore."); } public String getConfigurationEncryptionKey() { return props.getProperty("general.moaconfig.key"); } public boolean isPVPMetadataSchemaValidationActive() { return Boolean.parseBoolean(props.getProperty("general.pvp.schemavalidation", "true")); } /** * @return */ private boolean isHostNameValidationEnabled() { return Boolean.parseBoolean(props.getProperty("general.ssl.hostnamevalidation", "true")); } /** * @return the context */ public ApplicationContext getContext() { return context; } /** * @return the configModule */ public MOAIDConfigurationModul getConfigModule() { return configModule; } /** * @return the dbRead */ public NewConfigurationDBRead getDbRead() { return deprecatedDBRead; } private void initalPVP2Login() throws ConfigurationException { try { String metadataCert = getPVP2IDPMetadataCertificate(); if (MiscUtil.isEmpty(metadataCert)) { log.info("NO IDP Certificate to verify IDP Metadata"); throw new ConfigurationException("NO IDP Certificate to verify IDP Metadata"); } URL keystoreURL = new URL((FileUtils.makeAbsoluteURL(metadataCert, getConfigRootDir()))); InputStream certstream = keystoreURL.openStream(); X509Certificate cert = new X509Certificate(certstream); BasicX509Credential idpCredential = new BasicX509Credential(); idpCredential.setEntityCertificate(cert); log.debug("IDP Certificate loading finished"); String metadataurl = getPVP2IDPMetadataURL(); if (MiscUtil.isEmpty(metadataurl)) { log.info("NO IDP Metadata URL."); throw new ConfigurationException("NO IDP Metadata URL."); } MOAHttpClient httpClient = new MOAHttpClient(); if (metadataurl.startsWith("https:")) { try { MOAHttpProtocolSocketFactory protoSocketFactory = new MOAHttpProtocolSocketFactory( "MOAMetaDataProvider", true, ConfigurationProvider.getInstance().getCertStoreDirectory(), ConfigurationProvider.getInstance().getTrustStoreDirectory(), null, "pkix", true, new String[]{"crl"}, ConfigurationProvider.getInstance().isHostNameValidationEnabled()); httpClient.setCustomSSLTrustStore(metadataurl, protoSocketFactory); } catch (MOAHttpProtocolSocketFactoryException e) { log.warn("MOA SSL-TrustStore can not initialized. Use default Java TrustStore."); } } idpMetadataProvider = new HTTPMetadataProvider(new Timer(true), httpClient, metadataurl); idpMetadataProvider.setRequireValidMetadata(true); idpMetadataProvider.setParserPool(new BasicParserPool()); idpMetadataProvider.setMetadataFilter(new MetaDataVerificationFilter(idpCredential)); idpMetadataProvider.setMaxRefreshDelay(1000 * 3600 * 12 ); //refresh Metadata every 12h idpMetadataProvider.initialize(); pvp2logininitialzied = true; } catch (Exception e) { log.warn("PVP2 authentification can not be initialized."); throw new ConfigurationException("error.initialization.pvplogin", e); } } private String parseVersionFromManifest() { try { Class clazz = ConfigurationProvider.class; String className = clazz.getSimpleName() + ".class"; String classPath = clazz.getResource(className).toString(); if (classPath.startsWith("jar")) { log.info("MOA-ID-Configuration Version can NOT parsed from Manifest. Set blank Version"); return Constants.DEFAULT_VERSION; } String manifestPath = classPath.substring(0, classPath.lastIndexOf("WEB-INF/classes/") + "WEB-INF/classes/".length()) + "../../META-INF/MANIFEST.MF"; Manifest manifest = new Manifest(new URL(manifestPath).openStream());; Attributes attributes = manifest.getMainAttributes(); String version = attributes.getValue("version"); if (MiscUtil.isNotEmpty(version)) return version; else { log.info("MOA-ID-Configuration Version not found in Manifest. Set blank Version"); return Constants.DEFAULT_VERSION; } } catch (Throwable e) { log.info("MOA-ID Version can NOT parsed from Manifest. Set blank Version"); return Constants.DEFAULT_VERSION; } } }