package io.dropwizard.auth; import org.glassfish.hk2.api.InjectionResolver; import org.glassfish.hk2.api.ServiceLocator; import org.glassfish.hk2.api.TypeLiteral; import org.glassfish.hk2.utilities.binding.AbstractBinder; import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory; import org.glassfish.jersey.server.internal.inject.AbstractValueFactoryProvider; import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider; import org.glassfish.jersey.server.internal.inject.ParamInjectionResolver; import org.glassfish.jersey.server.model.Parameter; import org.glassfish.jersey.server.spi.internal.ValueFactoryProvider; import javax.inject.Inject; import javax.inject.Singleton; import java.lang.reflect.ParameterizedType; import java.security.Principal; import java.util.Optional; import java.util.Set; /** * Value factory provider supporting injection of a hierarchy of * {@link Principal} subclasses by the {@link Auth} annotation. * * @param <T> the type acting as the superclass from which injected * principals inherit */ @Singleton public class PolymorphicAuthValueFactoryProvider<T extends Principal> extends AbstractValueFactoryProvider { /** * Set of provided {@link Principal} subclasses. */ protected final Set<Class<? extends T>> principalClassSet; /** * {@link Principal} value factory provider injection constructor. * * @param mpep multivalued parameter extractor provider * @param injector injector instance * @param principalClassSetProvider provider(s) of the principal class */ @Inject public PolymorphicAuthValueFactoryProvider( MultivaluedParameterExtractorProvider mpep, ServiceLocator injector, PrincipalClassSetProvider<T> principalClassSetProvider ) { super(mpep, injector, Parameter.Source.UNKNOWN); this.principalClassSet = principalClassSetProvider.clazzSet; } /** * Return a factory for the provided parameter. We only expect objects of * the type {@link T} being annotated with {@link Auth} annotation. * * @param parameter parameter that was annotated for being injected * @return the factory if annotated parameter matched type */ @Override public AbstractContainerRequestValueFactory<?> createValueFactory(Parameter parameter) { if (!parameter.isAnnotationPresent(Auth.class)) { return null; } else if (principalClassSet.contains(parameter.getRawType())) { return new PrincipalContainerRequestValueFactory(); } else { final boolean isOptionalPrincipal = parameter.getRawType() == Optional.class && ParameterizedType.class.isAssignableFrom(parameter.getType().getClass()) && principalClassSet.contains(((ParameterizedType) parameter.getType()).getActualTypeArguments()[0]); return isOptionalPrincipal ? new OptionalPrincipalContainerRequestValueFactory() : null; } } @Singleton static class AuthInjectionResolver extends ParamInjectionResolver<Auth> { /** * Create new {@link Auth} annotation injection resolver. */ public AuthInjectionResolver() { super(PolymorphicAuthValueFactoryProvider.class); } } @Singleton protected static class PrincipalClassSetProvider<T extends Principal> { private final Set<Class<? extends T>> clazzSet; public PrincipalClassSetProvider(Set<Class<? extends T>> clazzSet) { this.clazzSet = clazzSet; } } /** * Injection binder for {@link PolymorphicAuthValueFactoryProvider} and * {@link AuthInjectionResolver}. * * @param <T> the type of the principal */ public static class Binder<T extends Principal> extends AbstractBinder { private final Set<Class<? extends T>> principalClassSet; public Binder(Set<Class<? extends T>> principalClassSet) { this.principalClassSet = principalClassSet; } @Override protected void configure() { bind(new PrincipalClassSetProvider<>(principalClassSet)).to(PrincipalClassSetProvider.class); bind(PolymorphicAuthValueFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class); bind(AuthInjectionResolver.class).to(new TypeLiteral<InjectionResolver<Auth>>() { }).in(Singleton.class); } } }