package io.dropwizard.auth.chained; import io.dropwizard.auth.AuthFilter; import javax.annotation.Priority; import javax.ws.rs.Priorities; import javax.ws.rs.WebApplicationException; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.core.SecurityContext; import java.io.IOException; import java.security.Principal; import java.util.List; /** * Chains together authFilters, short circuits when the first filter * successfully authenticates * * N.B. AuthFilters can be chained together as long as they produce the same type * of Principal. This is not enforced by the type system at compile time, using * inconsistent principals will lead to runtime errors * * There is no requirement for the filters that are chained to use the same type for credentials. * The reason is that the ChainedFilter delegates to a filter which encapsulates * the authenticator and credential type * * * @param <C> the type of Credentials to be authenticated * @param <P> the type of the Principal */ @Priority(Priorities.AUTHENTICATION) public class ChainedAuthFilter<C, P extends Principal> extends AuthFilter<C, P> { private final List<AuthFilter> handlers; public ChainedAuthFilter(List<AuthFilter> handlers) { this.handlers = handlers; } @Override public void filter(ContainerRequestContext containerRequestContext) throws IOException { WebApplicationException firstException = null; for (AuthFilter authFilter : handlers) { final SecurityContext securityContext = containerRequestContext.getSecurityContext(); try { authFilter.filter(containerRequestContext); if (securityContext != containerRequestContext.getSecurityContext()) { return; } } catch (WebApplicationException e) { if (firstException == null) { firstException = e; } } } throw firstException; } }