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

org.entur.jwt.spring.JwtAutoConfiguration Maven / Gradle / Ivy

package org.entur.jwt.spring;

import org.entur.jwt.spring.actuate.JwksHealthIndicator;
import org.entur.jwt.spring.auth0.properties.JwtProperties;
import org.entur.jwt.spring.auth0.properties.MdcPair;
import org.entur.jwt.spring.auth0.properties.MdcProperties;
import org.entur.jwt.spring.auth0.properties.SecurityProperties;
import org.entur.jwt.spring.auth0.properties.TenantFilter;
import org.entur.jwt.spring.filter.DefaultJwtDetailsMapper;
import org.entur.jwt.spring.filter.DefaultJwtPrincipalMapper;
import org.entur.jwt.spring.filter.JwtDetailsMapper;
import org.entur.jwt.spring.filter.JwtPrincipalMapper;
import org.entur.jwt.spring.filter.log.DefaultJwtMappedDiagnosticContextMapper;
import org.entur.jwt.spring.filter.log.JwtMappedDiagnosticContextMapper;
import org.entur.jwt.verifier.JwtClaimExtractor;
import org.entur.jwt.verifier.JwtVerifier;
import org.entur.jwt.verifier.JwtVerifierFactory;
import org.entur.jwt.verifier.config.JwtTenantProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

@Configuration
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@EnableConfigurationProperties({SecurityProperties.class})
@ConditionalOnProperty(name = {"entur.jwt.enabled"}, havingValue = "true")
public abstract class JwtAutoConfiguration {

    private static final Logger LOG = LoggerFactory.getLogger(JwtAutoConfiguration.class);

    @Bean
    @ConditionalOnMissingBean(JwtMappedDiagnosticContextMapper.class)
    @ConditionalOnProperty(name = {"entur.jwt.mdc.enabled"}, havingValue = "true")
    public  JwtMappedDiagnosticContextMapper mapper(SecurityProperties properties, JwtClaimExtractor extractor) {
        MdcProperties mdc = properties.getJwt().getMdc();
        List items = mdc.getMappings();
        List to = new ArrayList<>();
        List from = new ArrayList<>();

        // note: possible to map the same claim to different values
        for (MdcPair item : items) {
            to.add(item.getTo());
            from.add(item.getFrom());

            LOG.info("Map JWT claim '{}' to MDC key '{}'", item.getFrom(), item.getTo());
        }
        return new DefaultJwtMappedDiagnosticContextMapper<>(from, to, extractor);
    }

    @Bean(destroyMethod = "close")
    @ConditionalOnMissingBean(JwtVerifier.class)
    public  JwtVerifier jwtVerifier(SecurityProperties properties, JwtVerifierFactory factory) {
        JwtProperties jwtProperties = properties.getJwt();

        Map enabledTenants = new HashMap<>();
        for (Entry entry : jwtProperties.getTenants().entrySet()) {
            if (entry.getValue().isEnabled()) {
                enabledTenants.put(entry.getKey(), entry.getValue());
            }
        }

        Map tenants;
        TenantFilter filter = jwtProperties.getFilter();
        if (!filter.isEmpty()) {
            tenants = new HashMap<>();

            // filter on key
            for (String id : filter.getIds()) {
                id = id.trim();

                boolean endsWith = id.endsWith("*");

                if (endsWith) {
                    String prefix = id.substring(0, id.length() - 1);
                    for (Entry entry : enabledTenants.entrySet()) {
                        if (entry.getKey().startsWith(prefix)) {
                            tenants.put(id, entry.getValue());
                        }
                    }
                } else {
                    JwtTenantProperties candidate = enabledTenants.get(id);
                    if (candidate != null) {
                        tenants.put(id, candidate);
                    }
                }
            }
        } else {
            tenants = enabledTenants;
        }
        if (tenants.isEmpty()) {
            Set disabled = new HashSet<>(jwtProperties.getTenants().keySet());
            disabled.removeAll(enabledTenants.keySet());
            if (!filter.isEmpty()) {
                throw new IllegalStateException("No configured tenants for filter '" + filter + "', candidates were " + enabledTenants.keySet() + " (" + disabled + " were disabled)");
            } else {
                throw new IllegalStateException("No configured tenants (" + disabled + " were disabled)");
            }
        }

        // add a wrapper so that the verifier is closed on shutdown
        return factory.getVerifier(tenants, jwtProperties.getJwk(), jwtProperties.getClaims());
    }


    @Bean
    @ConditionalOnMissingBean(TenantsProperties.class)
    public TenantsProperties tenantsProperties(SecurityProperties properties) {
        TenantsProperties tenantsProperties = new TenantsProperties();

        for (Entry entry : properties.getJwt().getTenants().entrySet()) {
            JwtTenantProperties value = entry.getValue();
            if (value.isEnabled()) {
                tenantsProperties.add(new TenantProperties(entry.getKey(), value.getIssuer(), value.getProperties()));
            }
        }

        return tenantsProperties;
    }

    @Bean
    @ConditionalOnMissingBean(JwtDetailsMapper.class)
    public JwtDetailsMapper jwtDetailsMapper() {
        return new DefaultJwtDetailsMapper();
    }

    @Bean
    @ConditionalOnMissingBean(JwtPrincipalMapper.class)
    public JwtPrincipalMapper jwtPrincipalMapper() {
        return new DefaultJwtPrincipalMapper();
    }

    @Bean
    @ConditionalOnProperty(name = {"entur.jwt.jwk.health-indicator.enabled"}, havingValue = "true", matchIfMissing = true)
    @ConditionalOnBean(JwtVerifier.class)
    public  JwksHealthIndicator jwksHealthIndicator(JwtVerifier verifier) {
        return new JwksHealthIndicator(verifier);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy