All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.sdase.commons.server.auth.AuthBundle Maven / Gradle / Ivy
Go to download
A libraries to bootstrap services easily that follow the patterns and specifications promoted by the SDA SE
package org.sdase.commons.server.auth;
import static org.sdase.commons.server.opentracing.client.ClientTracingUtil.registerTracing;
import io.dropwizard.Configuration;
import io.dropwizard.ConfiguredBundle;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.client.JerseyClientBuilder;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import io.opentracing.Tracer;
import io.opentracing.util.GlobalTracer;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.ScheduledExecutorService;
import javax.ws.rs.client.Client;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.impl.conn.SystemDefaultRoutePlanner;
import org.sdase.commons.server.auth.config.AuthConfig;
import org.sdase.commons.server.auth.config.AuthConfigProvider;
import org.sdase.commons.server.auth.config.KeyLocation;
import org.sdase.commons.server.auth.error.ForbiddenExceptionMapper;
import org.sdase.commons.server.auth.error.JwtAuthExceptionMapper;
import org.sdase.commons.server.auth.filter.JwtAuthFilter;
import org.sdase.commons.server.auth.key.JwksKeySource;
import org.sdase.commons.server.auth.key.KeyLoaderScheduler;
import org.sdase.commons.server.auth.key.KeySource;
import org.sdase.commons.server.auth.key.OpenIdProviderDiscoveryKeySource;
import org.sdase.commons.server.auth.key.PemKeySource;
import org.sdase.commons.server.auth.key.PublicKeyLoader;
import org.sdase.commons.server.auth.service.AuthService;
import org.sdase.commons.server.auth.service.JwtAuthenticator;
import org.sdase.commons.server.auth.service.TokenAuthorizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AuthBundle implements ConfiguredBundle {
private static final Logger LOG = LoggerFactory.getLogger(AuthBundle.class);
private AuthConfigProvider configProvider;
private boolean useAnnotatedAuthorization;
private final Tracer tracer;
public static ProviderBuilder builder() {
return new Builder<>();
}
private AuthBundle(
AuthConfigProvider configProvider, boolean useAnnotatedAuthorization, Tracer tracer) {
this.configProvider = configProvider;
this.useAnnotatedAuthorization = useAnnotatedAuthorization;
this.tracer = tracer;
}
@Override
public void initialize(Bootstrap bootstrap) {
// no initialization needed
}
@Override
public void run(T configuration, Environment environment) {
AuthConfig config = configProvider.apply(configuration);
if (config.isDisableAuth()) {
LOG.warn("Authentication is disabled. This setting should NEVER be used in production.");
}
Tracer currentTracer = tracer == null ? GlobalTracer.get() : tracer;
Client client = createKeyLoaderClient(environment, config, currentTracer);
PublicKeyLoader keyLoader = new PublicKeyLoader();
config.getKeys().stream()
.map(k -> this.createKeySources(k, client))
.forEach(keyLoader::addKeySource);
ScheduledExecutorService executorService =
environment.lifecycle().scheduledExecutorService("reloadKeysExecutorService").build();
KeyLoaderScheduler.create(keyLoader, executorService).start();
TokenAuthorizer authService = new AuthService(keyLoader, config.getLeeway());
JwtAuthenticator authenticator = new JwtAuthenticator(authService, config.isDisableAuth());
JwtAuthFilter authFilter =
new JwtAuthFilter.Builder()
.withTracer(currentTracer)
.setAcceptAnonymous(!useAnnotatedAuthorization)
.setAuthenticator(authenticator)
.buildAuthFilter();
if (useAnnotatedAuthorization) {
// Use the AuthDynamicFeature to only affect endpoints that are
// annotated
environment.jersey().register(new AuthDynamicFeature(authFilter));
} else {
// Apply the filter for all calls
environment.jersey().register(authFilter);
}
environment.jersey().register(JwtAuthExceptionMapper.class);
environment.jersey().register(ForbiddenExceptionMapper.class);
}
private Client createKeyLoaderClient(Environment environment, AuthConfig config, Tracer tracer) {
JerseyClientBuilder jerseyClientBuilder = new JerseyClientBuilder(environment);
// a specific proxy configuration always overrides the system proxy
if (config.getKeyLoaderClient() == null
|| config.getKeyLoaderClient().getProxyConfiguration() == null) {
// register a route planner that uses the default proxy variables (e.g. http.proxyHost)
jerseyClientBuilder.using(new SystemDefaultRoutePlanner(ProxySelector.getDefault()));
}
if (config.getKeyLoaderClient() != null) {
jerseyClientBuilder.using(config.getKeyLoaderClient());
}
Client client = jerseyClientBuilder.build("keyLoader");
registerTracing(client, tracer);
return client;
}
private KeySource createKeySources(KeyLocation keyLocation, Client client) {
switch (keyLocation.getType()) {
case PEM:
return new PemKeySource(
keyLocation.getPemKeyId(),
keyLocation.getPemSignAlg(),
keyLocation.getLocation(),
keyLocation.getRequiredIssuer());
case OPEN_ID_DISCOVERY:
validateKeyLocation(keyLocation.getLocation(), keyLocation.getRequiredIssuer());
return new OpenIdProviderDiscoveryKeySource(
keyLocation.getLocation().toASCIIString(), client, keyLocation.getRequiredIssuer());
case JWKS:
validateKeyLocation(keyLocation.getLocation(), keyLocation.getRequiredIssuer());
return new JwksKeySource(
keyLocation.getLocation().toASCIIString(), client, keyLocation.getRequiredIssuer());
default:
throw new IllegalArgumentException(
"KeyLocation has no valid type: " + keyLocation.getType());
}
}
/**
* Validate, that if a required issuer is set as URI the host name of the key location source and
* the requiredIssuer must be the same.
*
* @param location The source {@link URI} of the key(s) as discovery or jwks endpoint.
* @param requiredIssuer The required issuer as {@link String} for the correlated key source.
*/
private void validateKeyLocation(URI location, String requiredIssuer) {
if (StringUtils.isNotBlank(requiredIssuer) && StringUtils.contains(requiredIssuer, ':')) {
try {
URI issuerUri = new URI(requiredIssuer);
if (!StringUtils.equalsIgnoreCase(location.getHost(), issuerUri.getHost())) {
LOG.warn(
"The required issuer host name <{}> for the key <{}> does not match to the key"
+ " source uri host name <{}>.",
issuerUri.getHost(),
location,
location.getHost());
}
} catch (URISyntaxException e) {
throw new IllegalArgumentException(
"The requiredIssuer <" + requiredIssuer + "> is no valid stringOrURI", e);
}
}
}
//
// Builder
//
public interface ProviderBuilder {
AuthorizationBuilder withAuthConfigProvider(
AuthConfigProvider authConfigProvider);
}
public interface AuthorizationBuilder {
/**
* Configures the bundle to require valid tokens for all endpoints that are annotated with
* {@code @PermitAll}.
*
* @return the builder
*/
AuthBuilder withAnnotatedAuthorization();
/**
* Configures the bundle to validate tokens but also permit requests without Authorization
* header. Authorization decisions need be made separately e.g. by the {@link
* org.sdase.commons.server.opa.OpaBundle}.
*
* @return the builder
*/
AuthBuilder withExternalAuthorization();
}
public interface AuthBuilder {
AuthBuilder withTracer(Tracer tracer);
AuthBundle build();
}
public static class Builder
implements ProviderBuilder, AuthorizationBuilder, AuthBuilder {
private AuthConfigProvider authConfigProvider;
private boolean useAnnotatedAuthorization = true;
private Tracer tracer;
private Builder() {}
private Builder(AuthConfigProvider authConfigProvider) {
this.authConfigProvider = authConfigProvider;
}
@Override
public AuthorizationBuilder withAuthConfigProvider(
AuthConfigProvider authConfigProvider) {
return new Builder<>(authConfigProvider);
}
@Override
public AuthBuilder withAnnotatedAuthorization() {
this.useAnnotatedAuthorization = true;
return this;
}
@Override
public AuthBuilder withExternalAuthorization() {
this.useAnnotatedAuthorization = false;
return this;
}
@Override
public AuthBuilder withTracer(Tracer tracer) {
this.tracer = tracer;
return this;
}
@Override
public AuthBundle build() {
return new AuthBundle<>(authConfigProvider, useAnnotatedAuthorization, tracer);
}
}
}