All Downloads are FREE. Search and download functionalities are using the official Maven repository.

be.atbash.ee.security.octopus.config.JwtSupportConfiguration Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017-2022 Rudy De Busscher (https://www.atbash.be)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package be.atbash.ee.security.octopus.config;

import be.atbash.config.AbstractConfiguration;
import be.atbash.config.exception.ConfigurationException;
import be.atbash.config.logging.ConfigEntry;
import be.atbash.config.logging.ModuleConfig;
import be.atbash.config.logging.ModuleConfigName;
import be.atbash.config.logging.StartupLogging;
import be.atbash.ee.security.octopus.keys.KeyManager;
import be.atbash.ee.security.octopus.keys.LocalKeyManager;
import be.atbash.ee.security.octopus.keys.reader.DefaultKeyResourceTypeProvider;
import be.atbash.ee.security.octopus.keys.reader.KeyResourceType;
import be.atbash.ee.security.octopus.keys.reader.KeyResourceTypeProvider;
import be.atbash.ee.security.octopus.keys.reader.password.ConfigKeyResourcePasswordLookup;
import be.atbash.ee.security.octopus.keys.reader.password.KeyResourcePasswordLookup;
import be.atbash.ee.security.octopus.nimbus.jwt.jwe.JWEAlgorithm;
import be.atbash.ee.security.octopus.nimbus.jwt.jws.JWSAlgorithm;
import be.atbash.ee.security.octopus.util.PeriodUtil;
import be.atbash.util.StringUtils;
import be.atbash.util.reflection.CDICheck;
import be.atbash.util.reflection.ClassUtils;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.enterprise.context.ApplicationScoped;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 *
 */
@ApplicationScoped
@ModuleConfigName("Octopus JWT Support Configuration")
public class JwtSupportConfiguration extends AbstractConfiguration implements ModuleConfig {

    private static final List RSA_SUPPORTED_ALGOS = Arrays.asList(JWSAlgorithm.RS256, JWSAlgorithm.RS384
            , JWSAlgorithm.RS512, JWSAlgorithm.PS256, JWSAlgorithm.PS384, JWSAlgorithm.PS512);

    private static final Logger LOGGER = LoggerFactory.getLogger(JwtSupportConfiguration.class);

    /**
     * The return value can also be a directory where multiple files are located (and retrieved).
     *
     * @return Location, file or directory where the keys are located.
     */
    @ConfigEntry
    public String getKeysLocation() {
        // TODO Optional, but afterwards we throw exception when empty
        // Can't we force the checks here? (so question : when octopus-jwt-support module added, isn't the parameter always required or not?)
        // be.atbash.ee.security.octopus.keys.LocalKeyManager.checkKeyLoading
        return getOptionalValue("keys.location", String.class);
    }

    @ConfigEntry
    public KeyResourcePasswordLookup getPasswordLookup() {

        String passwordClass = getOptionalValue("lookup.password.class", ConfigKeyResourcePasswordLookup.class.getName(), String.class);
        if (StringUtils.isEmpty(passwordClass)) {
            throw new ConfigurationException("Configuration parameter lookup.password.class is required to have a value.");
        }

        if (!ClassUtils.isAvailable(passwordClass)) {
            throw new ConfigurationException("Configuration parameter lookup.password.class class not found.");
        }

        Class passwordClz = ClassUtils.forName(passwordClass);
        if (!KeyResourcePasswordLookup.class.isAssignableFrom(passwordClz)) {
            throw new ConfigurationException("Configuration parameter lookup.password.class must be an implementation of be.atbash.ee.security.octopus.keys.reader.password.KeyResourcePasswordLookup");
        }
        return ClassUtils.newInstance(passwordClz);
    }

    @ConfigEntry
    public KeyManager getKeyManager() {

        String keyManagerClass = getOptionalValue("key.manager.class", LocalKeyManager.class.getName(), String.class);

        if (StringUtils.isEmpty(keyManagerClass)) {
            throw new ConfigurationException("Configuration parameter key.manager.class is required to have a value.");
        }

        if (!ClassUtils.isAvailable(keyManagerClass)) {
            throw new ConfigurationException("Configuration parameter key.manager.class class not found.");
        }

        Class keyManagerClz = ClassUtils.forName(keyManagerClass);
        if (!KeyManager.class.isAssignableFrom(keyManagerClz)) {
            throw new ConfigurationException("Configuration parameter key.manager.class must be an implementation of be.atbash.ee.security.octopus.keys.KeyManager");
        }

        return ClassUtils.newInstance(keyManagerClz);
    }

    @ConfigEntry
    public KeyResourceTypeProvider getKeyResourceTypeProvider() {
        String keyResourceTypeProviderClass = getOptionalValue("key.resourcetype.provider.class", DefaultKeyResourceTypeProvider.class.getName(), String.class);

        if (StringUtils.isEmpty(keyResourceTypeProviderClass)) {
            throw new ConfigurationException("Configuration parameter key.resourcetype.provider.class is required to have a value.");
        }

        if (!ClassUtils.isAvailable(keyResourceTypeProviderClass)) {
            throw new ConfigurationException("Configuration parameter key.resourcetype.provider.class class not found.");
        }

        Class keyResourceTypeProviderClz = ClassUtils.forName(keyResourceTypeProviderClass);
        if (!KeyResourceTypeProvider.class.isAssignableFrom(keyResourceTypeProviderClz)) {
            throw new ConfigurationException("Configuration parameter key.resourcetype.provider.class must be an implementation of be.atbash.ee.security.octopus.keys.reader.KeyResourceTypeProvider");
        }

        return ClassUtils.newInstance(keyResourceTypeProviderClz);

    }

    @ConfigEntry
    public PemKeyEncryption getPemKeyEncryption() {
        try {
            return getOptionalValue("key.pem.encryption", PemKeyEncryption.PKCS8, PemKeyEncryption.class);
        } catch (IllegalArgumentException e) {
            // When empty value, we need to return NONE
            String stringValue = getOptionalValue("key.pem.encryption", "", String.class);
            if (StringUtils.isEmpty(stringValue)) {
                return PemKeyEncryption.NONE;
            }

            // We assume that a wrong value, or wrong case is specified
            throw new ConfigurationException("Configuration parameter key.pem.encryption must be PKCS8 or PKCS1");
        }
    }

    @ConfigProperty
    public String getPKCS1EncryptionAlgorithm() {
        return getOptionalValue("key.pem.pkcs1.encryption", "DES-EDE3-CBC", String.class);
    }

    @ConfigProperty
    public String getNameCertificateKeyStore() {
        return getOptionalValue("key.store.certificate.x500name", "CN=localhost", String.class);
    }

    @ConfigProperty
    public String getCertificateSignatureAlgorithmRSA() {
        return getOptionalValue("key.store.signature.algo.RSA", "SHA1WithRSA", String.class);
    }

    @ConfigProperty
    public String getCertificateSignatureAlgorithmEC() {
        return getOptionalValue("key.store.signature.algo.EC", "SHA384withECDSA", String.class);
    }

    @ConfigProperty
    public String getKeyStoreType() {
        return getOptionalValue("key.store.type", "PKCS12", String.class);
    }

    @ConfigProperty
    public JWSAlgorithm getJWSAlgorithmForRSA() {
        String value = getOptionalValue("jwt.sign.rsa.algo", "RS256", String.class);
        JWSAlgorithm result = null;
        for (JWSAlgorithm algo : RSA_SUPPORTED_ALGOS) {
            if (algo.getName().equals(value)) {
                result = algo;
            }
        }
        if (result == null) {
            throw new ConfigurationException(String.format("Unsupported algorithm name %s for RSA signing", value));
        }
        return result;
    }

    @ConfigProperty
    public int getClockSkewSeconds() {
        Integer result;
        try {
            result = getOptionalValue("jwt.clock.skew.secs", 60, Integer.class);
        } catch (NumberFormatException e) {

            throw new ConfigurationException(String.format("Error in reading parameter value 'jwt.clock.skew.secs' : %s", e.getMessage()));
        }

        if (result < 0) {
            throw new ConfigurationException(String.format("Clock skew value must be positive, parameter 'jwt.clock.skew.secs' is %s", result));
        }
        return result;
    }

    @ConfigProperty
    public JWEAlgorithm getDefaultJWEAlgorithmRSA() {
        String configValue = getOptionalValue("jwt.jwe.algorithm.default.RSA", "RSA-OAEP-256", String.class);
        JWEAlgorithm jweAlgorithm = JWEAlgorithm.parse(configValue);
        if (!JWEAlgorithm.Family.RSA.contains(jweAlgorithm)) {

            throw new ConfigurationException("The default JWE Algorithm defined in parameter 'jwt.jwe.algorithm.default.RSA' is not valid ");
        }
        return jweAlgorithm;
    }

    @ConfigProperty
    public JWEAlgorithm getDefaultJWEAlgorithmEC() {
        String configValue = getOptionalValue("jwt.jwe.algorithm.default.EC", "ECDH-ES+A256KW", String.class);
        JWEAlgorithm jweAlgorithm = JWEAlgorithm.parse(configValue);
        if (!JWEAlgorithm.Family.ECDH_ES.contains(jweAlgorithm)) {

            throw new ConfigurationException("The default JWE Algorithm defined in parameter 'jwt.jwe.algorithm.default.EC' is not valid ");
        }
        return jweAlgorithm;
    }

    @ConfigProperty
    public JWEAlgorithm getDefaultJWEAlgorithmOCT() {
        String configValue = getOptionalValue("jwt.jwe.algorithm.default.OCT", "A256KW", String.class);
        JWEAlgorithm jweAlgorithm = JWEAlgorithm.parse(configValue);
        if (!JWEAlgorithm.Family.AES_KW.contains(jweAlgorithm)) {

            throw new ConfigurationException("The default JWE Algorithm defined in parameter 'jwt.jwe.algorithm.default.OCT' is not valid ");
        }
        return jweAlgorithm;
    }

    @ConfigProperty
    public String getJWKSetCachePeriod() {
        String configValue = getOptionalValue("jwt.remote.jwk.cache.period", "24h", String.class);
        // Validate the expression
        PeriodUtil.defineSecondsInPeriod(configValue);
        return configValue;
    }

    @ConfigEntry
    public boolean isJWKEncrypted() {
        return getOptionalValue("jwt.jwk.encrypted", Boolean.TRUE, Boolean.class);
    }

    @ConfigProperty
    public List getReaderOrder() {
        List result = new ArrayList<>();
        String order = getOptionalValue("jwt.reader.order", "JWKSET, JWK, PEM, KEYSTORE", String.class);
        String[] parts = order.split(",");
        for (String part : parts) {
            KeyResourceType type = KeyResourceType.valueFor(part.trim());
            if (type == null) {
                LOGGER.error(String.format("Parameter 'jwt.reader.order' must contain only values of 'KeyResourceType' but found '%s'.", part));
            } else {
                result.add(type);
            }
        }
        if (result.isEmpty()) {
            LOGGER.error("Parameter 'jwt.reader.order' resulted in an empty list. Taken the default order.");
            result.add(KeyResourceType.JWKSET);
            result.add(KeyResourceType.JWK);
            result.add(KeyResourceType.PEM);
            result.add(KeyResourceType.KEYSTORE);
        }
        return result;
    }

    @ConfigProperty
    public int getSaltLengthPasswordBasedEJWEEncryption() {
        Integer saltLength = getOptionalValue("jwt.jwe.pwbased.salt.length", 8, Integer.class);
        if (saltLength < 8) {
            throw new ConfigurationException(String.format("The value for the parameter 'jwt.jwe.pwbased.salt.length' must be at minimum 8 but was '%s'.", saltLength));
        }
        return saltLength;
    }

    // Java SE Support
    private static JwtSupportConfiguration INSTANCE;

    public static synchronized JwtSupportConfiguration getInstance() {
        // Synchronize methods are not so bad for performance anymore and since only 1 synchronized static there are no side effects
        if (INSTANCE == null) {
            INSTANCE = new JwtSupportConfiguration();
            if (!CDICheck.withinContainer()) {
                StartupLogging.logConfiguration(INSTANCE);
            }
        }
        return INSTANCE;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy