diff options
Diffstat (limited to 'eaaf_core_utils/src/main/java/at/gv')
28 files changed, 2856 insertions, 2059 deletions
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/exception/EaafKeyAccessException.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/exception/EaafKeyAccessException.java new file mode 100644 index 00000000..f9abd6d9 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/exception/EaafKeyAccessException.java @@ -0,0 +1,22 @@ +package at.gv.egiz.eaaf.core.exception; + +import at.gv.egiz.eaaf.core.exceptions.EaafException; + +public class EaafKeyAccessException extends EaafException { + + private static final long serialVersionUID = -2641273589744430903L; + + public static final String ERROR_CODE_08 = "internal.keystore.08"; + public static final String ERROR_CODE_09 = "internal.keystore.09"; + + public EaafKeyAccessException(String errorCode, String... params) { + super(errorCode, new Object[] {params}); + + } + + public EaafKeyAccessException(String errorCode, Throwable e, String... params) { + super(errorCode, new Object[] {params}, e); + + } + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreFactory.java new file mode 100644 index 00000000..e60c326c --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreFactory.java @@ -0,0 +1,263 @@ +package at.gv.egiz.eaaf.core.impl.credential; + +import java.io.IOException; +import java.io.InputStream; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.Security; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.PostConstruct; + +import at.asitplus.hsmfacade.provider.HsmFacadeProvider; +import at.asitplus.hsmfacade.provider.RemoteKeyStoreLoadParameter; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.exceptions.EaafFactoryException; +import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.impl.utils.FileUtils; +import at.gv.egiz.eaaf.core.impl.utils.KeyStoreUtils; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class EaafKeyStoreFactory { + + public static final String CONFIG_PROP_HSM_FACADE_HOST = "security.hsmfacade.host"; + public static final String CONFIG_PROP_HSM_FACADE_PORT = "security.hsmfacade.port"; + public static final String CONFIG_PROP_HSM_FACADE_SSLTRUST = "security.hsmfacade.trustedsslcert"; + public static final String CONFIG_PROP_HSM_FACADE_CLIENT_USERNAME = "security.hsmfacade.username"; + public static final String CONFIG_PROP_HSM_FACADE_CLIENT_PASSWORD = "security.hsmfacade.password"; + + public static final String ERRORCODE_00 = "internal.keystore.00"; + public static final String ERRORCODE_01 = "internal.keystore.01"; + public static final String ERRORCODE_02 = "internal.keystore.02"; + public static final String ERRORCODE_03 = "internal.keystore.03"; + public static final String ERRORCODE_04 = "internal.keystore.04"; + public static final String ERRORCODE_05 = "internal.keystore.05"; + public static final String ERRORCODE_06 = "internal.keystore.06"; + public static final String ERRORCODE_07 = "internal.keystore.07"; + + private static final String HSM_FACADE_PROVIDER = "HsmFacade"; + private static final String HSM_FACADE_KEYSTORE_TYPE = "RemoteKeyStore"; + + @Autowired + private IConfiguration basicConfig; + @Autowired + private ResourceLoader resourceLoader; + + private boolean isHsmFacadeInitialized = false; + + /** + * Get a new KeyStore based on a KeyStore configuration-object. + * + * @param config KeyStore configuration + * @return {@link Pair} of a new {@link KeyStore} instance and an optional {@link Provider}. + * The {@link KeyStore} is {@link Nonnull}. If the {@link Provider} is not <code>null</code> + * this {@link KeyStore} requires a specific {@link Provider} for {@link Key} operations. + * @throws EaafException In case of a KeyStore initialization error + */ + @Nonnull + public Pair<KeyStore, Provider> buildNewKeyStore(KeyStoreConfiguration config) throws EaafException { + log.trace("Starting KeyStore generation based on configuration object ... "); + if (KeyStoreType.PKCS12.equals(config.getKeyStoreType()) + || KeyStoreType.JKS.equals(config.getKeyStoreType())) { + return getKeyStoreFromFileSystem(config); + + } else if (KeyStoreType.HSMFACADE.equals(config.getKeyStoreType())) { + if (isHsmFacadeInitialized) { + return getKeyStoreFromHsmFacade(config); + + } else { + log.error("HSMFacade can NOT be used for KeyStore: {} because {} is not initialized", + config.getFriendlyName()); + throw new EaafConfigurationException(ERRORCODE_00, + new Object[] { config.getFriendlyName() }); + + } + + } else if (KeyStoreType.PKCS11.equals(config.getKeyStoreType())) { + log.warn("KeyStoreType: {} is NOT supported", config.getKeyStoreType()); + throw new EaafConfigurationException(ERRORCODE_02, + new Object[] { config.getFriendlyName(), config.getKeyStoreType() }); + + } else { + log.warn("KeyStoreType: {} is unrecognized", config.getKeyStoreType()); + throw new EaafConfigurationException(ERRORCODE_01, + new Object[] { config.getFriendlyName() }); + + } + } + + /** + * Get the initialization state of the HSM Facade module. + * + * @return true if HSM Facade is available, otherwise false + */ + public boolean isHsmFacadeInitialized() { + return isHsmFacadeInitialized; + + } + + @PostConstruct + private void initialize() throws EaafException { + + final String hsmFacadeHost = basicConfig.getBasicConfiguration(CONFIG_PROP_HSM_FACADE_HOST); + if (StringUtils.isNotEmpty(hsmFacadeHost)) { + log.debug("Find host for HSMFacade. Starting crypto provider initialization ... "); + try { + final int port = Integer.parseUnsignedInt( + getConfigurationParameter(CONFIG_PROP_HSM_FACADE_PORT)); + final String clientUsername = + getConfigurationParameter(CONFIG_PROP_HSM_FACADE_CLIENT_USERNAME); + final String clientPassword = + getConfigurationParameter(CONFIG_PROP_HSM_FACADE_CLIENT_PASSWORD); + + final HsmFacadeProvider provider = HsmFacadeProvider.Companion.getInstance(); + provider.init(getHsmFacadeTrustSslCertificate(), clientUsername, clientPassword, hsmFacadeHost, port); + //Security.addProvider(provider); + Security.insertProviderAt(provider, 0); + isHsmFacadeInitialized = true; + log.info("HSM Facade is initialized. {} can provide KeyStores based on remote HSM", + EaafKeyStoreFactory.class.getSimpleName()); + + } catch (final EaafException e) { + throw e; + + } catch (final Exception e) { + log.error("HSM Facade initialization FAILED with an generic error.", e); + throw new EaafConfigurationException(ERRORCODE_03, new Object[] { e.getMessage() }, e); + } + + } else { + log.info("HSM Facade is not configurated. {} can only provide software keystores", + EaafKeyStoreFactory.class.getSimpleName()); + + } + + } + + @Nonnull + private Pair<KeyStore, Provider> getKeyStoreFromFileSystem(KeyStoreConfiguration config) + throws EaafConfigurationException, EaafFactoryException { + try { + final String keyStorePath = checkConfigurationParameter(config.getSoftKeyStoreFilePath(), + ERRORCODE_06, config.getFriendlyName(), "Software-KeyStore missing filepath to KeyStore"); + + final String keyStorePassword = checkConfigurationParameter(config.getSoftKeyStorePassword(), + ERRORCODE_06, config.getFriendlyName(), "Software-KeyStore missing Password for KeyStore"); + + final String absKeyStorePath = FileUtils.makeAbsoluteUrl(keyStorePath, basicConfig + .getConfigurationRootDirectory()); + final Resource ressource = resourceLoader.getResource(absKeyStorePath); + if (!ressource.exists()) { + throw new EaafConfigurationException(ERRORCODE_05, + new Object[] { config.getFriendlyName(), + "File not found at: " + absKeyStorePath }); + + } + + final InputStream is = ressource.getInputStream(); + final KeyStore keyStore = KeyStoreUtils.loadKeyStore(is, keyStorePassword); + is.close(); + if (keyStore == null) { + throw new EaafFactoryException(ERRORCODE_06, + new Object[] { config.getFriendlyName(), "KeyStore not valid or password wrong" }); + + } + + return Pair.newInstance(keyStore, null); + + } catch (KeyStoreException | IOException e) { + log.error("Software KeyStore initialization FAILED with an generic error.", e); + throw new EaafConfigurationException(ERRORCODE_03, new Object[] { e.getMessage() }, e); + + } + } + + @Nonnull + private Pair<KeyStore, Provider> getKeyStoreFromHsmFacade(KeyStoreConfiguration config) + throws EaafFactoryException, EaafConfigurationException { + final String keyStoreName = checkConfigurationParameter(config.getKeyStoreName(), + ERRORCODE_06, config.getFriendlyName(), "KeyStoreName missing for HSM Facade"); + + try { + final KeyStore keyStore = KeyStore.getInstance(HSM_FACADE_KEYSTORE_TYPE, HSM_FACADE_PROVIDER); + keyStore.load(new RemoteKeyStoreLoadParameter(keyStoreName)); + return Pair.newInstance(keyStore, keyStore.getProvider()); + + } catch (NoSuchAlgorithmException | CertificateException | IOException | KeyStoreException + | NoSuchProviderException e) { + log.error("Can not initialize KeyStore: {} with reason: {}", + config.getFriendlyName(), e.getMessage()); + throw new EaafFactoryException(ERRORCODE_06, + new Object[] { config.getFriendlyName(), e.getMessage() }, e); + + } + } + + private X509Certificate getHsmFacadeTrustSslCertificate() throws EaafConfigurationException { + try { + final String certFilePath = getConfigurationParameter(CONFIG_PROP_HSM_FACADE_SSLTRUST); + + final String absolutCertFilePath = FileUtils.makeAbsoluteUrl( + certFilePath, basicConfig.getConfigurationRootDirectory()); + final Resource certFile = resourceLoader.getResource(absolutCertFilePath); + + if (!certFile.exists()) { + throw new EaafConfigurationException(ERRORCODE_05, + new Object[] { CONFIG_PROP_HSM_FACADE_SSLTRUST, + "File not found at: " + absolutCertFilePath }); + + } + + return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(certFile + .getInputStream()); + + } catch (final EaafConfigurationException e) { + throw e; + + } catch (CertificateException | IOException e) { + log.error("Can not load trusted server-certificate for HSM-Facade. Reason: {}", e.getMessage()); + throw new EaafConfigurationException(ERRORCODE_05, + new Object[] { CONFIG_PROP_HSM_FACADE_SSLTRUST, e.getMessage() }, e); + + } + } + + @Nonnull + private String getConfigurationParameter(@Nonnull String configParamKey) + throws EaafConfigurationException { + return checkConfigurationParameter( + basicConfig.getBasicConfiguration(configParamKey), ERRORCODE_04, configParamKey); + + } + + @Nonnull + private String checkConfigurationParameter(@Nullable String configParam, @Nonnull String errorCode, + @Nonnull String... errorParams) + throws EaafConfigurationException { + if (StringUtils.isEmpty(configParam)) { + throw new EaafConfigurationException(errorCode, new Object[] { errorParams }); + + } + return configParam; + + } + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreUtils.java new file mode 100644 index 00000000..b4b44724 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/EaafKeyStoreUtils.java @@ -0,0 +1,147 @@ +package at.gv.egiz.eaaf.core.impl.credential; + +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import at.gv.egiz.eaaf.core.exception.EaafKeyAccessException; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class EaafKeyStoreUtils { + private static final String ERROR_MSG_REASON = "Maybe 'Alias' is not valid"; + private static final String ERROR_MSG_1 = "Can NOT access key: {} in KeyStore: {}. Reason: {}"; + private static final String ERROR_MSG_2 = "Key: {} will be NOT available"; + + /** + * Read all certificates from a {@link KeyStore}. + * + * @param keyStore KeyStore with certificates + * @return {@link List} of {@link X509Certificate}, but never null + * @throws KeyStoreException In case of an error during KeyStore operations + */ + @Nonnull + public static List<X509Certificate> readCertsFromKeyStore(@Nonnull final KeyStore keyStore) throws KeyStoreException { + final List<X509Certificate> result = new ArrayList<>(); + + final Enumeration<String> aliases = keyStore.aliases(); + while (aliases.hasMoreElements()) { + final String el = aliases.nextElement(); + log.trace("Process TrustStoreEntry: " + el); + if (keyStore.isCertificateEntry(el)) { + final Certificate cert = keyStore.getCertificate(el); + if (cert != null && cert instanceof X509Certificate) { + result.add((X509Certificate) cert); + } else { + log.info("Can not process entry: {}. Reason: {}", el, cert != null ? cert.getType() : "cert is null"); + } + + } + } + + return Collections.unmodifiableList(result); + } + + /** + * Get a specific private Key and the corresponding certificate from a {@link KeyStore}. + * + * @param keyStore KeyStore with certificates + * @param keyAlias Alias of the entry + * @param keyPassword Password to access the Key + * @param isRequired if true, the method throw an {@link EaafKeyAccessException} + * if the key is not available or invalid + * @param friendlyName Name of the KeyStore for logging purposes + * @return A {@link Pair} of {@link Key} and {@link X509Certificate} for this alias, + * or maybe null if isRequired was <code>false</code> + * @throws EaafKeyAccessException In case of an error during KeyStore operations + */ + @Nullable + public static Pair<Key, X509Certificate[]> getPrivateKeyAndCertificates(@Nonnull KeyStore keyStore, + @Nonnull String keyAlias, @Nullable char[] keyPassword, boolean isRequired, @Nonnull String friendlyName) + throws EaafKeyAccessException { + try { + Key privKey = keyStore.getKey(keyAlias, keyPassword); + if (privKey != null) { + final Certificate[] certChainSigning = keyStore.getCertificateChain(keyAlias); + X509Certificate[] certChain = new X509Certificate[certChainSigning.length]; + + for (int i = 0; i < certChainSigning.length; i++) { + if (certChainSigning[i] instanceof X509Certificate) { + certChain[i] = (X509Certificate) certChainSigning[i]; + } else { + log.warn("NO X509 certificate for signing: " + certChainSigning[i].getType()); + } + + } + + Pair<Key, X509Certificate[]> keyResult = Pair.newInstance(privKey, certChain); + validateKeyResult(keyResult, friendlyName, keyAlias); + return keyResult; + + } else { + if (isRequired) { + log.warn(ERROR_MSG_1, + keyAlias, friendlyName, ERROR_MSG_REASON); + throw new EaafKeyAccessException(EaafKeyAccessException.ERROR_CODE_09, + friendlyName, keyAlias, ERROR_MSG_REASON); + + } else { + log.info(ERROR_MSG_1, + keyAlias, friendlyName, ERROR_MSG_REASON); + log.info(ERROR_MSG_2, keyAlias); + + } + } + + } catch (KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException e) { + if (isRequired) { + log.warn(ERROR_MSG_1, + keyAlias, friendlyName, e.getMessage()); + throw new EaafKeyAccessException( + EaafKeyAccessException.ERROR_CODE_09, e, friendlyName, keyAlias, e.getMessage()); + + } else { + log.info(ERROR_MSG_1, + keyAlias, friendlyName, e.getMessage()); + log.info(ERROR_MSG_2, keyAlias); + + } + } + + return null; + + } + + private static void validateKeyResult(Pair<Key, X509Certificate[]> keyResult, + String friendlyName, String keyAlias) throws EaafKeyAccessException { + // some short validation + if (!(keyResult.getFirst() instanceof PrivateKey)) { + log.info("PrivateKey: {} in KeyStore: {} is of wrong type", keyAlias, friendlyName); + throw new EaafKeyAccessException( + EaafKeyAccessException.ERROR_CODE_09, + friendlyName, keyAlias, "Wrong PrivateKey type "); + + } + + if (keyResult.getSecond() == null || keyResult.getSecond().length == 0) { + log.info("NO certificate for Key: {} in KeyStore: {}", keyAlias, friendlyName); + throw new EaafKeyAccessException( + EaafKeyAccessException.ERROR_CODE_09, + friendlyName, keyAlias, "NO certificate for PrivateKey"); + + } + } +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/KeyStoreConfiguration.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/KeyStoreConfiguration.java new file mode 100644 index 00000000..970efd22 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/credential/KeyStoreConfiguration.java @@ -0,0 +1,225 @@ +package at.gv.egiz.eaaf.core.impl.credential; + +import java.util.Map; + +import javax.annotation.Nonnull; + +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; + +import org.apache.commons.lang3.StringUtils; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Getter +@Setter +public class KeyStoreConfiguration { + + public static final String PROP_CONFIG_KEYSTORE_TYPE = + "keystore.type"; + + public static final String PROP_CONFIG_HSMFACADE_NAME = + "keystore.name"; + + public static final String PROP_CONFIG_SOFTWARE_KEYSTORE_PATH = + "keystore.path"; + public static final String PROP_CONFIG_SOFTWARE_KEYSTORE_PASSORD = + "keystore.password"; + + /** + * FriendlyName for this KeyStore. Mainly used for logging. + */ + private String friendlyName; + + /** + * General type of the KeyStore that should be generated. + */ + private KeyStoreType keyStoreType; + + /** + * Name of the KeyStore in HSM Facade. + */ + private String keyStoreName; + + /** + * Path to software KeyStore in case of a PKCS12 or JKS KeyStore. + */ + private String softKeyStoreFilePath; + + /** + * Password of a software KeyStore in case of a PKCS12 or JKS KeyStore. + */ + private String softKeyStorePassword; + + /** + * Build a {@link KeyStoreConfiguration} from a configuration map. <br> + * <p> + * The configuration parameters defined in this class are used to load the + * configuration. + * </p> + * + * @param config Configuration + * @param friendlyName FriendlyName for this KeyStore + * @return Configuration object for {@link EaafKeyStoreFactory} + * @throws EaafConfigurationException In case of a configuration error. + */ + public static KeyStoreConfiguration buildFromConfigurationMap(Map<String, String> config, + String friendlyName) throws EaafConfigurationException { + + final KeyStoreConfiguration internalConfig = new KeyStoreConfiguration(); + internalConfig.setFriendlyName(friendlyName); + + final KeyStoreType internalKeyStoreType = KeyStoreType.fromString( + getConfigurationParameter(config, PROP_CONFIG_KEYSTORE_TYPE)); + if (internalKeyStoreType != null) { + internalConfig.setKeyStoreType(internalKeyStoreType); + + } else { + log.error("KeyStore: {} sets an unknown KeyStore type: {}", + friendlyName, getConfigurationParameter(config, PROP_CONFIG_KEYSTORE_TYPE)); + throw new EaafConfigurationException(EaafKeyStoreFactory.ERRORCODE_01, + new Object[] { friendlyName }); + + } + + if (internalKeyStoreType.equals(KeyStoreType.HSMFACADE)) { + log.trace("Set-up HSM-Facade KeyStore ... "); + internalConfig.setKeyStoreName( + getConfigurationParameter(config, PROP_CONFIG_HSMFACADE_NAME)); + + } else if (internalKeyStoreType.equals(KeyStoreType.PKCS12) + || internalKeyStoreType.equals(KeyStoreType.JKS)) { + log.trace("Set-up software KeyStore ... "); + internalConfig.setSoftKeyStoreFilePath( + getConfigurationParameter(config, PROP_CONFIG_SOFTWARE_KEYSTORE_PATH)); + internalConfig.setSoftKeyStorePassword( + getConfigurationParameter(config, PROP_CONFIG_SOFTWARE_KEYSTORE_PASSORD)); + + } else { + log.info("Configuration of type: {} not supported yet", internalKeyStoreType); + throw new EaafConfigurationException(EaafKeyStoreFactory.ERRORCODE_02, + new Object[] { friendlyName, config.get(PROP_CONFIG_KEYSTORE_TYPE) }); + + } + + return internalConfig; + } + + /** + * Set the Type of the KeyStore based on String identifier. + * + * @param keyStoreType String based KeyStore type + * @throws EaafConfigurationException In case of an unknown KeyStore type + */ + public void setKeyStoreType(@Nonnull String keyStoreType) throws EaafConfigurationException { + final KeyStoreType internalKeyStoreType = KeyStoreType.fromString(keyStoreType); + if (internalKeyStoreType != null) { + setKeyStoreType(internalKeyStoreType); + + } else { + log.error("KeyStore: {} sets an unknown KeyStore type: {}", + friendlyName, keyStoreType); + throw new EaafConfigurationException(EaafKeyStoreFactory.ERRORCODE_01, + new Object[] { friendlyName }); + + } + + } + + /** + * Set the Type of the KeyStore based on String identifier. + * + * @param type String based KeyStore type + */ + public void setKeyStoreType(@Nonnull KeyStoreType type) { + this.keyStoreType = type; + + } + + /** + * Validate the internal state of this configuration object. + * + * @throws EaafConfigurationException In case of a configuration error + */ + public void validate() throws EaafConfigurationException { + if (KeyStoreType.HSMFACADE.equals(keyStoreType)) { + log.trace("Validate HSM-Facade KeyStore ... "); + checkConfigurationValue(keyStoreName, EaafKeyStoreFactory.ERRORCODE_07, + friendlyName, "Missing 'KeyName' for HSM-Facade"); + + } else if (KeyStoreType.PKCS12.equals(keyStoreType) + || KeyStoreType.JKS.equals(keyStoreType)) { + log.trace("Validate software KeyStore ... "); + checkConfigurationValue(softKeyStoreFilePath, EaafKeyStoreFactory.ERRORCODE_07, + friendlyName, "Missing 'KeyPath' for software keystore"); + checkConfigurationValue(softKeyStorePassword, EaafKeyStoreFactory.ERRORCODE_07, + friendlyName, "Missing 'KeyPassword' for software keystore"); + + } else { + log.info("Validation of type: {} not supported yet", keyStoreType); + + } + } + + public enum KeyStoreType { + PKCS12("pkcs12"), JKS("jks"), HSMFACADE("hsmfacade"), PKCS11("pkcs11"); + + private final String keyStoreType; + + KeyStoreType(final String keyStoreType) { + this.keyStoreType = keyStoreType; + } + + /** + * Get Type of this KeyStore. + * + * @return + */ + public String getKeyStoreType() { + return this.keyStoreType; + } + + /** + * Get KeyStore type from String representation. + * + * @param s Config parameter + * @return + */ + public static KeyStoreType fromString(final String s) { + try { + return KeyStoreType.valueOf(s.toUpperCase()); + + } catch (IllegalArgumentException | NullPointerException e) { + return null; + } + } + + @Override + public String toString() { + return getKeyStoreType(); + + } + } + + @Nonnull + private static String getConfigurationParameter(@Nonnull Map<String, String> config, + @Nonnull String configParamKey) + throws EaafConfigurationException { + final String configValue = config.get(configParamKey); + checkConfigurationValue(configValue, EaafKeyStoreFactory.ERRORCODE_04, configParamKey); + return configValue; + + } + + private static void checkConfigurationValue(String configValue, String errorCode, String... params) + throws EaafConfigurationException { + if (StringUtils.isEmpty(configValue)) { + throw new EaafConfigurationException(errorCode, + params); + + } + + } +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/conf/SpConfigurationImpl.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/conf/SpConfigurationImpl.java new file mode 100644 index 00000000..de54d103 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/conf/SpConfigurationImpl.java @@ -0,0 +1,183 @@ +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.2 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: + * https://joinup.ec.europa.eu/news/understanding-eupl-v12 + * + * 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.egiz.eaaf.core.impl.idp.conf; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.eaaf.core.api.data.EaafConfigConstants; +import at.gv.egiz.eaaf.core.api.data.EaafConstants; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.api.idp.ISpConfiguration; +import at.gv.egiz.eaaf.core.impl.utils.KeyValueUtils; + +public class SpConfigurationImpl implements ISpConfiguration { + private static final long serialVersionUID = 688541755446463453L; + + private static final Logger log = LoggerFactory.getLogger(SpConfigurationImpl.class); + + private final Map<String, String> spConfiguration; + private final List<String> targetAreasWithNoInteralBaseIdRestriction; + private final List<String> targetAreasWithNoBaseIdTransmissionRestriction; + + /** + * Service-provider configuration holder. + * + * @param spConfig Key/value based configuration + * @param authConfig Basic application configuration + */ + public SpConfigurationImpl(final Map<String, String> spConfig, final IConfiguration authConfig) { + this.spConfiguration = spConfig; + + // set oa specific restrictions + targetAreasWithNoInteralBaseIdRestriction = Collections + .unmodifiableList(KeyValueUtils.getListOfCsvValues(authConfig.getBasicConfiguration( + CONFIG_KEY_RESTRICTIONS_BASEID_INTERNAL, EaafConstants.URN_PREFIX_CDID))); + + targetAreasWithNoBaseIdTransmissionRestriction = Collections + .unmodifiableList(KeyValueUtils.getListOfCsvValues(authConfig.getBasicConfiguration( + CONFIG_KEY_RESTRICTIONS_BASEID_TRANSMISSION, EaafConstants.URN_PREFIX_CDID))); + + if (log.isTraceEnabled()) { + log.trace("Internal policy for OA: " + getUniqueIdentifier()); + for (final String el : targetAreasWithNoInteralBaseIdRestriction) { + log.trace(" Allow baseID processing for prefix " + el); + } + for (final String el : targetAreasWithNoBaseIdTransmissionRestriction) { + log.trace(" Allow baseID transfer for prefix " + el); + } + + } + } + + @Override + public final Map<String, String> getFullConfiguration() { + return this.spConfiguration; + + } + + @Override + public final String getConfigurationValue(final String key) { + if (key == null) { + return null; + } else { + return this.spConfiguration.get(key); + } + + } + + @Override + public final String getConfigurationValue(final String key, final String defaultValue) { + final String value = getConfigurationValue(key); + if (value == null) { + return defaultValue; + } else { + return value; + } + } + + @Override + public final boolean isConfigurationValue(final String key) { + return isConfigurationValue(key, false); + + } + + @Override + public final boolean isConfigurationValue(final String key, final boolean defaultValue) { + final String value = getConfigurationValue(key); + if (value != null) { + return Boolean.parseBoolean(value); + + } + + return defaultValue; + } + + @Override + public final boolean containsConfigurationKey(final String key) { + if (key == null) { + return false; + } else { + return this.spConfiguration.containsKey(key); + } + + } + + @Override + public String getUniqueIdentifier() { + return getConfigurationValue(EaafConfigConstants.SERVICE_UNIQUEIDENTIFIER); + + } + + @Override + public boolean hasBaseIdInternalProcessingRestriction() { + return false; + + } + + @Override + public boolean hasBaseIdTransferRestriction() { + return true; + + } + + @Override + public final List<String> getTargetsWithNoBaseIdInternalProcessingRestriction() { + return this.targetAreasWithNoInteralBaseIdRestriction; + } + + @Override + public final List<String> getTargetsWithNoBaseIdTransferRestriction() { + return this.targetAreasWithNoBaseIdTransmissionRestriction; + } + + @Override + public List<String> getRequiredLoA() { + log.warn( + "Method not implemented: " + SpConfigurationImpl.class.getName() + " 'getRequiredLoA()'"); + return null; + } + + @Override + public String getLoAMatchingMode() { + log.warn("Method not implemented: " + SpConfigurationImpl.class.getName() + + " 'getLoAMatchingMode()'"); + return null; + } + + @Override + public String getAreaSpecificTargetIdentifier() { + log.warn("Method not implemented: " + SpConfigurationImpl.class.getName() + + " 'getAreaSpecificTargetIdentifier()'"); + return null; + } + + @Override + public String getFriendlyName() { + log.warn( + "Method not implemented: " + SpConfigurationImpl.class.getName() + " 'getFriendlyName()'"); + return null; + } + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/support/SecureRandomHolder.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/support/SecureRandomHolder.java index a297367f..67d87b0d 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/support/SecureRandomHolder.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/support/SecureRandomHolder.java @@ -1,61 +1,58 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 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: + * Licensed under the EUPL, Version 1.2 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: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * 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. - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ + * 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.egiz.eaaf.core.impl.idp.process.support; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; /** - * Holder for a secure random instance following the initialization on demand holder design pattern. The secure random - * instance is a singleton that is initialized on first usage. - * + * Holder for a secure random instance following the initialization on demand + * holder design pattern. The secure random instance is a singleton that is + * initialized on first usage. + * * @author tknall - * + * */ public class SecureRandomHolder { - private SecureRandomHolder() { - } - - private static final SecureRandom SRND_INSTANCE; - static { - try { - SRND_INSTANCE = SecureRandom.getInstance("SHA1PRNG"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("Unable to instantiate SHA1PRNG.", e); - } - } - - /** - * Returns a secure random generator instance. - * @return The secure random instance. - */ - public static SecureRandom getInstance() { - return SecureRandomHolder.SRND_INSTANCE; - } - -}
\ No newline at end of file + private SecureRandomHolder() { + + } + + private static final SecureRandom SRND_INSTANCE; + + static { + try { + SRND_INSTANCE = SecureRandom.getInstance("SHA1PRNG"); + } catch (final NoSuchAlgorithmException e) { + throw new RuntimeException("Unable to instantiate SHA1PRNG.", e); + } + } + + /** + * Returns a secure random generator instance. + * + * @return The secure random instance. + */ + public static SecureRandom getInstance() { + return SecureRandomHolder.SRND_INSTANCE; + } + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/logging/EaafUtilsMessageSource.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/logging/EaafUtilsMessageSource.java new file mode 100644 index 00000000..5aa5b3b5 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/logging/EaafUtilsMessageSource.java @@ -0,0 +1,16 @@ +package at.gv.egiz.eaaf.core.impl.logging; + +import java.util.Arrays; +import java.util.List; + +import at.gv.egiz.eaaf.core.api.logging.IMessageSourceLocation; + +public class EaafUtilsMessageSource implements IMessageSourceLocation { + + @Override + public List<String> getMessageSourceLocation() { + return Arrays.asList("classpath:messages/eaaf_utils_message"); + + } + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/logging/SimpleStatusMessager.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/logging/SimpleStatusMessager.java index d36c79b9..0d394d19 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/logging/SimpleStatusMessager.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/logging/SimpleStatusMessager.java @@ -5,40 +5,41 @@ import java.text.MessageFormat; import at.gv.egiz.eaaf.core.api.IStatusMessenger; /** - * Simple {@link IStatusMessenger} implementation that formats messages by using {@link MessageFormat} - * + * Simple {@link IStatusMessenger} implementation that formats messages by + * using. {@link MessageFormat} + * * @author tlenz * */ public class SimpleStatusMessager implements IStatusMessenger { - private static final String NOTSUPPORTED = "Error response-codes not supported"; - private static final String NULLMESSAGE = "No error-message provided"; - - @Override - public String getMessage(String messageId, Object[] parameters) { - return getMessageWithoutDefault(messageId, parameters); - - } - - @Override - public String getMessageWithoutDefault(String messageId, Object[] parameters) { - if (messageId != null) { - return MessageFormat.format(messageId, parameters); - - } - - return NULLMESSAGE; - } - - @Override - public String getResponseErrorCode(Throwable throwable) { - return NOTSUPPORTED; - } - - @Override - public String mapInternalErrorToExternalError(String intErrorCode) { - return NOTSUPPORTED; - } + private static final String NOTSUPPORTED = "Error response-codes not supported"; + private static final String NULLMESSAGE = "No error-message provided"; + + @Override + public String getMessage(final String messageId, final Object[] parameters) { + return getMessageWithoutDefault(messageId, parameters); + + } + + @Override + public String getMessageWithoutDefault(final String messageId, final Object[] parameters) { + if (messageId != null) { + return MessageFormat.format(messageId, parameters); + + } + + return NULLMESSAGE; + } + + @Override + public String getResponseErrorCode(final Throwable throwable) { + return NOTSUPPORTED; + } + + @Override + public String mapInternalErrorToExternalError(final String intErrorCode) { + return NOTSUPPORTED; + } } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ArrayUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ArrayUtils.java index 8585bc05..1da82f43 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ArrayUtils.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ArrayUtils.java @@ -1,44 +1,42 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 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: + * Licensed under the EUPL, Version 1.2 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: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * 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. - *******************************************************************************/ + * 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.egiz.eaaf.core.impl.utils; import java.util.List; public class ArrayUtils { - /** - * Check if a String 's' is part of a List 'l' in qualsIgnoreCase mode - * - * @param s Search String - * @param l List of String elements - * @return true if 's' is in 'l', otherwise false - */ - public static boolean containsCaseInsensitive(String s, List<String> l){ - if (l == null || s == null) - return false; - - return l.stream().anyMatch(x -> x.equalsIgnoreCase(s)); - + /** + * Check if a String 's' is part of a List 'l' in qualsIgnoreCase mode. + * + * @param s Search String + * @param l List of String elements + * @return true if 's' is in 'l', otherwise false + */ + public static boolean containsCaseInsensitive(final String s, final List<String> l) { + if (l == null || s == null) { + return false; } + return l.stream().anyMatch(x -> x.equalsIgnoreCase(s)); + + } + } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DataURLBuilder.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DataURLBuilder.java deleted file mode 100644 index a81fafbc..00000000 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DataURLBuilder.java +++ /dev/null @@ -1,113 +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. - ******************************************************************************/ -/* - * Copyright 2003 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.egiz.eaaf.core.impl.utils; - -import org.apache.commons.lang3.StringUtils; - -import at.gv.egiz.eaaf.core.api.data.EAAFConstants; - -/** - * Builds a DataURL parameter meant for the security layer implementation - * to respond to. - * - * @author Paul Ivancsics - * @version $Id$ - */ -public class DataURLBuilder { - - /** - * Constructor for DataURLBuilder. - */ - public DataURLBuilder() { - super(); - } - - /** - * Constructs a data URL for <code>VerifyIdentityLink</code> or <code>VerifyAuthenticationBlock</code>, - * including the <code>MOASessionID</code> as a parameter. - * - * @param authBaseURL base URL (context path) of the MOA ID Authentication component, - * including a trailing <code>'/'</code> - * @param authServletName request part of the data URL - * @param pendingReqId sessionID to be included in the dataURL - * @return String - */ - public String buildDataURL(String authBaseURL, String authServletName, String pendingReqId) { - String dataURL; - if (!authBaseURL.endsWith("/")) - authBaseURL += "/"; - - if (authServletName.startsWith("/")) - authServletName = authServletName.substring(1); - - dataURL = authBaseURL + authServletName; - - if (StringUtils.isNotEmpty(pendingReqId)) - dataURL = addParameter(dataURL, EAAFConstants.PARAM_HTTP_TARGET_PENDINGREQUESTID, pendingReqId); - - return dataURL; - } - - /** - * Method addParameter. - * @param urlString represents the url - * @param paramname is the parameter to be added - * @param value is the value of that parameter - * @return String - */ - private String addParameter(String urlString, String paramname, String value) { - String url = urlString; - if (paramname != null) { - if (url.indexOf("?") < 0) - url += "?"; - else - url += "&"; - url += paramname + "=" + value; - } - return url; - } -} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DataUrlBuilder.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DataUrlBuilder.java new file mode 100644 index 00000000..ef1f3fdc --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DataUrlBuilder.java @@ -0,0 +1,93 @@ +/* + * 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.egiz.eaaf.core.impl.utils; + +import org.apache.commons.lang3.StringUtils; + +import at.gv.egiz.eaaf.core.api.data.EaafConstants; + +/** + * Builds a DataURL parameter meant for the security layer implementation to + * respond to. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class DataUrlBuilder { + + /** + * Constructor for DataURLBuilder. + */ + public DataUrlBuilder() { + super(); + } + + /** + * Constructs a data URL for <code>VerifyIdentityLink</code> or + * <code>VerifyAuthenticationBlock</code>, including the + * <code>MOASessionID</code> as a parameter. + * + * @param authBaseUrl base URL (context path) of the MOA ID Authentication + * component, including a trailing <code>'/'</code> + * @param authServletName request part of the data URL + * @param pendingReqId sessionID to be included in the dataURL + * @return String + */ + public String buildDataUrl(String authBaseUrl, String authServletName, + final String pendingReqId) { + String dataUrl; + if (!authBaseUrl.endsWith("/")) { + authBaseUrl += "/"; + } + + if (authServletName.startsWith("/")) { + authServletName = authServletName.substring(1); + } + + dataUrl = authBaseUrl + authServletName; + + if (StringUtils.isNotEmpty(pendingReqId)) { + dataUrl = + addParameter(dataUrl, EaafConstants.PARAM_HTTP_TARGET_PENDINGREQUESTID, pendingReqId); + } + + return dataUrl; + } + + /** + * Method addParameter. + * + * @param urlString represents the url + * @param paramname is the parameter to be added + * @param value is the value of that parameter + * @return String + */ + private String addParameter(final String urlString, final String paramname, final String value) { + String url = urlString; + if (paramname != null) { + if (url.indexOf("?") < 0) { + url += "?"; + } else { + url += "&"; + } + url += paramname + "=" + value; + } + return url; + } +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/FileUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/FileUtils.java index 6ac51ac4..7cb551e2 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/FileUtils.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/FileUtils.java @@ -1,30 +1,21 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 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: + * Licensed under the EUPL, Version 1.2 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: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * 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. - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ - + * 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.egiz.eaaf.core.impl.utils; @@ -44,16 +35,16 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FileUtils { - private static final Logger log = LoggerFactory.getLogger(FileUtils.class); - - + private static final Logger log = LoggerFactory.getLogger(FileUtils.class); + /** * Reads a file, given by URL, into a byte array. + * * @param urlString file URL * @return file content * @throws IOException on any exception thrown */ - public static byte[] readURL(String urlString) throws IOException { + public static byte[] readUrl(final String urlString) throws IOException { final URL url = new URL(urlString); final InputStream in = new BufferedInputStream(url.openStream()); final byte[] content = StreamUtils.readStream(in); @@ -63,114 +54,142 @@ public class FileUtils { /** * Reads a file from a resource. + * * @param name resource name * @return file content as a byte array * @throws IOException on any exception thrown */ - public static byte[] readResource(String name) throws IOException { + public static byte[] readResource(final String name) throws IOException { final ClassLoader cl = FileUtils.class.getClassLoader(); final BufferedInputStream in = new BufferedInputStream(cl.getResourceAsStream(name)); final byte[] content = StreamUtils.readStream(in); in.close(); return content; } + /** * Reads a file from a resource. - * @param name filename + * + * @param name filename * @param encoding character encoding * @return file content * @throws IOException on any exception thrown */ - public static String readResource(String name, String encoding) throws IOException { + public static String readResource(final String name, final String encoding) throws IOException { final byte[] content = readResource(name); return new String(content, encoding); } - - - /** - * Returns the absolute URL of a given url which is relative to the parameter root - * @param url - * @param root - * @return String - * @throws MalformedURLException - */ - public static String makeAbsoluteURL(String url, URI root) throws MalformedURLException { - if (root != null) - return makeAbsoluteURL(url, root.toURL().toString()); - else - return makeAbsoluteURL(url, StringUtils.EMPTY); - - } - - /** - * Returns the absolute URL of a given url which is relative to the parameter root - * @param url - * @param root - * @return String - */ - public static String makeAbsoluteURL(String url, String root) { - //if url is relative to rootConfigFileDirName make it absolute - - log.trace("Making AbsoluteURL URL: " + url + " Root-Path: " + root); - - if (StringUtils.isEmpty(root)) - root = null; - - File keyFile; - String newURL = url; - - if(null == url) return null; - - if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:") - || url.startsWith("ftp:") || url.startsWith("classpath:")) { - return url; - - } else { - // check if absolute - if not make it absolute - keyFile = new File(url); - if (!keyFile.isAbsolute()) { - keyFile = new File(root, url); - - if (keyFile.toString().startsWith("file:")) - newURL = keyFile.toString(); - - else - newURL = keyFile.toURI().toString(); - - } - return newURL; - } - } - - - private static void copy( InputStream fis, OutputStream fos ) - { - try - { - final byte[] buffer = new byte[ 0xFFFF ]; - for ( int len; (len = fis.read(buffer)) != -1; ) - fos.write( buffer, 0, len ); - } - catch( final IOException e ) { - System.err.println( e ); - } - finally { - if ( fis != null ) - try { fis.close(); } catch ( final IOException e ) { e.printStackTrace(); } - if ( fos != null ) - try { fos.close(); } catch ( final IOException e ) { e.printStackTrace(); } - } - } - - public static void copyFile(File src, File dest) - { - try - { - copy( new FileInputStream( src ), new FileOutputStream( dest ) ); - } - catch( final IOException e ) { - e.printStackTrace(); - } - } - + + /** + * Returns the absolute URL of a given url which is relative to the parameter + * root. + * + * @param url Filepath + * @param root configuration root context + * @return absolut filepath + * @throws MalformedURLException In case of a filepath error + */ + public static String makeAbsoluteUrl(final String url, final URI root) + throws MalformedURLException { + if (root != null) { + return makeAbsoluteUrl(url, root.toURL().toString()); + } else { + return makeAbsoluteUrl(url, StringUtils.EMPTY); + } + + } + + /** + * Returns the absolute URL of a given url which is relative to the parameter + * root. + * + * @param url Filepath + * @param root configuration root context + * @return absolut filepath + */ + public static String makeAbsoluteUrl(final String url, String root) { + // if url is relative to rootConfigFileDirName make it absolute + + log.trace("Making AbsoluteURL URL: " + url + " Root-Path: " + root); + + if (StringUtils.isEmpty(root)) { + root = null; + } + + File keyFile; + String newUrl = url; + + if (null == url) { + return null; + } + + if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:") + || url.startsWith("ftp:") || url.startsWith("classpath:")) { + return url; + + } else { + // check if absolute - if not make it absolute + keyFile = new File(url); + if (!keyFile.isAbsolute()) { + keyFile = new File(root, url); + + if (keyFile.toString().startsWith("file:")) { + newUrl = keyFile.toString(); + } else { + newUrl = keyFile.toURI().toString(); + } + + } + return newUrl; + } + } + + private static void copy(final InputStream fis, final OutputStream fos) { + try { + final byte[] buffer = new byte[0xFFFF]; + for (int len; (len = fis.read(buffer)) != -1;) { + fos.write(buffer, 0, len); + } + } catch (final IOException e) { + System.err.println(e); + + } + } + + /** + * Copy file from source to destination. + * + * @param src File source + * @param dest file destination + */ + public static void copyFile(final File src, final File dest) { + InputStream fis = null; + OutputStream fos = null; + + try { + fis = new FileInputStream(src); + fos = new FileOutputStream(src); + copy(fis, fos); + + } catch (final IOException e) { + e.printStackTrace(); + + } finally { + if (fis != null) { + try { + fis.close(); + } catch (final IOException e) { + e.printStackTrace(); + } + } + if (fos != null) { + try { + fos.close(); + } catch (final IOException e) { + e.printStackTrace(); + } + } + } + } + } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HTTPUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HTTPUtils.java deleted file mode 100644 index cf1abaa7..00000000 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HTTPUtils.java +++ /dev/null @@ -1,178 +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. - ******************************************************************************/ -/* - * Copyright 2003 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.egiz.eaaf.core.impl.utils; - -import javax.servlet.http.HttpServletRequest; - -import org.apache.commons.lang3.StringUtils; - - -/** - * - * @author Rudolf Schamberger - * - */ -public class HTTPUtils { - -// /** -// * Utility used to obtainin correct encoded HTTP content. -// * Reads a given Content adressed by HTTP-URL into String. -// * Content encoding is considered by using the Content-Type HTTP header charset value. -// * @param URL HTTP URL to read from. -// * @return String representation of content -// * @throws IOException on data-reading problems -// */ -// public static String readHttpURL(String URL) -// throws IOException { -// -// URL url = new URL(URL); -// HttpURLConnection conn = (HttpURLConnection)url.openConnection(); -// conn.setRequestMethod("GET"); -// String contentType = conn.getContentType(); -// RE regExp = null; -// try { -// regExp = new RE("(;.*charset=)(\"*)(.*[^\"])"); -// } catch (RESyntaxException e) { -// //RESyntaxException is not possible = expr. is costant -// } -// boolean charsetSupplied = regExp.match(contentType); -// String encoding = "ISO-8859-1"; //default HTTP encoding -// if (charsetSupplied) { -// encoding = regExp.getParen(3); -// } -// InputStream instream = new BufferedInputStream(conn.getInputStream()); -// InputStreamReader isr = new InputStreamReader(instream, encoding); -// Reader in = new BufferedReader(isr); -// int ch; -// StringBuffer buffer = new StringBuffer(); -// while ((ch = in.read()) > -1) { -// buffer.append((char)ch); -// } -// in.close(); -// conn.disconnect(); -// return buffer.toString(); -// } - - /** - * Helper method to retrieve server URL including context path - * @param request HttpServletRequest - * @return Server URL including context path (e.g. http://localhost:8443/moa-id-auth - */ - public static String getBaseURL(HttpServletRequest request) { - StringBuffer buffer = new StringBuffer(getServerURL(request)); - - // add context path if available - String contextPath = request.getContextPath(); - if (!StringUtils.isEmpty(contextPath)) { - buffer.append(contextPath); - } - - return buffer.toString(); - } - - /** - * Helper method to retrieve server URL - * @param request HttpServletRequest - * @return Server URL (e.g. http://localhost:8443) - */ - public static String getServerURL(HttpServletRequest request) { - StringBuffer buffer = new StringBuffer(); - - // get protocol - String protocol = request.getScheme(); - buffer.append(protocol).append("://"); - - // server name - buffer.append(request.getServerName()); - - // add port if necessary - int port = request.getServerPort(); - if ((protocol.equals("http") && port != 80) || (protocol.equals("https") && port != 443)) { - buffer.append(':'); - buffer.append(port); - } - - return buffer.toString(); - } - - /** - * Extract the IDP PublicURLPrefix from authrequest - * - * @param req HttpServletRequest - * @return PublicURLPrefix <String> which ends always without / - */ - public static String extractAuthURLFromRequest(HttpServletRequest req) { - String authURL = req.getScheme() + "://" + req.getServerName(); - if ((req.getScheme().equalsIgnoreCase("https") && req.getServerPort()!=443) || (req.getScheme().equalsIgnoreCase("http") && req.getServerPort()!=80)) { - authURL = authURL.concat(":" + req.getServerPort()); - } - authURL = authURL.concat(req.getContextPath()); - return authURL; - - } - - /** - * Extract the IDP requested URL from authrequest - * - * @param req HttpServletRequest - * @return RequestURL <String> which ends always without / - */ - public static String extractAuthServletPathFromRequest(HttpServletRequest req) { - return extractAuthURLFromRequest(req).concat(req.getServletPath()); - - } - - public static String addURLParameter(String url, String paramname, - String paramvalue) { - String param = paramname + "=" + paramvalue; - if (url.indexOf("?") < 0) - return url + "?" + param; - else - return url + "&" + param; - } - -} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java index a8cfa7c1..e681e705 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpClientFactory.java @@ -1,18 +1,22 @@ package at.gv.egiz.eaaf.core.impl.utils; -import java.io.IOException; -import java.io.InputStream; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; import javax.annotation.PostConstruct; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; +import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration; +import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType; + import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; @@ -38,345 +42,329 @@ import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.protocol.HttpContext; import org.apache.http.ssl.SSLContexts; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; -import at.gv.egiz.eaaf.core.api.idp.IConfiguration; -import at.gv.egiz.eaaf.core.exceptions.EAAFConfigurationException; +import lombok.extern.slf4j.Slf4j; +@Slf4j public class HttpClientFactory implements IHttpClientFactory { - private static final Logger log = LoggerFactory.getLogger(HttpClientFactory.class); - @Autowired(required=true) private IConfiguration basicConfig; - @Autowired(required=true) ResourceLoader resourceLoader; - - public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE = "client.http.connection.pool.use"; - public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL = "client.http.connection.pool.maxtotal"; - public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE = "client.http.connection.pool.maxperroute"; - public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET = "client.http.connection.timeout.socket"; - public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION = "client.http.connection.timeout.connection"; - public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST = "client.http.connection.timeout.request"; - public static final String PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL = "client.http.ssl.hostnameverifier.trustall"; - - public static final String PROP_CONFIG_CLIENT_MODE = "client.authmode"; - public static final String PROP_CONFIG_CLIENT_AUTH_HTTP_USERNAME = "client.auth.http.username"; - public static final String PROP_CONFIG_CLIENT_AUTH_HTTP_PASSORD = "client.auth.http.password"; - public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PATH = "client.auth.ssl.keystore.path"; - public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PASSORD = "client.auth.ssl.keystore.password"; - public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_TYPE = "client.auth.ssl.keystore.type"; - public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEY_PASSWORD = "client.auth.ssl.key.password"; - public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEY_ALIAS = "client.auth.ssl.key.alias"; - - // default configuration values - public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET = "15"; - public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION = "15"; - public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST = "30"; - public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL = "500"; - public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE = "100"; - - public enum ClientAuthMode { - NONE("none"), - PASSWORD("password"), - SSL("ssl"); - - private final String mode; - - private ClientAuthMode(String mode) { - this.mode = mode; - } - - /** - * Get the PVP mode - * - * @return - */ - public String getMode() { - return this.mode; - } - - public static ClientAuthMode fromString(String s) { - try { - return ClientAuthMode.valueOf(s.toUpperCase()); - - } catch (IllegalArgumentException|NullPointerException e) { - return null; - } - } - - @Override - public String toString() { - return getMode(); - - } - - }; - - public enum KeyStoreType { - PKCS12("pkcs12"), - JKS("jks"); - - private final String type; - - private KeyStoreType (String type) { - this.type = type; - } - - /** - * Get the PVP mode - * - * @return - */ - public String getType() { - return this.type; - } - - public static KeyStoreType fromString(String s) { - try { - return KeyStoreType.valueOf(s.toUpperCase()); - - } catch (IllegalArgumentException|NullPointerException e) { - return null; - } - } - - @Override - public String toString() { - return getType(); - - } - - }; - - private HttpClientBuilder httpClientBuilder = null; - - /* (non-Javadoc) - * @see at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory#getHttpClient() - */ - @Override - public CloseableHttpClient getHttpClient() { - return getHttpClient(true); - - } - - @Override - public CloseableHttpClient getHttpClient(boolean followRedirects) { - RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); - if (!followRedirects) - redirectStrategy = new RedirectStrategy() { - - @Override - public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) - throws ProtocolException { - return false; - } - - @Override - public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context) - throws ProtocolException { - return null; - } - }; - - return httpClientBuilder - .setRedirectStrategy(redirectStrategy) - .build(); - - } - - @PostConstruct - private void initalize() { - //initialize http client - log.trace("Initializing HTTP Client-builder ... "); - httpClientBuilder = HttpClients.custom(); - - //set default request configuration - final RequestConfig requestConfig = RequestConfig.custom() - .setConnectTimeout(Integer.valueOf(basicConfig.getBasicConfiguration( - PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION, - DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION)) * 1000) - .setConnectionRequestTimeout(Integer.valueOf(basicConfig.getBasicConfiguration( - PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST, - DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST)) * 1000) - .setSocketTimeout(Integer.valueOf(basicConfig.getBasicConfiguration( - PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET, - DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET)) * 1000) - .build(); - httpClientBuilder.setDefaultRequestConfig(requestConfig); - - ClientAuthMode clientAuthMode = ClientAuthMode.fromString( - basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_MODE, ClientAuthMode.NONE.getMode())); - if (clientAuthMode == null) { - log.warn("Can Not parse ClientAuthMode! Set mode to default value"); - clientAuthMode = ClientAuthMode.NONE; - - } - - //inject basic http authentication if required - log.info("Client authentication-mode is set to: {}", clientAuthMode); - injectBasicAuthenticationIfRequired(clientAuthMode); - - //inject authentication if required - final LayeredConnectionSocketFactory sslConnectionFactory = getSSLContext(clientAuthMode); - - //set pool connection if required - injectConnectionPoolIfRequired(sslConnectionFactory); - - - } - - private void injectBasicAuthenticationIfRequired(ClientAuthMode clientAuthMode) { - if (clientAuthMode.equals(ClientAuthMode.PASSWORD)) { - final CredentialsProvider provider = new BasicCredentialsProvider(); - - final String username = basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_HTTP_USERNAME); - final String password = basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_HTTP_PASSORD); - - if (StringUtils.isEmpty(username)) { - log.warn("Http basic authentication was activated but NOT username was set!"); - - } - - log.trace("Injecting basic authentication with username: {} and password: {}", username, password); - final UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password); - provider.setCredentials(AuthScope.ANY, credentials); - httpClientBuilder.setDefaultCredentialsProvider(provider); - log.info("Basic http authentication was injected with username: {}", username); - - } else { - log.trace("Injection of Http Basic authentication was skipped"); - - } - - } - - private SSLContext buildSSLContextWithSSLClientAuthentication() throws KeyManagementException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, EAAFConfigurationException { - log.trace("Injecting SSL client-authentication into http client ... "); - final KeyStore keystore = getSSLAuthKeyStore(); - final String keyPasswordString = basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEY_PASSWORD); - log.trace("Open SSL Client-Auth keystore with password: {}", keyPasswordString); - final char[] keyPassword = (keyPasswordString == null) ? StringUtils.EMPTY.toCharArray() : keyPasswordString.toCharArray(); - return SSLContexts.custom().loadKeyMaterial(keystore, keyPassword).build(); - - } - - private KeyStore getSSLAuthKeyStore() throws EAAFConfigurationException { - final KeyStoreType keyStoreType = KeyStoreType.fromString( - basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_TYPE, KeyStoreType.PKCS12.getType())); - final String localKeyStorePath = basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PATH, StringUtils.EMPTY); - final String keyStorePassword = basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PASSORD, StringUtils.EMPTY); - - try { - log.debug("Open keyStore with type: {}", keyStoreType); - KeyStore clientStore; - if (keyStoreType.equals(KeyStoreType.PKCS12)) { - clientStore = KeyStore.getInstance("pkcs12"); - } else { - clientStore = KeyStore.getInstance("JKS"); - } - - - log.debug("Read keyStore path: {} from configuration", localKeyStorePath); - if (StringUtils.isNotEmpty(localKeyStorePath)) { - final String absFilePath = FileUtils.makeAbsoluteURL(localKeyStorePath, basicConfig.getConfigurationRootDirectory()); - final Resource ressource = resourceLoader.getResource(absFilePath); - final InputStream is = ressource.getInputStream(); - log.trace("Load keyStore: {} with password: {}", absFilePath, keyStorePassword); - clientStore.load(is, keyStorePassword.toCharArray()); - is.close(); - - return clientStore; - - } else { - log.warn("Path to keyStore for SSL Client-Authentication is empty or null"); - throw new EAAFConfigurationException("Path to keyStore for SSL Client-Authentication is empty or null", new Object[] {}); - - } - - } catch (final KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) { - log.warn("Can NOT read keyStore: {} from filesystem", localKeyStorePath, null, e); - throw new EAAFConfigurationException("Can NOT read keyStore: {} from filesystem", new Object[] {localKeyStorePath}, e); - - } - - } - - private LayeredConnectionSocketFactory getSSLContext(ClientAuthMode clientAuthMode) { - SSLContext sslContext = null; - try { - if (clientAuthMode.equals(ClientAuthMode.SSL)) { - sslContext = buildSSLContextWithSSLClientAuthentication(); - - } else { - log.trace("Initializing default SSL Context ... "); - sslContext = SSLContext.getDefault(); - - } - - //set hostname verifier - HostnameVerifier hostnameVerifier = null; - if (basicConfig.getBasicConfigurationBoolean( - PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL, - false)) { - hostnameVerifier = new NoopHostnameVerifier(); - log.warn("HTTP client-builder deactivates SSL Host-name verification!"); - - } - - final LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext , hostnameVerifier); - - return sslSocketFactory; - - } catch (final NoSuchAlgorithmException | KeyManagementException | UnrecoverableKeyException | KeyStoreException | EAAFConfigurationException e) { - log.warn("HTTP client-builder can NOT initialze SSL-Context", e); - - } - - log.info("HTTP client-builder successfuly initialized"); - return null; - - } - - private void injectConnectionPoolIfRequired(LayeredConnectionSocketFactory sslConnectionFactory) { - if (basicConfig.getBasicConfigurationBoolean( - PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE, - true)) { - PoolingHttpClientConnectionManager pool; - - //set socketFactoryRegistry if SSLConnectionFactory is Set - if (sslConnectionFactory != null) { - final Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create() - .register("http", PlainConnectionSocketFactory.getSocketFactory()) - .register("https", sslConnectionFactory) - .build(); - log.trace("Inject SSLSocketFactory into pooled connection"); - pool = new PoolingHttpClientConnectionManager(socketFactoryRegistry); - - } else { - pool = new PoolingHttpClientConnectionManager(); - - } - - pool.setDefaultMaxPerRoute(Integer.valueOf(basicConfig.getBasicConfiguration( - PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE, - DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE))); - pool.setMaxTotal(Integer.valueOf(basicConfig.getBasicConfiguration( - PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL, - DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL))); - - httpClientBuilder.setConnectionManager(pool); - log.debug("Initalize http-client pool with, maxTotal: {} maxPerRoute: {}", pool.getMaxTotal(), pool.getDefaultMaxPerRoute()); - - } else if (sslConnectionFactory != null) { - log.trace("Inject SSLSocketFactory without connection pool"); - httpClientBuilder.setSSLSocketFactory(sslConnectionFactory ); - - } - - - } - - - + + @Autowired(required = true) + private IConfiguration basicConfig; + + @Autowired(required = true) + ResourceLoader resourceLoader; + + @Autowired private EaafKeyStoreFactory keyStoreFactory; + + public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE = + "client.http.connection.pool.use"; + public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL = + "client.http.connection.pool.maxtotal"; + public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE = + "client.http.connection.pool.maxperroute"; + public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET = + "client.http.connection.timeout.socket"; + public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION = + "client.http.connection.timeout.connection"; + public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST = + "client.http.connection.timeout.request"; + public static final String PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL = + "client.http.ssl.hostnameverifier.trustall"; + + public static final String PROP_CONFIG_CLIENT_MODE = "client.authmode"; + public static final String PROP_CONFIG_CLIENT_AUTH_HTTP_USERNAME = "client.auth.http.username"; + public static final String PROP_CONFIG_CLIENT_AUTH_HTTP_PASSORD = "client.auth.http.password"; + public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PATH = + "client.auth.ssl.keystore.path"; + public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PASSORD = + "client.auth.ssl.keystore.password"; + private static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_NAME = + "client.auth.ssl.keystore.name"; + public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_TYPE = + "client.auth.ssl.keystore.type"; + public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEY_PASSWORD = + "client.auth.ssl.key.password"; + public static final String PROP_CONFIG_CLIENT_AUTH_SSL_KEY_ALIAS = "client.auth.ssl.key.alias"; + + // default configuration values + public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET = "15"; + public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION = "15"; + public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST = "30"; + public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL = "500"; + public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE = "100"; + + public enum ClientAuthMode { + NONE("none"), PASSWORD("password"), SSL("ssl"); + + private final String mode; + + ClientAuthMode(final String mode) { + this.mode = mode; + } + + /** + * Get the PVP mode. + * + * @return + */ + public String getMode() { + return this.mode; + } + + /** + * Get http-client authentication mode from String representation. + * + * @param s Config parameter + * @return + */ + public static ClientAuthMode fromString(final String s) { + try { + return ClientAuthMode.valueOf(s.toUpperCase()); + + } catch (IllegalArgumentException | NullPointerException e) { + return null; + } + } + + @Override + public String toString() { + return getMode(); + + } + + } + + private HttpClientBuilder httpClientBuilder = null; + + /* + * (non-Javadoc) + * + * @see at.gv.egiz.eaaf.core.impl.utils.IHttpClientFactory#getHttpClient() + */ + @Override + public CloseableHttpClient getHttpClient() { + return getHttpClient(true); + + } + + @Override + public CloseableHttpClient getHttpClient(final boolean followRedirects) { + RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); + if (!followRedirects) { + redirectStrategy = new RedirectStrategy() { + + @Override + public boolean isRedirected(final HttpRequest request, final HttpResponse response, + final HttpContext context) throws ProtocolException { + return false; + } + + @Override + public HttpUriRequest getRedirect(final HttpRequest request, final HttpResponse response, + final HttpContext context) throws ProtocolException { + return null; + } + }; + } + + return httpClientBuilder.setRedirectStrategy(redirectStrategy).build(); + + } + + @PostConstruct + private void initalize() { + // initialize http client + log.trace("Initializing HTTP Client-builder ... "); + httpClientBuilder = HttpClients.custom(); + + // set default request configuration + final RequestConfig requestConfig = + RequestConfig.custom() + .setConnectTimeout( + Integer.parseInt(basicConfig.getBasicConfiguration( + PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION, + DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION)) * 1000) + .setConnectionRequestTimeout(Integer.parseInt(basicConfig.getBasicConfiguration( + PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST, + DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST)) * 1000) + .setSocketTimeout(Integer.parseInt( + basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET, + DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET)) + * 1000) + .build(); + httpClientBuilder.setDefaultRequestConfig(requestConfig); + + ClientAuthMode clientAuthMode = ClientAuthMode.fromString( + basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_MODE, ClientAuthMode.NONE.getMode())); + if (clientAuthMode == null) { + log.warn("Can Not parse ClientAuthMode! Set mode to default value"); + clientAuthMode = ClientAuthMode.NONE; + + } + + // inject basic http authentication if required + log.info("Client authentication-mode is set to: {}", clientAuthMode); + injectBasicAuthenticationIfRequired(clientAuthMode); + + // inject authentication if required + final LayeredConnectionSocketFactory sslConnectionFactory = getSslContext(clientAuthMode); + + // set pool connection if required + injectConnectionPoolIfRequired(sslConnectionFactory); + + } + + private void injectBasicAuthenticationIfRequired(final ClientAuthMode clientAuthMode) { + if (clientAuthMode.equals(ClientAuthMode.PASSWORD)) { + final CredentialsProvider provider = new BasicCredentialsProvider(); + + final String username = + basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_HTTP_USERNAME); + final String password = + basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_HTTP_PASSORD); + + if (StringUtils.isEmpty(username)) { + log.warn("Http basic authentication was activated but NOT username was set!"); + + } + + log.trace("Injecting basic authentication with username: {} and password: {}", username, + password); + final UsernamePasswordCredentials credentials = + new UsernamePasswordCredentials(username, password); + provider.setCredentials(AuthScope.ANY, credentials); + httpClientBuilder.setDefaultCredentialsProvider(provider); + log.info("Basic http authentication was injected with username: {}", username); + + } else { + log.trace("Injection of Http Basic authentication was skipped"); + + } + + } + + private SSLContext buildSslContextWithSslClientAuthentication() + throws KeyManagementException, UnrecoverableKeyException, NoSuchAlgorithmException, + KeyStoreException, EaafConfigurationException { + log.trace("Injecting SSL client-authentication into http client ... "); + final KeyStore keystore = getSslAuthKeyStore(); + final String keyPasswordString = + basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEY_PASSWORD); + log.trace("Open SSL Client-Auth keystore with password: {}", keyPasswordString); + final char[] keyPassword = keyPasswordString == null ? StringUtils.EMPTY.toCharArray() + : keyPasswordString.toCharArray(); + return SSLContexts.custom().loadKeyMaterial(keystore, keyPassword).build(); + + } + + private KeyStore getSslAuthKeyStore() throws EaafConfigurationException { + final String keyStoreType = basicConfig.getBasicConfiguration( + PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_TYPE, KeyStoreType.PKCS12.getKeyStoreType()); + final String localKeyStorePath = basicConfig + .getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PATH, StringUtils.EMPTY); + final String keyStorePassword = basicConfig + .getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_PASSORD, StringUtils.EMPTY); + final String keyStoreName = basicConfig + .getBasicConfiguration(PROP_CONFIG_CLIENT_AUTH_SSL_KEYSTORE_NAME, StringUtils.EMPTY); + + try { + final KeyStoreConfiguration keyStoreConfig = new KeyStoreConfiguration(); + keyStoreConfig.setKeyStoreType(keyStoreType); + keyStoreConfig.setFriendlyName("HttpClient Keystore"); + keyStoreConfig.setSoftKeyStoreFilePath(localKeyStorePath); + keyStoreConfig.setSoftKeyStorePassword(keyStorePassword); + keyStoreConfig.setKeyStoreName(keyStoreName); + + log.debug("Open keyStore with type: {}", keyStoreType); + final KeyStore keyStore = keyStoreFactory.buildNewKeyStore(keyStoreConfig).getFirst(); + + return keyStore; + + } catch (final EaafException e) { + log.warn("Can NOT read keyStore: {} from filesystem", localKeyStorePath, null, e); + throw new EaafConfigurationException("Can NOT read keyStore: {} from filesystem", + new Object[] { localKeyStorePath }, e); + + } + + } + + private LayeredConnectionSocketFactory getSslContext(final ClientAuthMode clientAuthMode) { + SSLContext sslContext = null; + try { + if (clientAuthMode.equals(ClientAuthMode.SSL)) { + sslContext = buildSslContextWithSslClientAuthentication(); + + } else { + log.trace("Initializing default SSL Context ... "); + sslContext = SSLContext.getDefault(); + + } + + // set hostname verifier + HostnameVerifier hostnameVerifier = null; + if (basicConfig.getBasicConfigurationBoolean( + PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL, false)) { + hostnameVerifier = new NoopHostnameVerifier(); + log.warn("HTTP client-builder deactivates SSL Host-name verification!"); + + } + + final LayeredConnectionSocketFactory sslSocketFactory = + new SSLConnectionSocketFactory(sslContext, hostnameVerifier); + + return sslSocketFactory; + + } catch (final NoSuchAlgorithmException | KeyManagementException | UnrecoverableKeyException + | KeyStoreException | EaafConfigurationException e) { + log.warn("HTTP client-builder can NOT initialze SSL-Context", e); + + } + + log.info("HTTP client-builder successfuly initialized"); + return null; + + } + + private void injectConnectionPoolIfRequired( + final LayeredConnectionSocketFactory sslConnectionFactory) { + if (basicConfig.getBasicConfigurationBoolean(PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE, + true)) { + PoolingHttpClientConnectionManager pool; + + // set socketFactoryRegistry if SSLConnectionFactory is Set + if (sslConnectionFactory != null) { + final Registry<ConnectionSocketFactory> socketFactoryRegistry = + RegistryBuilder.<ConnectionSocketFactory>create() + .register("http", PlainConnectionSocketFactory.getSocketFactory()) + .register("https", sslConnectionFactory).build(); + log.trace("Inject SSLSocketFactory into pooled connection"); + pool = new PoolingHttpClientConnectionManager(socketFactoryRegistry); + + } else { + pool = new PoolingHttpClientConnectionManager(); + + } + + pool.setDefaultMaxPerRoute(Integer.parseInt( + basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE, + DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE))); + pool.setMaxTotal(Integer.parseInt( + basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL, + DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL))); + + httpClientBuilder.setConnectionManager(pool); + log.debug("Initalize http-client pool with, maxTotal: {} maxPerRoute: {}", pool.getMaxTotal(), + pool.getDefaultMaxPerRoute()); + + } else if (sslConnectionFactory != null) { + log.trace("Inject SSLSocketFactory without connection pool"); + httpClientBuilder.setSSLSocketFactory(sslConnectionFactory); + + } + + } + } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpUtils.java new file mode 100644 index 00000000..66356ba0 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HttpUtils.java @@ -0,0 +1,118 @@ +/* + * 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.egiz.eaaf.core.impl.utils; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang3.StringUtils; + +public class HttpUtils { + + /** + * Helper method to retrieve server URL including context path. + * + * @param request HttpServletRequest + * @return Server URL including context path (e.g. + * http://localhost:8443/moa-id-auth + */ + public static String getBaseUrl(final HttpServletRequest request) { + final StringBuffer buffer = new StringBuffer(getServerUrl(request)); + + // add context path if available + final String contextPath = request.getContextPath(); + if (!StringUtils.isEmpty(contextPath)) { + buffer.append(contextPath); + } + + return buffer.toString(); + } + + /** + * Helper method to retrieve server URL. + * + * @param request HttpServletRequest + * @return Server URL (e.g. http://localhost:8443) + */ + public static String getServerUrl(final HttpServletRequest request) { + final StringBuffer buffer = new StringBuffer(); + + // get protocol + final String protocol = request.getScheme(); + buffer.append(protocol).append("://"); + + // server name + buffer.append(request.getServerName()); + + // add port if necessary + final int port = request.getServerPort(); + if (protocol.equals("http") && port != 80 || protocol.equals("https") && port != 443) { + buffer.append(':'); + buffer.append(port); + } + + return buffer.toString(); + } + + /** + * Extract the IDP PublicURLPrefix from authrequest. + * + * @param req HttpServletRequest + * @return PublicURLPrefix which ends always without / + */ + public static String extractAuthUrlFromRequest(final HttpServletRequest req) { + String authUrl = req.getScheme() + "://" + req.getServerName(); + if (req.getScheme().equalsIgnoreCase("https") && req.getServerPort() != 443 + || req.getScheme().equalsIgnoreCase("http") && req.getServerPort() != 80) { + authUrl = authUrl.concat(":" + req.getServerPort()); + } + authUrl = authUrl.concat(req.getContextPath()); + return authUrl; + + } + + /** + * Extract the IDP requested URL from authrequest. + * + * @param req HttpServletRequest + * @return RequestURL which ends always without / + */ + public static String extractAuthServletPathFromRequest(final HttpServletRequest req) { + return extractAuthUrlFromRequest(req).concat(req.getServletPath()); + + } + + /** + * Add a http GET parameter to URL. + * + * @param url URL + * @param paramname Name of the parameter. + * @param paramvalue Value of the parameter. + * @return + */ + public static String addUrlParameter(final String url, final String paramname, + final String paramvalue) { + final String param = paramname + "=" + paramvalue; + if (url.indexOf("?") < 0) { + return url + "?" + param; + } else { + return url + "&" + param; + } + } + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java index 1975fb52..f922e1ac 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/IHttpClientFactory.java @@ -4,18 +4,21 @@ import org.apache.http.impl.client.CloseableHttpClient; public interface IHttpClientFactory { - /** - * Return an instance of a Apache HTTP client that follows http redirects automatically - * - * @return - */ - CloseableHttpClient getHttpClient(); + /** + * Return an instance of a Apache HTTP client that follows http redirects + * automatically. + * + * @return + */ + CloseableHttpClient getHttpClient(); - /** - * Return an instance of a Apache HTTP client - * @param followRedirects - * @return - */ - CloseableHttpClient getHttpClient(boolean followRedirects); - -}
\ No newline at end of file + /** + * Return an instance of a Apache HTTP client. + * + * @param followRedirects if <code>false</code>, the client does not flow 30x + * http redirects + * @return + */ + CloseableHttpClient getHttpClient(boolean followRedirects); + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyStoreUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyStoreUtils.java index e3d74066..99b87819 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyStoreUtils.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyStoreUtils.java @@ -1,36 +1,26 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 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: + * Licensed under the EUPL, Version 1.2 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: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * 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. - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ - + * 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.egiz.eaaf.core.impl.utils; import java.io.BufferedInputStream; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -42,88 +32,127 @@ import java.security.cert.CertificateFactory; /** * Utility for creating and loading key stores. - * + * * @author Paul Ivancsics * @version $Id$ */ public class KeyStoreUtils { - - /** - * JAVA KeyStore - */ - private static final String KEYSTORE_TYPE_JKS = "JKS"; - - /** - * PKCS12 KeyStore - */ - private static final String KEYSTORE_TYPE_PKCS12 = "PKCS12"; - - + + /** + * JAVA KeyStore. + */ + private static final String KEYSTORE_TYPE_JKS = "JKS"; + + /** + * PKCS12 KeyStore. + */ + private static final String KEYSTORE_TYPE_PKCS12 = "PKCS12"; /** * Loads a key store from file. - * + * * @param keystoreType key store type - * @param urlString URL of key store - * @param password password protecting the key store + * @param urlString URL of key store + * @param password password protecting the key store * @return key store loaded - * @throws IOException thrown while reading the key store from file + * @throws IOException thrown while reading the key store from file * @throws GeneralSecurityException thrown while creating the key store */ - public static KeyStore loadKeyStore( - String keystoreType, - String urlString, - String password) - throws IOException, GeneralSecurityException { - - URL keystoreURL = new URL(urlString); - InputStream in = keystoreURL.openStream(); + public static KeyStore loadKeyStore(final String keystoreType, final String urlString, + final String password) throws IOException, GeneralSecurityException { + + final URL keystoreUrl = new URL(urlString); + final InputStream in = keystoreUrl.openStream(); return loadKeyStore(keystoreType, in, password); } + + /** + * Load a KeyStore from Filesystem. + * + * @param keyStorePath Path to KeyStore + * @param password KeyStore password + * @return KeyStore + * @throws KeyStoreException In case of a keystore error + * @throws IOException In case of a general read error + */ + public static KeyStore loadKeyStore(final String keyStorePath, final String password) + throws KeyStoreException, IOException { + final URL keystoreUrl = new URL(keyStorePath); + final InputStream in = keystoreUrl.openStream(); + final InputStream isBuffered = new BufferedInputStream(in); + return loadKeyStore(isBuffered, password); + + } + /** - * Loads a key store from an <code>InputStream</code>, and - * closes the <code>InputStream</code>. - * + * Loads a key store from an <code>InputStream</code>, and closes the + * <code>InputStream</code>. + * * @param keystoreType key store type - * @param in input stream - * @param password password protecting the key store + * @param in input stream + * @param password password protecting the key store * @return key store loaded - * @throws IOException thrown while reading the key store from the stream + * @throws IOException thrown while reading the key store from the + * stream * @throws GeneralSecurityException thrown while creating the key store */ - public static KeyStore loadKeyStore( - String keystoreType, - InputStream in, - String password) - throws IOException, GeneralSecurityException { + public static KeyStore loadKeyStore(final String keystoreType, final InputStream in, + final String password) throws IOException, GeneralSecurityException { char[] chPassword = null; - if (password != null) + if (password != null) { chPassword = password.toCharArray(); - KeyStore ks = KeyStore.getInstance(keystoreType); + } + final KeyStore ks = KeyStore.getInstance(keystoreType); ks.load(in, chPassword); in.close(); return ks; } + + /** + * Loads a keyStore without knowing the keyStore type. + * + * @param is input stream + * @param password Password protecting the keyStore + * @return keyStore loaded + * @throws KeyStoreException thrown if keyStore cannot be loaded + * @throws IOException In case of a general error + */ + public static KeyStore loadKeyStore(final InputStream is, final String password) + throws KeyStoreException, IOException { + is.mark(1024 * 1024); + KeyStore ks = null; + try { + try { + ks = loadKeyStore(KEYSTORE_TYPE_PKCS12, is, password); + } catch (final IOException e2) { + is.reset(); + ks = loadKeyStore(KEYSTORE_TYPE_JKS, is, password); + } + } catch (final Exception e) { + e.printStackTrace(); + + } + return ks; + + } + /** - * Creates a key store from X509 certificate files, aliasing them with - * the index in the <code>String[]</code>, starting with <code>"0"</code>. - * - * @param keyStoreType key store type + * Creates a key store from X509 certificate files, aliasing them with the index + * in the <code>String[]</code>, starting with <code>"0"</code>. + * + * @param keyStoreType key store type * @param certFilenames certificate filenames * @return key store created - * @throws IOException thrown while reading the certificates from file - * @throws GeneralSecurityException thrown while creating the key store + * @throws Exception In case of an error */ - public static KeyStore createKeyStore( - String keyStoreType, - String[] certFilenames) - throws IOException, GeneralSecurityException { + public static KeyStore createKeyStore(final String keyStoreType, final String[] certFilenames) + throws Exception { - KeyStore ks = KeyStore.getInstance(keyStoreType); + final KeyStore ks = KeyStore.getInstance(keyStoreType); ks.load(null, null); for (int i = 0; i < certFilenames.length; i++) { - Certificate cert = loadCertificate(certFilenames[i]); + final Certificate cert = loadCertificate(certFilenames[i]); ks.setCertificateEntry("" + i, cert); } return ks; @@ -131,69 +160,34 @@ public class KeyStoreUtils { /** * Loads an X509 certificate from file. + * * @param certFilename filename * @return the certificate loaded - * @throws IOException thrown while reading the certificate from file - * @throws GeneralSecurityException thrown while creating the certificate + * @throws Exception In case of an IO exception */ - private static Certificate loadCertificate(String certFilename) - throws IOException, GeneralSecurityException { + private static Certificate loadCertificate(final String certFilename) + throws Exception { + FileInputStream in = null; + try { + in = new FileInputStream(certFilename); + final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + final Certificate cert = certFactory.generateCertificate(in); + in.close(); + return cert; - FileInputStream in = new FileInputStream(certFilename); - CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - Certificate cert = certFactory.generateCertificate(in); - in.close(); - return cert; - } - - - /** - * Loads a keyStore without knowing the keyStore type - * @param keyStorePath URL to the keyStore - * @param password Password protecting the keyStore - * @return keyStore loaded - * @throws KeyStoreException thrown if keyStore cannot be loaded - * @throws FileNotFoundException - * @throws IOException - */ - public static KeyStore loadKeyStore(String keyStorePath, String password) throws KeyStoreException, IOException{ - - //InputStream is = new FileInputStream(keyStorePath); - URL keystoreURL = new URL(keyStorePath); - InputStream in = keystoreURL.openStream(); - InputStream isBuffered = new BufferedInputStream(in); - return loadKeyStore(isBuffered, password); - - } - - /** - * Loads a keyStore without knowing the keyStore type - * @param in input stream - * @param password Password protecting the keyStore - * @return keyStore loaded - * @throws KeyStoreException thrown if keyStore cannot be loaded - * @throws FileNotFoundException - * @throws IOException - */ -public static KeyStore loadKeyStore(InputStream is, String password) throws KeyStoreException, IOException{ - is.mark(1024*1024); - KeyStore ks = null; - try { - try { - ks = loadKeyStore(KEYSTORE_TYPE_PKCS12, is, password); - } catch (IOException e2) { - is.reset(); - ks = loadKeyStore(KEYSTORE_TYPE_JKS, is, password); - } - } catch(Exception e) { - e.printStackTrace(); - - } - return ks; - - } - - + } catch (final Exception e) { + throw e; + + } finally { + if (in != null) { + try { + in.close(); + } catch (final IOException e) { + e.printStackTrace(); + } + } + } + } } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyValueUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyValueUtils.java index e753f19f..0c5eeb40 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyValueUtils.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyValueUtils.java @@ -1,29 +1,22 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 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: + * Licensed under the EUPL, Version 1.2 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: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * 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. - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ + * 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.egiz.eaaf.core.impl.utils; import java.util.ArrayList; @@ -44,332 +37,344 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** + * Utils to operate on Key/Value based configurations. + * * @author tlenz * */ public class KeyValueUtils { - private static final Logger log = LoggerFactory.getLogger(KeyValueUtils.class); - - public static final String KEY_DELIMITER = "."; - public static final String CSV_DELIMITER = ","; - public static final String KEYVVALUEDELIMITER = "="; - public static final String DEFAULT_VALUE = "default"; - - /** - * Convert Java properties into a Map<String, String> - * <br><br> - * <b>Important:</b> The key/values from properties must be of type String! - * - * @param properties - * @return - */ - public static Map<String, String> convertPropertiesToMap(Properties properties) { - return new HashMap<String, String>((Map) properties); - - //INFO Java8 solution ;) -// return properties.entrySet().stream().collect( -// Collectors.toMap( -// e -> e.getKey().toString(), -// e -> e.getValue().toString() -// ) -// ); - - } - - /** - * Extract the first child of an input key after a the prefix - * - * @param key Full input key - * @param prefix Prefix - * @return Child key {String} if it exists or null - */ - public static String getFirstChildAfterPrefix(String key, String prefix) { - final String idAfterPrefix = removePrefixFromKey(key, prefix); - if (idAfterPrefix != null) { - final int index = idAfterPrefix.indexOf(KEY_DELIMITER); - if (index > 0) { - final String adding = idAfterPrefix.substring(0, index); - if (!(adding.isEmpty())) { - return adding; - - } - } else if (!(idAfterPrefix.isEmpty())) { - return idAfterPrefix; - - } - - } - return null; - } - - /** - * Extract the prefix from an input key - * - * @param key Full input key - * @param suffix Suffix of this key - * @return Prefix {String} of the key or null if input key does not ends with postfix string - */ - public static String getPrefixFromKey(String key, String suffix) { - if (key != null && suffix != null && key.endsWith(suffix)) { - final String idPreforeSuffix = key.substring(0, key.length()-suffix.length()); - if (idPreforeSuffix.endsWith(KEY_DELIMITER)) - return idPreforeSuffix.substring(0, idPreforeSuffix.length()-1); - else - return idPreforeSuffix; - } - return null; - - } - - /** - * Remove a prefix string from a key - * - * @param key Full input key - * @param prefix Prefix, which should be removed - * @return The suffix of the input key or null if the input does not starts with the prefix - */ - public static String removePrefixFromKey(String key, String prefix) { - if (prefix == null) - prefix = new String(); - - if (key!=null && key.startsWith(prefix)) { - String afterPrefix = key.substring(prefix.length()); - final int index = afterPrefix.indexOf(KEY_DELIMITER); - - if (index == 0) { - afterPrefix = afterPrefix.substring(1); - - } - return afterPrefix; - - } - return null; - } - - /** - * Remove a prefix string from all keys in {Map<String, String>} of key/value pairs - * - * @param keys Input data of key/value pairs - * @param prefix Prefix which should be removed - * @return {Map<String, String>} of key/value pairs without prefix in key, but never null - */ - public static Map<String, String> removePrefixFromKeys(Map<String, String> keys, String prefix) { - final Map<String, String> result = new HashMap<String, String>(); - final Iterator<Entry<String, String>> interator = keys.entrySet().iterator(); - while(interator.hasNext()) { - final Entry<String, String> el = interator.next(); - final String newKey = removePrefixFromKey(el.getKey(), prefix); - if (StringUtils.isNotEmpty(newKey)) { - result.put(newKey, el.getValue()); - } - } - - return result; - } - - /** - * Get a subset of key/value pairs which starts with a prefix string - * The Prefix is removed from the key - * - * @param keys Input data of key/value pairs - * @param prefix Prefix string - * @return {Map<String, String>} of key/value pairs without prefix in key, but never null - */ - public static Map<String, String> getSubSetWithPrefix(Map<String, String> keys, String prefix) { - return removePrefixFromKeys(keys, prefix); - } - - - /** - * Add a prefix to key/value pairs to make the key absolute according to key namespace convention - * - * @param input Input key/value pairs which should be updated - * @param prefix Key prefix, which should be added if the key is not absolute - * @param absolutIdentifier Key identifier, which indicates an absolute key - * @return {Map<String, String>} of key/value pairs in which all keys are absolute but never null - */ - public static Map<String, String> makeKeysAbsolut(Map<String, String> input, String prefix, String absolutIdentifier) { - final Map<String, String> result = new HashMap<String, String>(); - final Iterator<Entry<String, String>> interator = input.entrySet().iterator(); - while(interator.hasNext()) { - final Entry<String, String> el = interator.next(); - if (!el.getKey().startsWith(absolutIdentifier)) { - //key is not absolute -> add prefix - result.put(prefix - + KEY_DELIMITER - + el.getKey(), - el.getValue()); - - } else { - //key is absolute - result.put(el.getKey(), el.getValue()); - } - } - return result; - } - - /** - * Get the parent key string from an input key - * - * @param key input key - * @return parent key or the empty String if no parent exists - */ - public static String getParentKey(String key) { - if (StringUtils.isNotEmpty(key)) { - final int index = key.lastIndexOf(KEY_DELIMITER); - if (index > 0) { - return key.substring(0, index); - - } - } - - return new String(); - } - - /** - * Find the highest free list counter - * - * @param input Array of list keys - * @param listPrefix {String} prefix of the list - * @return {int} highest free list counter - */ - public static int findNextFreeListCounter(String[] input, - String listPrefix) { - final List<Integer> counters = new ArrayList<Integer>(); - if (input == null || input.length == 0) - return 0; - - else { - for (final String key : input) { - final String listIndex = getFirstChildAfterPrefix(key, listPrefix); - counters.add(Integer.parseInt(listIndex)); - - } - Collections.sort(counters); - return counters.get(counters.size()-1) + 1; - } - } - - /** - * Find the highest free list counter - * - * @param keySet {Set<String>} of list keys - * @param listPrefix {String} prefix of the list - * @return {int} highest free list counter - */ - public static int findNextFreeListCounter(Set<String> keySet, - String listPrefix) { - if (keySet.isEmpty()) - return 0; - - final String[] array = new String[keySet.size()]; - keySet.toArray(array); - return findNextFreeListCounter(array, listPrefix); - } - - - /** - * Normalize a CSV encoded list of value of an key/value pair - * - * This method removes all whitespace at the begin or the - * end of CSV values and remove newLine signs at the end of value. - * The ',' is used as list delimiter - * - * @param value CSV encoded input data - * @return normalized CSV encoded data or null if {value} is null or empty - */ - public static String normalizeCSVValueString(String value) { - String normalizedCodes = null; - if (StringUtils.isNotEmpty(value)) { - final String[] codes = value.split(CSV_DELIMITER); - for (final String el: codes) { - if (normalizedCodes == null) - normalizedCodes = StringUtils.chomp(el.trim()); - else - normalizedCodes += "," + StringUtils.chomp(el.trim()); - - } - } - return normalizedCodes; - } - - - /** - * Check a String if it is a comma separated list of values - * - * This method uses the ',' as list delimiter. - * - * @param value CSV encoded input data - * @return true if the input data contains a ',' and has more then 1 list element, otherwise false - */ - public static boolean isCSVValueString(String value) { - if (StringUtils.isNotEmpty(value)) { - final String[] codes = value.split(CSV_DELIMITER); - if (codes.length >= 2) { - if (StringUtils.isNotEmpty(codes[1].trim())) - return true; - - } - } - - return false; - } - - /** - * Convert a CSV list to a List of CSV values - * <br><br> - * This method removes all whitespace at the begin or the - * end of CSV values and remove newLine signs at the end of value. - * The ',' is used as list delimiter - * - * @param csv CSV encoded input data - * @return List of CSV normalized values, but never null - */ - @Nonnull - public static List<String> getListOfCSVValues(@Nullable String csv) { - final List<String> list = new ArrayList<String>(); - if (StringUtils.isNotEmpty(csv)) { - final String[] values = csv.split(CSV_DELIMITER); - for (final String el: values) - list.add(el.trim()); - - } - - return list; - } - - /** - * Convert a List of String elements to a Map of Key/Value pairs - * <br> - * Every List element used as a key/value pair and the '=' sign represents the delimiter between key and value - * - * @param elements List of key/value elements - * @return Map of Key / Value pairs, but never null - */ - public static Map<String, String> convertListToMap(List<String> elements) { - final Map<String, String> map = new HashMap<String, String>(); - for (final String el : elements) { - if (el.contains(KEYVVALUEDELIMITER)) { - final String[] split = el.split(KEYVVALUEDELIMITER); - map.put(split[0], split[1]); - - } else - log.debug("Key/Value Mapper: '" + el + "' contains NO '='. Ignore it."); - - } - - return map; - } - - /** - * This method remove all newline delimiter (\n or \r\n) from input data - * - * @param value Input String - * @return Input String without newline characters - */ - public static String removeAllNewlineFromString(String value) { - return value.replaceAll("(\\t|\\r?\\n)+", ""); - - } - + private static final Logger log = LoggerFactory.getLogger(KeyValueUtils.class); + + public static final String KEY_DELIMITER = "."; + public static final String CSV_DELIMITER = ","; + public static final String KEYVVALUEDELIMITER = "="; + public static final String DEFAULT_VALUE = "default"; + + /** + * Convert Java properties into a Map String/String. <br> + * <b>Important:</b> The key/values from properties must be of type String! + * + * @param properties Java {@link Properties} that should be converted + * @return + */ + public static Map<String, String> convertPropertiesToMap(final Properties properties) { + return new HashMap<String, String>((Map) properties); + + // INFO Java8 solution ;) + // return properties.entrySet().stream().collect( + // Collectors.toMap( + // e -> e.getKey().toString(), + // e -> e.getValue().toString() + // ) + // ); + + } + + /** + * Extract the first child of an input key after a the prefix. + * + * @param key Full input key + * @param prefix Prefix + * @return Child key {String} if it exists or null + */ + public static String getFirstChildAfterPrefix(final String key, final String prefix) { + final String idAfterPrefix = removePrefixFromKey(key, prefix); + if (idAfterPrefix != null) { + final int index = idAfterPrefix.indexOf(KEY_DELIMITER); + if (index > 0) { + final String adding = idAfterPrefix.substring(0, index); + if (!adding.isEmpty()) { + return adding; + + } + } else if (!idAfterPrefix.isEmpty()) { + return idAfterPrefix; + + } + + } + return null; + } + + /** + * Extract the prefix from an input key. + * + * @param key Full input key + * @param suffix Suffix of this key + * @return Prefix {String} of the key or null if input key does not ends with + * postfix string + */ + public static String getPrefixFromKey(final String key, final String suffix) { + if (key != null && suffix != null && key.endsWith(suffix)) { + final String idPreforeSuffix = key.substring(0, key.length() - suffix.length()); + if (idPreforeSuffix.endsWith(KEY_DELIMITER)) { + return idPreforeSuffix.substring(0, idPreforeSuffix.length() - 1); + } else { + return idPreforeSuffix; + } + } + return null; + + } + + /** + * Remove a prefix string from a key. + * + * @param key Full input key + * @param prefix Prefix, which should be removed + * @return The suffix of the input key or null if the input does not starts with + * the prefix + */ + public static String removePrefixFromKey(final String key, String prefix) { + if (prefix == null) { + prefix = StringUtils.EMPTY; + + } + + if (key != null && key.startsWith(prefix)) { + String afterPrefix = key.substring(prefix.length()); + final int index = afterPrefix.indexOf(KEY_DELIMITER); + + if (index == 0) { + afterPrefix = afterPrefix.substring(1); + + } + return afterPrefix; + + } + return null; + } + + /** + * Remove a prefix string from all keys in Map String/String of key/value pairs. + * + * @param keys Input data of key/value pairs + * @param prefix Prefix which should be removed + * @return Map String/String of key/value pairs without prefix in key, but never + * null + */ + public static Map<String, String> removePrefixFromKeys(final Map<String, String> keys, + final String prefix) { + final Map<String, String> result = new HashMap<>(); + final Iterator<Entry<String, String>> interator = keys.entrySet().iterator(); + while (interator.hasNext()) { + final Entry<String, String> el = interator.next(); + final String newKey = removePrefixFromKey(el.getKey(), prefix); + if (StringUtils.isNotEmpty(newKey)) { + result.put(newKey, el.getValue()); + } + } + + return result; + } + + /** + * Get a subset of key/value pairs which starts with a prefix string The Prefix + * is removed from the key. + * + * @param keys Input data of key/value pairs + * @param prefix Prefix string + * @return Map String/String of key/value pairs without prefix in key, but never + * null + */ + public static Map<String, String> getSubSetWithPrefix(final Map<String, String> keys, + final String prefix) { + return removePrefixFromKeys(keys, prefix); + } + + /** + * Add a prefix to key/value pairs to make the key absolute according to key + * namespace convention. + * + * @param input Input key/value pairs which should be updated + * @param prefix Key prefix, which should be added if the key is not + * absolute + * @param absolutIdentifier Key identifier, which indicates an absolute key + * @return Map String/String of key/value pairs in which all keys are absolute + * but never null + */ + public static Map<String, String> makeKeysAbsolut(final Map<String, String> input, + final String prefix, final String absolutIdentifier) { + final Map<String, String> result = new HashMap<>(); + final Iterator<Entry<String, String>> interator = input.entrySet().iterator(); + while (interator.hasNext()) { + final Entry<String, String> el = interator.next(); + if (!el.getKey().startsWith(absolutIdentifier)) { + // key is not absolute -> add prefix + result.put(prefix + KEY_DELIMITER + el.getKey(), el.getValue()); + + } else { + // key is absolute + result.put(el.getKey(), el.getValue()); + } + } + return result; + } + + /** + * Get the parent key string from an input key. + * + * @param key input key + * @return parent key or the empty String if no parent exists + */ + public static String getParentKey(final String key) { + if (StringUtils.isNotEmpty(key)) { + final int index = key.lastIndexOf(KEY_DELIMITER); + if (index > 0) { + return key.substring(0, index); + + } + } + + return StringUtils.EMPTY; + } + + /** + * Find the highest free list counter. + * + * @param input Array of list keys + * @param listPrefix {String} prefix of the list + * @return {int} highest free list counter + */ + public static int findNextFreeListCounter(final String[] input, final String listPrefix) { + final List<Integer> counters = new ArrayList<>(); + if (input == null || input.length == 0) { + return 0; + } else { + for (final String key : input) { + final String listIndex = getFirstChildAfterPrefix(key, listPrefix); + counters.add(Integer.parseInt(listIndex)); + + } + Collections.sort(counters); + return counters.get(counters.size() - 1) + 1; + } + } + + /** + * Find the highest free list counter. + * + * @param keySet Set of list keys + * @param listPrefix {String} prefix of the list + * @return {int} highest free list counter + */ + public static int findNextFreeListCounter(final Set<String> keySet, final String listPrefix) { + if (keySet.isEmpty()) { + return 0; + } + + final String[] array = new String[keySet.size()]; + keySet.toArray(array); + return findNextFreeListCounter(array, listPrefix); + } + + /** + * Normalize a CSV encoded list of value of an key/value pair. + * + * <p> + * This method removes all whitespace at the begin or the end of CSV values and + * remove newLine signs at the end of value. The ',' is used as list delimiter + * </p> + * + * @param value CSV encoded input data + * @return normalized CSV encoded data or null if {value} is null or empty + */ + public static String normalizeCsvValueString(final String value) { + String normalizedCodes = null; + if (StringUtils.isNotEmpty(value)) { + final String[] codes = value.split(CSV_DELIMITER); + for (final String el : codes) { + if (normalizedCodes == null) { + normalizedCodes = StringUtils.chomp(el.trim()); + } else { + normalizedCodes += "," + StringUtils.chomp(el.trim()); + } + + } + } + return normalizedCodes; + } + + /** + * Check a String if it is a comma separated list of values. + * + * <p> + * This method uses the ',' as list delimiter. + * </p> + * + * @param value CSV encoded input data + * @return true if the input data contains a ',' and has more then 1 list + * element, otherwise false + */ + public static boolean isCsvValueString(final String value) { + if (StringUtils.isNotEmpty(value)) { + final String[] codes = value.split(CSV_DELIMITER); + if (codes.length >= 2 + && StringUtils.isNotEmpty(codes[1].trim())) { + return true; + + } + } + + return false; + } + + /** + * Convert a CSV list to a List of CSV values. <br> + * <br> + * This method removes all whitespace at the begin or the end of CSV values and + * remove newLine signs at the end of value. The ',' is used as list delimiter + * + * @param csv CSV encoded input data + * @return List of CSV normalized values, but never null + */ + @Nonnull + public static List<String> getListOfCsvValues(@Nullable final String csv) { + final List<String> list = new ArrayList<>(); + if (StringUtils.isNotEmpty(csv)) { + final String[] values = csv.split(CSV_DELIMITER); + for (final String el : values) { + list.add(el.trim()); + } + + } + + return list; + } + + /** + * Convert a List of String elements to a Map of Key/Value pairs. <br> + * Every List element used as a key/value pair and the '=' sign represents the + * delimiter between key and value + * + * @param elements List of key/value elements + * @return Map of Key / Value pairs, but never null + */ + public static Map<String, String> convertListToMap(final List<String> elements) { + final Map<String, String> map = new HashMap<>(); + for (final String el : elements) { + if (el.contains(KEYVVALUEDELIMITER)) { + final String[] split = el.split(KEYVVALUEDELIMITER); + map.put(split[0], split[1]); + + } else { + log.debug("Key/Value Mapper: '" + el + "' contains NO '='. Ignore it."); + } + + } + + return map; + } + + /** + * This method remove all newline delimiter (\n or \r\n) from input data. + * + * @param value Input String + * @return Input String without newline characters + */ + public static String removeAllNewlineFromString(final String value) { + return value.replaceAll("(\\t|\\r?\\n)+", ""); + + } + } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeIteratorAdapter.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeIteratorAdapter.java index ec57b92a..5d2a11d0 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeIteratorAdapter.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeIteratorAdapter.java @@ -1,30 +1,21 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 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: + * Licensed under the EUPL, Version 1.2 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: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * 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. - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ - + * 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.egiz.eaaf.core.impl.utils; @@ -38,55 +29,46 @@ import org.w3c.dom.traversal.NodeIterator; /** * A <code>NodeIterator</code> implementation based on a * <code>ListIterator</code>. - * + * * @see java.util.ListIterator * @see org.w3c.dom.traversal.NodeIterator - * + * */ public class NodeIteratorAdapter implements NodeIterator { /** The <code>ListIterator</code> to wrap. */ - private ListIterator nodeIterator; + private final ListIterator nodeIterator; /** * Create a new <code>NodeIteratorAdapter</code>. + * * @param nodeIterator The <code>ListIterator</code> to iterate over. */ - public NodeIteratorAdapter(ListIterator nodeIterator) { + public NodeIteratorAdapter(final ListIterator nodeIterator) { this.nodeIterator = nodeIterator; } - /** - * @see org.w3c.dom.traversal.NodeIterator#getRoot() - */ + @Override public Node getRoot() { return null; } - /** - * @see org.w3c.dom.traversal.NodeIterator#getWhatToShow() - */ + @Override public int getWhatToShow() { return NodeFilter.SHOW_ALL; } - /** - * @see org.w3c.dom.traversal.NodeIterator#getFilter() - */ + @Override public NodeFilter getFilter() { return null; } - /** - * @see org.w3c.dom.traversal.NodeIterator#getExpandEntityReferences() - */ + @Override public boolean getExpandEntityReferences() { return false; } - /** - * @see org.w3c.dom.traversal.NodeIterator#nextNode() - */ + @Override public Node nextNode() throws DOMException { if (nodeIterator.hasNext()) { return (Node) nodeIterator.next(); @@ -94,9 +76,7 @@ public class NodeIteratorAdapter implements NodeIterator { return null; } - /** - * @see org.w3c.dom.traversal.NodeIterator#previousNode() - */ + @Override public Node previousNode() throws DOMException { if (nodeIterator.hasPrevious()) { return (Node) nodeIterator.previous(); @@ -104,10 +84,9 @@ public class NodeIteratorAdapter implements NodeIterator { return null; } - /** - * @see org.w3c.dom.traversal.NodeIterator#detach() - */ + @Override public void detach() { + } } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeListAdapter.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeListAdapter.java index 69045aaa..83a6725c 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeListAdapter.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeListAdapter.java @@ -1,30 +1,21 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 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: + * Licensed under the EUPL, Version 1.2 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: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * 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. - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ - + * 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.egiz.eaaf.core.impl.utils; @@ -35,34 +26,30 @@ import org.w3c.dom.NodeList; /** * A <code>NodeList</code> implementation based on a <code>List</code>. - * + * * @see java.util.List * @see org.w3c.dom.NodeList */ public class NodeListAdapter implements NodeList { /** The <code>List</code> to wrap. */ - private List nodeList; - + private final List nodeList; + /** * Create a new <code>NodeListAdapter</code>. - * - * @param nodeList The <code>List</code> containing the nodes. + * + * @param nodeList The <code>List</code> containing the nodes. */ - public NodeListAdapter(List nodeList) { + public NodeListAdapter(final List nodeList) { this.nodeList = nodeList; } - /** - * @see org.w3c.dom.NodeList#item(int) - */ - public Node item(int index) { + @Override + public Node item(final int index) { return (Node) nodeList.get(index); } - /** - * @see org.w3c.dom.NodeList#getLength() - */ + @Override public int getLength() { return nodeList.size(); } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/Random.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/Random.java index e236b3a9..aedbbb7f 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/Random.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/Random.java @@ -1,29 +1,25 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 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: + * Licensed under the EUPL, Version 1.2 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: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * 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. - *******************************************************************************/ + * 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.egiz.eaaf.core.impl.utils; - +import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; @@ -38,139 +34,151 @@ import org.slf4j.LoggerFactory; import at.gv.egiz.eaaf.core.impl.idp.process.support.SecureRandomHolder; - /** - * Random number generator used to generate ID's + * Random number generator used to generate ID's. + * * @author Paul Ivancsics * @version $Id$ */ public class Random { - private static final Logger log = LoggerFactory.getLogger(Random.class); - - private final static char[] allowedPreFix = - {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', - 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}; - private static final DateFormat dateFormater = new SimpleDateFormat("yyyyddMM"); - - /** random number generator used */ - private static SecureRandom random; - //private static SeedGenerator seedgenerator; - - static { - try { - random = SecureRandom.getInstance("SHA256PRNG-FIPS186"); - - } catch (NoSuchAlgorithmException e) { - log.warn("Can NOT initialize SecureRandom with: 'SHA256PRNG-FIPS186'. Use 'StrongSecureRandom' as backup"); - random = SecureRandomHolder.getInstance(); - - } - - - //random = iaik.security.random.SHA256FIPS186Random.getDefault(); - } - - /** - * Generate a unique process reference-value [160bit], which always starts with a letter - * <br> - * This unique ID consists of single letter, a 64bit date String[yyyyddMM], - * and a 88bit random value. - * - * @return 160bit ID, which is hex encoded - */ - public static String nextProcessReferenceValue() { - //pre-process all three parts of a unique reference value - String now = dateFormater.format(new Date()); //8 bytes = 64bit - byte[] randValue = nextByteRandom(11); - char preFix = allowedPreFix[Math.abs(random.nextInt() % allowedPreFix.length)]; - - //generate ID - String returnValue = preFix + new String(Hex.encodeHex(ArrayUtils.addAll(now.getBytes(), randValue))); // 20 bytes = 160 bits - if (returnValue.length() > 40) - return returnValue.substring(0, 40); - else - return returnValue; - - } - - - - /** - * Creates a new random number [256bit], and encode it as hex value. - * - * @return random hex encoded value [256bit] - */ - public static String nextHexRandom32() { - return new String(Hex.encodeHex(nextByteRandom(32))); // 32 bytes = 256 bits - - } - - /** - * Creates a new random number [128bit], and encode it as hex value. - * - * @return random hex encoded value [128bit] - */ - public static String nextHexRandom16() { - return new String(Hex.encodeHex(nextByteRandom(16))); // 16 bytes = 128 bits - - } - - /** - * Creates a new random number [64bit], to be used as an ID. - * - * @return random long as a String [64bit] - */ - public static String nextLongRandom() { - return "".concat(String.valueOf(Math.abs(generateLongRandom(32)))); // 32 bytes = 256 bits - - } - + private static final Logger log = LoggerFactory.getLogger(Random.class); + + private static final char[] allowedPreFix = + { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', + 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; + + /** random number generator used. */ + private static SecureRandom random; + // private static SeedGenerator seedgenerator; + + static { + try { + random = SecureRandom.getInstance("SHA256PRNG-FIPS186"); + + } catch (final NoSuchAlgorithmException e) { + log.warn( + "Can NOT initialize SecureRandom with: 'SHA256PRNG-FIPS186'. Use 'StrongSecureRandom' as backup"); + random = SecureRandomHolder.getInstance(); + + } + + // random = iaik.security.random.SHA256FIPS186Random.getDefault(); + } + + /** + * Generate a unique process reference-value [160bit], which always starts with + * a letter <br> + * This unique ID consists of single letter, a 64bit date String[yyyyddMM], and + * a 88bit random value. + * + * @return 160bit ID, which is hex encoded + */ + public static String nextProcessReferenceValue() { + // pre-process all three parts of a unique reference value + final DateFormat dateFormater = new SimpleDateFormat("yyyyddMM"); + final String now = dateFormater.format(new Date()); // 8 bytes = 64bit + final byte[] randValue = nextByteRandom(11); + final char preFix = allowedPreFix[Math.abs(random.nextInt() % allowedPreFix.length)]; + + // generate ID + String returnValue; + try { + returnValue = preFix + new String(Hex.encodeHex(ArrayUtils.addAll(now.getBytes("UTF-8"), randValue))); + + // 20 bytes = 160 bits + if (returnValue.length() > 40) { + return returnValue.substring(0, 40); + } else { + return returnValue; + } + + } catch (final UnsupportedEncodingException e) { + throw new RuntimeException(e); + + } + + } + + /** + * Creates a new random number [256bit], and encode it as hex value. + * + * @return random hex encoded value [256bit] + */ + public static String nextHexRandom32() { + return new String(Hex.encodeHex(nextByteRandom(32))); // 32 bytes = 256 bits + + } + + /** + * Creates a new random number [128bit], and encode it as hex value. + * + * @return random hex encoded value [128bit] + */ + public static String nextHexRandom16() { + return new String(Hex.encodeHex(nextByteRandom(16))); // 16 bytes = 128 bits + + } + + /** + * Creates a new random number [64bit], to be used as an ID. + * + * @return random long as a String [64bit] + */ + public static String nextLongRandom() { + return "".concat(String.valueOf(Math.abs(generateLongRandom(32)))); // 32 bytes = 256 bits + + } + /** * Creates a new random number, to be used as an ID. - * + * * @return random long as a String [64bit] */ - @Deprecated - public static String nextRandom() { - long l = ByteBuffer.wrap(nextByteRandom(32)).getLong(); // 32 bytes = 256 bits - return "" + Math.abs(l); - + @Deprecated + public static String nextRandom() { + final long l = ByteBuffer.wrap(nextByteRandom(32)).getLong(); // 32 bytes = 256 bits + return "" + Math.abs(l); + } - -/** - * Creates a new random byte[] - * - * @param size Size of random number in byte - * @return - */ -public static byte[] nextBytes(int size) { - return nextByteRandom(size); - -} - + + /** + * Creates a new random byte[]. + * + * @param size Size of random number in byte + * @return + */ + public static byte[] nextBytes(final int size) { + return nextByteRandom(size); + + } + + /** + * initialize random-number generator. + */ public static void seedRandom() { - //TODO: implement reflection on IAIK Seed generator -// seedgenerator = iaik.security.random.AutoSeedGenerator.getDefault(); -// if (seedgenerator.seedAvailable()) -// random.setSeed(seedgenerator.getSeed()); - - random.setSeed(System.nanoTime()); + // TODO: implement reflection on IAIK Seed generator + // seedgenerator = iaik.security.random.AutoSeedGenerator.getDefault(); + // if (seedgenerator.seedAvailable()) + // random.setSeed(seedgenerator.getSeed()); + + random.setSeed(System.nanoTime()); } - - private static long generateLongRandom(int size) { - return ByteBuffer.wrap(nextByteRandom(size)).getLong(); - } - + + private static long generateLongRandom(final int size) { + return ByteBuffer.wrap(nextByteRandom(size)).getLong(); + } + /** - * Generate a new random number - * + * Generate a new random number. + * * @param size Size of random number in byte * @return */ - private static synchronized byte[] nextByteRandom(int size) { - byte[] b = new byte[size]; - random.nextBytes(b); - return b; - + private static synchronized byte[] nextByteRandom(final int size) { + final byte[] b = new byte[size]; + random.nextBytes(b); + return b; + } } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SecurePendingRequestIdGenerationStrategy.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SecurePendingRequestIdGenerationStrategy.java index f0ef9b38..bc770a8c 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SecurePendingRequestIdGenerationStrategy.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SecurePendingRequestIdGenerationStrategy.java @@ -1,6 +1,7 @@ package at.gv.egiz.eaaf.core.impl.utils; import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; @@ -27,188 +28,211 @@ import org.springframework.lang.Nullable; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; import at.gv.egiz.eaaf.core.api.utils.IPendingRequestIdGenerationStrategy; -import at.gv.egiz.eaaf.core.exceptions.EAAFConfigurationException; -import at.gv.egiz.eaaf.core.exceptions.EAAFException; -import at.gv.egiz.eaaf.core.exceptions.EAAFIllegalStateException; +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.exceptions.EaafIllegalStateException; import at.gv.egiz.eaaf.core.exceptions.PendingReqIdValidationException; /** - * PendingRequestId generation strategy based on signed tokens that facilitates extended token validation - * + * PendingRequestId generation strategy based on signed tokens that facilitates + * extended token validation. + * * @author tlenz * */ -public class SecurePendingRequestIdGenerationStrategy implements IPendingRequestIdGenerationStrategy { - private static final Logger log = LoggerFactory.getLogger(SecurePendingRequestIdGenerationStrategy.class); - - @Autowired(required=true) IConfiguration baseConfig; - - public static final String CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET = "core.pendingrequestid.digist.secret"; - public static final String CONFIG_PROP_PENDINGREQUESTID_DIGIST_ALGORITHM = "core.pendingrequestid.digist.algorithm"; - public static final String CONFIG_PROP_PENDINGREQUESTID_MAX_LIFETIME = "core.pendingrequestid.maxlifetime"; - - public static final String DEFAULT_PENDINGREQUESTID_DIGIST_ALGORITHM = "HmacSHA256"; - public static final String DEFAULT_PENDINGREQUESTID_MAX_LIFETIME = "300"; - - private static final int ENCODED_TOKEN_PARTS = 3; - private static final String TOKEN_SEPARATOR = "|"; - private static final DateTimeFormatter TOKEN_TEXTUAL_DATE_FORMAT = - DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss SSS"); - - private int maxPendingRequestIdLifeTime = 300; - private final int maxPendingReqIdSize = 1024; - private String digistAlgorithm = null; - private SecretKey key = null; - private final byte[] salt = "notRequiredInThisScenario".getBytes(); - - @Override - public String generateExternalPendingRequestId() throws EAAFException { - try { - final String toSign = buildInternalToken(Random.nextLongRandom(), DateTime.now()); - final StringBuilder externalPendingRequestId= new StringBuilder(); - externalPendingRequestId.append(toSign); - externalPendingRequestId.append(TOKEN_SEPARATOR); - externalPendingRequestId.append(Base64.getEncoder().encodeToString(calculateHMAC(toSign))); - return Base64.getUrlEncoder().encodeToString(externalPendingRequestId.toString().getBytes("UTF-8")); - - } catch (final UnsupportedEncodingException e) { - throw new EAAFException("internal.99", new Object[] {e.getMessage()}, e); - - } - - } - - @Override - public String getPendingRequestIdWithOutChecks(String externalPendingReqId) throws PendingReqIdValidationException { - final String[] tokenElements = extractTokens(externalPendingReqId); - return tokenElements[1]; - - } - - @Override - public String validateAndGetPendingRequestId(String externalPendingReqId) throws PendingReqIdValidationException { - try { - final String[] tokenElements = extractTokens(externalPendingReqId); - final String internalPendingReqId = tokenElements[1]; - final DateTime timeStamp = TOKEN_TEXTUAL_DATE_FORMAT.parseDateTime(tokenElements[0]); - - log.trace("Checking HMAC from externalPendingReqId ... "); - final byte[] tokenDigest = Base64.getDecoder().decode(tokenElements[2]); - final byte[] refDigist = calculateHMAC(buildInternalToken(internalPendingReqId, timeStamp)); - if (!Arrays.equals(tokenDigest, refDigist)) { - log.warn("Digest of Token does NOT match"); - log.debug("Token: {} | Ref: {}", tokenDigest, refDigist); - throw new PendingReqIdValidationException(null, "Digest of pendingRequestId does NOT match"); - - } - log.debug("PendingRequestId HMAC digest check successful"); - - log.trace("Checking valid period ... "); - final DateTime now = DateTime.now(); - if (timeStamp.withFieldAdded( - DurationFieldType.seconds(), maxPendingRequestIdLifeTime).isBefore(now)) { - log.warn("Token exceeds the valid period"); - log.debug("Token: {} | Now: {}", timeStamp, now ); - throw new PendingReqIdValidationException(internalPendingReqId, "PendingRequestId exceeds the valid period"); - - } - log.debug("Token valid-period check successful"); - - return internalPendingReqId; - - - } catch (final IllegalArgumentException | EAAFIllegalStateException e) { - log.warn("Token is NOT a valid String. Msg: {}", e.getMessage()); - log.debug("TokenValue: {}", externalPendingReqId); - throw new PendingReqIdValidationException(null, "PendingReqId is NOT a valid String", e); - - } - } - - @NonNull - private String[] extractTokens(@Nullable String externalPendingReqId) throws PendingReqIdValidationException { - if (StringUtils.isEmpty(externalPendingReqId)) { - log.info("PendingReqId is 'null' or empty"); - throw new PendingReqIdValidationException(null, "PendingReqId is 'null' or empty"); - - } - - log.trace("RAW external pendingReqId: {}", externalPendingReqId); - final byte[] externalPendingReqIdBytes = Base64.getUrlDecoder().decode(externalPendingReqId); - - if (externalPendingReqIdBytes.length > maxPendingReqIdSize) { - log.warn("pendingReqId size exceeds {}", maxPendingReqIdSize); - throw new PendingReqIdValidationException(null, "pendingReqId exceeds max.size: " + maxPendingReqIdSize); - - } - - final String stringToken = new String(externalPendingReqIdBytes); - if (StringUtils.countMatches(stringToken, TOKEN_SEPARATOR) == ENCODED_TOKEN_PARTS - 1) { - final String[] tokenElements = StringUtils.split(stringToken, - TOKEN_SEPARATOR, ENCODED_TOKEN_PARTS); - return tokenElements; - - } else { - log.warn("PendingRequestId has an unvalid format"); - log.debug("PendingRequestId: {}", stringToken); - throw new PendingReqIdValidationException(null, "PendingReqId has an unvalid format"); - - } - - } - - - @PostConstruct - private void initialize() throws EAAFConfigurationException { - log.debug("Initializing " + this.getClass().getName() + " ... "); - - final String pendingReqIdDigistSecret = baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET); - if (StringUtils.isEmpty(pendingReqIdDigistSecret)) - throw new EAAFConfigurationException("config.08", new Object[] {CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET}); - - digistAlgorithm = baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_DIGIST_ALGORITHM, DEFAULT_PENDINGREQUESTID_DIGIST_ALGORITHM); - - maxPendingRequestIdLifeTime = Integer.valueOf( - baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_MAX_LIFETIME, DEFAULT_PENDINGREQUESTID_MAX_LIFETIME)); - - try { - final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WITHHMACSHA256"); - final KeySpec spec = new PBEKeySpec(pendingReqIdDigistSecret.toCharArray(), salt, 10000, 128); - key = keyFactory.generateSecret(spec); - - - } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { - log.error("Can NOT initialize TokenService with configuration object", e); - throw new EAAFConfigurationException("config.09", - new Object[] { CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET, - "Can NOT generate HMAC key"}, - e); - - } - - log.info(this.getClass().getName() + " initialized with digistAlg: {} and maxLifeTime: {}", digistAlgorithm, maxPendingRequestIdLifeTime); - - } - - private String buildInternalToken(String internalPendingReqId, DateTime now) { - return new StringBuilder() - .append(TOKEN_TEXTUAL_DATE_FORMAT.print(now)) - .append(TOKEN_SEPARATOR) - .append(internalPendingReqId).toString(); - } - - private byte[] calculateHMAC(String toSign) throws EAAFIllegalStateException { - try { - final Mac mac = Mac.getInstance(digistAlgorithm); - mac.init(key); - return mac.doFinal(toSign.getBytes("UTF-8")); - - } catch (UnsupportedEncodingException | NoSuchAlgorithmException | InvalidKeyException e) { - log.error("Can NOT generate secure pendingRequestId", e); - throw new EAAFIllegalStateException(new Object[] {"Can NOT caluclate digist for secure pendingRequestId"}, e); - - } - - } +public class SecurePendingRequestIdGenerationStrategy + implements IPendingRequestIdGenerationStrategy { + private static final Logger log = + LoggerFactory.getLogger(SecurePendingRequestIdGenerationStrategy.class); + + @Autowired(required = true) + IConfiguration baseConfig; + + public static final String CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET = + "core.pendingrequestid.digist.secret"; + public static final String CONFIG_PROP_PENDINGREQUESTID_DIGIST_ALGORITHM = + "core.pendingrequestid.digist.algorithm"; + public static final String CONFIG_PROP_PENDINGREQUESTID_MAX_LIFETIME = + "core.pendingrequestid.maxlifetime"; + + public static final String DEFAULT_PENDINGREQUESTID_DIGIST_ALGORITHM = "HmacSHA256"; + public static final String DEFAULT_PENDINGREQUESTID_MAX_LIFETIME = "300"; + + private static final int ENCODED_TOKEN_PARTS = 3; + private static final String TOKEN_SEPARATOR = "|"; + private static final DateTimeFormatter TOKEN_TEXTUAL_DATE_FORMAT = + DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss SSS"); + + private int maxPendingRequestIdLifeTime = 300; + private final int maxPendingReqIdSize = 1024; + private String digistAlgorithm = null; + private SecretKey key = null; + private final byte[] salt = "notRequiredInThisScenario".getBytes(Charset.defaultCharset()); + + @Override + public String generateExternalPendingRequestId() throws EaafException { + try { + final String toSign = buildInternalToken(Random.nextLongRandom(), DateTime.now()); + final StringBuilder externalPendingRequestId = new StringBuilder(); + externalPendingRequestId.append(toSign); + externalPendingRequestId.append(TOKEN_SEPARATOR); + externalPendingRequestId.append(Base64.getEncoder().encodeToString(calculateHmac(toSign))); + return Base64.getUrlEncoder() + .encodeToString(externalPendingRequestId.toString().getBytes("UTF-8")); + + } catch (final UnsupportedEncodingException e) { + throw new EaafException("internal.99", new Object[] { e.getMessage() }, e); + + } + + } + + @Override + public String getPendingRequestIdWithOutChecks(final String externalPendingReqId) + throws PendingReqIdValidationException { + try { + final String[] tokenElements = extractTokens(externalPendingReqId); + return tokenElements[1]; + + } catch (final UnsupportedEncodingException e) { + throw new RuntimeException(e); + + } + } + + @Override + public String validateAndGetPendingRequestId(final String externalPendingReqId) + throws PendingReqIdValidationException { + try { + final String[] tokenElements = extractTokens(externalPendingReqId); + final String internalPendingReqId = tokenElements[1]; + final DateTime timeStamp = TOKEN_TEXTUAL_DATE_FORMAT.parseDateTime(tokenElements[0]); + + log.trace("Checking HMAC from externalPendingReqId ... "); + final byte[] tokenDigest = Base64.getDecoder().decode(tokenElements[2]); + final byte[] refDigist = calculateHmac(buildInternalToken(internalPendingReqId, timeStamp)); + if (!Arrays.equals(tokenDigest, refDigist)) { + log.warn("Digest of Token does NOT match"); + log.debug("Token: {} | Ref: {}", tokenDigest, refDigist); + throw new PendingReqIdValidationException(null, + "Digest of pendingRequestId does NOT match"); + + } + log.debug("PendingRequestId HMAC digest check successful"); + + log.trace("Checking valid period ... "); + final DateTime now = DateTime.now(); + if (timeStamp.withFieldAdded(DurationFieldType.seconds(), maxPendingRequestIdLifeTime) + .isBefore(now)) { + log.warn("Token exceeds the valid period"); + log.debug("Token: {} | Now: {}", timeStamp, now); + throw new PendingReqIdValidationException(internalPendingReqId, + "PendingRequestId exceeds the valid period"); + + } + log.debug("Token valid-period check successful"); + + return internalPendingReqId; + + } catch (final IllegalArgumentException | EaafIllegalStateException e) { + log.warn("Token is NOT a valid String. Msg: {}", e.getMessage()); + log.debug("TokenValue: {}", externalPendingReqId); + throw new PendingReqIdValidationException(null, "PendingReqId is NOT a valid String", e); + + } catch (final UnsupportedEncodingException e) { + throw new RuntimeException(e); + + } + } + + @NonNull + private String[] extractTokens(@Nullable final String externalPendingReqId) + throws PendingReqIdValidationException, UnsupportedEncodingException { + if (StringUtils.isEmpty(externalPendingReqId)) { + log.info("PendingReqId is 'null' or empty"); + throw new PendingReqIdValidationException(null, "PendingReqId is 'null' or empty"); + + } + + log.trace("RAW external pendingReqId: {}", externalPendingReqId); + final byte[] externalPendingReqIdBytes = Base64.getUrlDecoder().decode(externalPendingReqId); + + if (externalPendingReqIdBytes.length > maxPendingReqIdSize) { + log.warn("pendingReqId size exceeds {}", maxPendingReqIdSize); + throw new PendingReqIdValidationException(null, + "pendingReqId exceeds max.size: " + maxPendingReqIdSize); + + } + + final String stringToken = new String(externalPendingReqIdBytes, "UTF-8"); + if (StringUtils.countMatches(stringToken, TOKEN_SEPARATOR) == ENCODED_TOKEN_PARTS - 1) { + final String[] tokenElements = + StringUtils.split(stringToken, TOKEN_SEPARATOR, ENCODED_TOKEN_PARTS); + return tokenElements; + + } else { + log.warn("PendingRequestId has an unvalid format"); + log.debug("PendingRequestId: {}", stringToken); + throw new PendingReqIdValidationException(null, "PendingReqId has an unvalid format"); + + } + + } + + @PostConstruct + private void initialize() throws EaafConfigurationException { + log.debug("Initializing " + this.getClass().getName() + " ... "); + + final String pendingReqIdDigistSecret = + baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET); + if (StringUtils.isEmpty(pendingReqIdDigistSecret)) { + throw new EaafConfigurationException("config.08", + new Object[] { CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET }); + } + + digistAlgorithm = baseConfig.getBasicConfiguration( + CONFIG_PROP_PENDINGREQUESTID_DIGIST_ALGORITHM, DEFAULT_PENDINGREQUESTID_DIGIST_ALGORITHM); + + maxPendingRequestIdLifeTime = + Integer.parseInt(baseConfig.getBasicConfiguration(CONFIG_PROP_PENDINGREQUESTID_MAX_LIFETIME, + DEFAULT_PENDINGREQUESTID_MAX_LIFETIME)); + + try { + final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WITHHMACSHA256"); + final KeySpec spec = new PBEKeySpec(pendingReqIdDigistSecret.toCharArray(), salt, 10000, 128); + key = keyFactory.generateSecret(spec); + + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + log.error("Can NOT initialize TokenService with configuration object", e); + throw new EaafConfigurationException("config.09", + new Object[] { CONFIG_PROP_PENDINGREQUESTID_DIGIST_SECRET, "Can NOT generate HMAC key" }, + e); + + } + + log.info(this.getClass().getName() + " initialized with digistAlg: {} and maxLifeTime: {}", + digistAlgorithm, maxPendingRequestIdLifeTime); + + } + + private String buildInternalToken(final String internalPendingReqId, final DateTime now) { + return new StringBuilder().append(TOKEN_TEXTUAL_DATE_FORMAT.print(now)).append(TOKEN_SEPARATOR) + .append(internalPendingReqId).toString(); + } + + private byte[] calculateHmac(final String toSign) throws EaafIllegalStateException { + try { + final Mac mac = Mac.getInstance(digistAlgorithm); + mac.init(key); + return mac.doFinal(toSign.getBytes("UTF-8")); + + } catch (UnsupportedEncodingException | NoSuchAlgorithmException | InvalidKeyException e) { + log.error("Can NOT generate secure pendingRequestId", e); + throw new EaafIllegalStateException( + new Object[] { "Can NOT caluclate digist for secure pendingRequestId" }, e); + + } + + } } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ServletUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ServletUtils.java index 38e873e2..c8865465 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ServletUtils.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ServletUtils.java @@ -1,43 +1,41 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 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: + * Licensed under the EUPL, Version 1.2 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: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * 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. - *******************************************************************************/ + * 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.egiz.eaaf.core.impl.utils; import javax.servlet.http.HttpServletRequest; public class ServletUtils { - - - public static String getBaseUrl( HttpServletRequest request ) { - if ( ( request.getServerPort() == 80 ) || - ( request.getServerPort() == 443 ) ) - return request.getScheme() + "://" + - request.getServerName() + - request.getContextPath(); - else - return request.getScheme() + "://" + - request.getServerName() + ":" + request.getServerPort() + - request.getContextPath(); - } - + + /** + * Get Context URL from http request. + * + * @param request http Request + * @return Context URL + */ + public static String getBaseUrl(final HttpServletRequest request) { + if (request.getServerPort() == 80 || request.getServerPort() == 443) { + return request.getScheme() + "://" + request.getServerName() + request.getContextPath(); + } else { + return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + + request.getContextPath(); + } + } + } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SimplePendingRequestIdGenerationStrategy.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SimplePendingRequestIdGenerationStrategy.java index 6b8fe9b7..78f0cdec 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SimplePendingRequestIdGenerationStrategy.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/SimplePendingRequestIdGenerationStrategy.java @@ -6,33 +6,37 @@ import at.gv.egiz.eaaf.core.api.utils.IPendingRequestIdGenerationStrategy; import at.gv.egiz.eaaf.core.exceptions.PendingReqIdValidationException; /** - * Simple pendingRequestId generation strategy that facilitates no extended validation - * + * Simple pendingRequestId generation strategy that facilitates no extended + * validation. + * * @author tlenz * */ -public class SimplePendingRequestIdGenerationStrategy implements IPendingRequestIdGenerationStrategy { - - @Override - public String generateExternalPendingRequestId() { - return Random.nextLongRandom(); - - } - - @Override - public String validateAndGetPendingRequestId(String pendingReqId) throws PendingReqIdValidationException { - return getPendingRequestIdWithOutChecks(pendingReqId); - - } - - @Override - public String getPendingRequestIdWithOutChecks(String externalPendingReqId) throws PendingReqIdValidationException { - if (StringUtils.isEmpty(externalPendingReqId)) - throw new PendingReqIdValidationException(externalPendingReqId, "PendingRequestId is empty or null"); - - - - return externalPendingReqId; - } +public class SimplePendingRequestIdGenerationStrategy + implements IPendingRequestIdGenerationStrategy { + + @Override + public String generateExternalPendingRequestId() { + return Random.nextLongRandom(); + + } + + @Override + public String validateAndGetPendingRequestId(final String pendingReqId) + throws PendingReqIdValidationException { + return getPendingRequestIdWithOutChecks(pendingReqId); + + } + + @Override + public String getPendingRequestIdWithOutChecks(final String externalPendingReqId) + throws PendingReqIdValidationException { + if (StringUtils.isEmpty(externalPendingReqId)) { + throw new PendingReqIdValidationException(externalPendingReqId, + "PendingRequestId is empty or null"); + } + + return externalPendingReqId; + } } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/StreamUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/StreamUtils.java index 530da777..22a6de2b 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/StreamUtils.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/StreamUtils.java @@ -1,30 +1,21 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 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: + * Licensed under the EUPL, Version 1.2 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: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * 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. - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ - + * 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.egiz.eaaf.core.impl.utils; @@ -32,38 +23,37 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.PrintStream; /** * Utility methods for streams. - * + * * @author Patrick Peck * @version $Id$ */ public class StreamUtils { - + /** * Compare the contents of two <code>InputStream</code>s. - * + * * @param is1 The 1st <code>InputStream</code> to compare. * @param is2 The 2nd <code>InputStream</code> to compare. * @return boolean <code>true</code>, if both streams contain the exactly the - * same content, <code>false</code> otherwise. + * same content, <code>false</code> otherwise. * @throws IOException An error occurred reading one of the streams. */ - public static boolean compareStreams(InputStream is1, InputStream is2) - throws IOException { - - byte[] buf1 = new byte[256]; - byte[] buf2 = new byte[256]; + public static boolean compareStreams(final InputStream is1, final InputStream is2) + throws IOException { + + final byte[] buf1 = new byte[256]; + final byte[] buf2 = new byte[256]; int length1; int length2; - + try { while (true) { length1 = is1.read(buf1); length2 = is2.read(buf2); - + if (length1 != length2) { return false; } @@ -74,128 +64,127 @@ public class StreamUtils { return false; } } - } catch (IOException e) { + } catch (final IOException e) { throw e; } finally { // close both streams try { is1.close(); is2.close(); - } catch (IOException e) { - // ignore this + } catch (final IOException e) { + e.printStackTrace(); + } } } - + /** * Compare two byte arrays, up to a given maximum length. - * - * @param b1 1st byte array to compare. - * @param b2 2nd byte array to compare. + * + * @param b1 1st byte array to compare. + * @param b2 2nd byte array to compare. * @param length The maximum number of bytes to compare. * @return <code>true</code>, if the byte arrays are equal, <code>false</code> - * otherwise. + * otherwise. */ - private static boolean compareBytes(byte[] b1, byte[] b2, int length) { + private static boolean compareBytes(final byte[] b1, final byte[] b2, final int length) { if (b1.length != b2.length) { return false; } - + for (int i = 0; i < b1.length && i < length; i++) { if (b1[i] != b2[i]) { return false; } } - + return true; } /** * Reads a byte array from a stream. + * * @param in The <code>InputStream</code> to read. * @return The bytes contained in the given <code>InputStream</code>. * @throws IOException on any exception thrown */ - public static byte[] readStream(InputStream in) throws IOException { + public static byte[] readStream(final InputStream in) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); + final ByteArrayOutputStream out = new ByteArrayOutputStream(); copyStream(in, out, null); - - /* - ByteArrayOutputStream out = new ByteArrayOutputStream(); - int b; - while ((b = in.read()) >= 0) - out.write(b); - - */ + + /* + * ByteArrayOutputStream out = new ByteArrayOutputStream(); int b; while ((b = + * in.read()) >= 0) out.write(b); + * + */ in.close(); return out.toByteArray(); } /** * Reads a <code>String</code> from a stream, using given encoding. - * @param in The <code>InputStream</code> to read. - * @param encoding The character encoding to use for converting the bytes - * of the <code>InputStream</code> into a <code>String</code>. - * @return The content of the given <code>InputStream</code> converted into - * a <code>String</code>. + * + * @param in The <code>InputStream</code> to read. + * @param encoding The character encoding to use for converting the bytes of the + * <code>InputStream</code> into a <code>String</code>. + * @return The content of the given <code>InputStream</code> converted into a + * <code>String</code>. * @throws IOException on any exception thrown */ - public static String readStream(InputStream in, String encoding) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); + public static String readStream(final InputStream in, final String encoding) throws IOException { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); copyStream(in, out, null); /* - ByteArrayOutputStream out = new ByteArrayOutputStream(); - int b; - while ((b = in.read()) >= 0) - out.write(b); - */ + * ByteArrayOutputStream out = new ByteArrayOutputStream(); int b; while ((b = + * in.read()) >= 0) out.write(b); + */ in.close(); return out.toString(encoding); } - + /** - * Reads all data (until EOF is reached) from the given source to the + * Reads all data (until EOF is reached) from the given source to the * destination stream. If the destination stream is null, all data is dropped. - * It uses the given buffer to read data and forward it. If the buffer is - * null, this method allocates a buffer. + * It uses the given buffer to read data and forward it. If the buffer is null, + * this method allocates a buffer. * - * @param source The stream providing the data. - * @param destination The stream that takes the data. If this is null, all - * data from source will be read and discarded. - * @param buffer The buffer to use for forwarding. If it is null, the method - * allocates a buffer. - * @exception IOException If reading from the source or writing to the + * @param source The stream providing the data. + * @param destination The stream that takes the data. If this is null, all data + * from source will be read and discarded. + * @param buffer The buffer to use for forwarding. If it is null, the + * method allocates a buffer. + * @exception IOException If reading from the source or writing to the * destination fails. */ - private static void copyStream(InputStream source, OutputStream destination, byte[] buffer) throws IOException { + private static void copyStream(final InputStream source, final OutputStream destination, + byte[] buffer) throws IOException { if (source == null) { throw new NullPointerException("Argument \"source\" must not be null."); } if (buffer == null) { buffer = new byte[8192]; } - + if (destination != null) { int bytesRead; while ((bytesRead = source.read(buffer)) >= 0) { destination.write(buffer, 0, bytesRead); } - } else { - while (source.read(buffer) >= 0); - } - } - - /** - * Gets the stack trace of the <code>Throwable</code> passed in as a string. - * @param t The <code>Throwable</code>. - * @return a String representing the stack trace of the <code>Throwable</code>. - */ - public static String getStackTraceAsString(Throwable t) - { - ByteArrayOutputStream stackTraceBIS = new ByteArrayOutputStream(); - t.printStackTrace(new PrintStream(stackTraceBIS)); - return new String(stackTraceBIS.toByteArray()); + } } + + // /** + // * Gets the stack trace of the <code>Throwable</code> passed in as a string. + // * + // * @param t The <code>Throwable</code>. + // * @return a String representing the stack trace of the + // <code>Throwable</code>. + // */ + // public static String getStackTraceAsString(final Throwable t) { + // final ByteArrayOutputStream stackTraceBis = new ByteArrayOutputStream(); + // t.printStackTrace(new PrintStream(stackTraceBis)); + // return new String(stackTraceBis.toByteArray()); + // } } diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIDUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIDUtils.java deleted file mode 100644 index 2e016848..00000000 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIDUtils.java +++ /dev/null @@ -1,101 +0,0 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. - * - * Licensed under the EUPL, Version 1.2 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: - * https://joinup.ec.europa.eu/news/understanding-eupl-v12 - * - * 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.egiz.eaaf.core.impl.utils; - - -import at.gv.egiz.eaaf.core.api.IRequest; - -/** - * @author tlenz - * - */ -public class TransactionIDUtils { - - //MDC variables for logging - public static final String MDC_TRANSACTION_ID = "transactionId"; - public static final String MDC_SESSION_ID = "sessionId"; - public static final String MDC_SERVICEPROVIDER_ID = "oaId"; - - /** - * Set all MDC variables from pending request to this threat context<br> - * These includes SessionID, TransactionID, and unique service-provider identifier - * - * @param pendingRequest - */ - public static void setAllLoggingVariables(IRequest pendingRequest) { - setTransactionId(pendingRequest.getUniqueTransactionIdentifier()); - setSessionId(pendingRequest.getUniqueSessionIdentifier()); - setServiceProviderId(pendingRequest.getServiceProviderConfiguration().getUniqueIdentifier()); - - } - - /** - * Remove all MDC variables from this threat context - * - */ - public static void removeAllLoggingVariables() { - removeSessionId(); - removeTransactionId(); - removeServiceProviderId(); - - } - - - public static void setServiceProviderId(String oaUniqueId) { - org.slf4j.MDC.put(MDC_SERVICEPROVIDER_ID, oaUniqueId); - - } - - public static void removeServiceProviderId() { - org.slf4j.MDC.remove(MDC_SERVICEPROVIDER_ID); - - } - - public static void setTransactionId(String pendingRequestID) { - org.slf4j.MDC.put(MDC_TRANSACTION_ID, - "TID-" + pendingRequestID); - - } - - public static void removeTransactionId() { - org.slf4j.MDC.remove(MDC_TRANSACTION_ID); - - } - - public static void setSessionId(String uniqueSessionId) { - org.slf4j.MDC.put(MDC_SESSION_ID, - "SID-" + uniqueSessionId); - - } - - public static void removeSessionId() { - org.slf4j.MDC.remove(MDC_SESSION_ID); - - } - - -} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIdUtils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIdUtils.java new file mode 100644 index 00000000..4cbcfa70 --- /dev/null +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIdUtils.java @@ -0,0 +1,139 @@ +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.2 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: + * https://joinup.ec.europa.eu/news/understanding-eupl-v12 + * + * 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.egiz.eaaf.core.impl.utils; + +import at.gv.egiz.eaaf.core.api.IRequest; + +/** + * Transaction Identifier Utils. + * + * @author tlenz + * + */ +public class TransactionIdUtils { + + // MDC variables for logging + /** + * To correlate technical logs over one single transactions. + */ + public static final String MDC_TRANSACTION_ID = "transactionId"; + + /** + * To correlate technical logs over a set of transactions, like SSO. + */ + public static final String MDC_SESSION_ID = "sessionId"; + + /** + * Unique application identifier that is processed in this transaction. + */ + public static final String MDC_SERVICEPROVIDER_ID = "oaId"; + + /** + * Set all MDC variables from pending request to this threat context.<br> + * These includes SessionID, TransactionID, and unique service-provider + * identifier + * + * @param pendingRequest Http request object + */ + public static void setAllLoggingVariables(final IRequest pendingRequest) { + setTransactionId(pendingRequest.getUniqueTransactionIdentifier()); + setSessionId(pendingRequest.getUniqueSessionIdentifier()); + setServiceProviderId(pendingRequest.getServiceProviderConfiguration().getUniqueIdentifier()); + + } + + /** + * Remove all MDC variables from this threat context. + * + */ + public static void removeAllLoggingVariables() { + removeSessionId(); + removeTransactionId(); + removeServiceProviderId(); + + } + + /** + * Set unique service-provider identifier for Logging purposes. + * + * @param oaUniqueId Unique application Id + */ + public static void setServiceProviderId(final String oaUniqueId) { + org.slf4j.MDC.put(MDC_SERVICEPROVIDER_ID, oaUniqueId); + + } + + /** + * Remove service-provider identifier for Logging. + */ + public static void removeServiceProviderId() { + org.slf4j.MDC.remove(MDC_SERVICEPROVIDER_ID); + + } + + /** + * Get Id to correlate technical logs over one single transactions. + * + * @return Unique transaction Id + */ + public static String getTransactionId() { + return org.slf4j.MDC.get(MDC_TRANSACTION_ID); + + } + + /** + * Set Id to correlate technical logs over one single transactions. + * + * @param transactionId Unique transaction Id + */ + public static void setTransactionId(final String transactionId) { + org.slf4j.MDC.put(MDC_TRANSACTION_ID, transactionId); + + } + + /** + * Remove transactionId for Logging. + */ + public static void removeTransactionId() { + org.slf4j.MDC.remove(MDC_TRANSACTION_ID); + + } + + /** + * Set Id to correlate technical logs over a set of transactions, like SSO. + * + * @param uniqueSessionId Unique Id + */ + public static void setSessionId(final String uniqueSessionId) { + org.slf4j.MDC.put(MDC_SESSION_ID, uniqueSessionId); + + } + + /** + * Remove sessionId for Logging. + * + */ + public static void removeSessionId() { + org.slf4j.MDC.remove(MDC_SESSION_ID); + + } + + +} diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/X509Utils.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/X509Utils.java index b3fb42c4..72c183bf 100644 --- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/X509Utils.java +++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/utils/X509Utils.java @@ -7,56 +7,45 @@ import javax.security.auth.x500.X500Principal; public class X509Utils { - /** - * Sorts the Certificate Chain by IssuerDN and SubjectDN. The [0]-Element should be the Hostname, - * the last Element should be the Root Certificate. - * - * @param certs - * The first element must be the correct one. - * @return sorted Certificate Chain - */ - public static List<X509Certificate> sortCertificates( - List<X509Certificate> certs) - { - int length = certs.size(); - if (certs.size() <= 1) - { - return certs; - } + /** + * Sorts the Certificate Chain by IssuerDN and SubjectDN. The [0]-Element should + * be the Hostname, the last Element should be the Root Certificate. + * + * @param certs The first element must be the correct one. + * @return sorted Certificate Chain + */ + public static List<X509Certificate> sortCertificates(final List<X509Certificate> certs) { + final int length = certs.size(); + if (certs.size() <= 1) { + return certs; + } - for (X509Certificate cert : certs) - { - if (cert == null) - { - throw new NullPointerException(); - } - } + for (final X509Certificate cert : certs) { + if (cert == null) { + throw new NullPointerException(); + } + } - for (int i = 0; i < length; i++) - { - boolean found = false; - X500Principal issuer = certs.get(i).getIssuerX500Principal(); - for (int j = i + 1; j < length; j++) - { - X500Principal subject = certs.get(j).getSubjectX500Principal(); - if (issuer.equals(subject)) - { - // sorting necessary? - if (i + 1 != j) - { - X509Certificate tmp = certs.get(i + 1); - certs.set(i + 1, certs.get(j)); - certs.set(j, tmp); - } - found = true; - } - } - if (!found) - { - break; - } - } + for (int i = 0; i < length; i++) { + boolean found = false; + final X500Principal issuer = certs.get(i).getIssuerX500Principal(); + for (int j = i + 1; j < length; j++) { + final X500Principal subject = certs.get(j).getSubjectX500Principal(); + if (issuer.equals(subject)) { + // sorting necessary? + if (i + 1 != j) { + final X509Certificate tmp = certs.get(i + 1); + certs.set(i + 1, certs.get(j)); + certs.set(j, tmp); + } + found = true; + } + } + if (!found) { + break; + } + } - return certs; - } + return certs; + } } |