principalType;
22 | protected UnauthorizedHandler unauthorizedHandler = new DefaultUnauthorizedHandler();
23 |
24 |
25 | public boolean supports(Type clazz) {
26 | return clazz.equals(principalType);
27 | }
28 |
29 | /**
30 | * Abstract builder for auth filters.
31 | *
32 | * @param the type of the principal that the filter accepts
34 | */
35 | public abstract static class AuthFilterBuilder principalType;
41 |
42 | /**
43 | * Sets the given realm
44 | *
45 | * @param realm a realm
46 | * @return the current builder
47 | */
48 | public AuthFilterBuilder principalType) {
76 | this.principalType = principalType;
77 | return this;
78 | }
79 |
80 | /**
81 | * Builds an instance of the filter with a provided authenticator,
82 | * an authorizer, a prefix, and a realm.
83 | *
84 | * @return a new instance of the filter
85 | */
86 | public T buildAuthFilter() {
87 | Preconditions.checkArgument(realm != null, "Realm is not set");
88 | Preconditions.checkArgument(prefix != null, "Prefix is not set");
89 | Preconditions.checkArgument(authenticator != null, "Authenticator is not set");
90 |
91 | T authFilter = newInstance();
92 | authFilter.authenticator = authenticator;
93 | authFilter.prefix = prefix;
94 | authFilter.realm = realm;
95 | authFilter.principalType = principalType;
96 | return authFilter;
97 | }
98 |
99 | protected abstract T newInstance();
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/org/whispersystems/dropwizard/simpleauth/AuthPrincipal.java:
--------------------------------------------------------------------------------
1 | package org.whispersystems.dropwizard.simpleauth;
2 |
3 | import javax.security.auth.Subject;
4 | import java.security.Principal;
5 |
6 | public class AuthPrincipal implements Principal {
7 |
8 | private final Object authenticated;
9 |
10 | public AuthPrincipal(Object authenticated) {
11 | this.authenticated = authenticated;
12 | }
13 |
14 | @Override
15 | public String getName() {
16 | return null;
17 | }
18 |
19 | @Override
20 | public boolean implies(Subject subject) {
21 | return false;
22 | }
23 |
24 | public Object getAuthenticated() {
25 | return authenticated;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/org/whispersystems/dropwizard/simpleauth/AuthSecurityContext.java:
--------------------------------------------------------------------------------
1 | package org.whispersystems.dropwizard.simpleauth;
2 |
3 | import javax.ws.rs.core.SecurityContext;
4 | import java.security.Principal;
5 |
6 | public class AuthSecurityContext implements SecurityContext {
7 |
8 | private final AuthPrincipal principal;
9 | private final boolean secure;
10 |
11 | public AuthSecurityContext(P principal, boolean secure) {
12 | this.principal = new AuthPrincipal(principal);
13 | this.secure = secure;
14 | }
15 |
16 | @Override
17 | public Principal getUserPrincipal() {
18 | return principal;
19 | }
20 |
21 | @Override
22 | public boolean isUserInRole(String role) {
23 | return false;
24 | }
25 |
26 | @Override
27 | public boolean isSecure() {
28 | return secure;
29 | }
30 |
31 | @Override
32 | public String getAuthenticationScheme() {
33 | return SecurityContext.BASIC_AUTH;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/org/whispersystems/dropwizard/simpleauth/AuthValueFactoryProvider.java:
--------------------------------------------------------------------------------
1 | package org.whispersystems.dropwizard.simpleauth;
2 |
3 | import org.glassfish.hk2.api.InjectionResolver;
4 | import org.glassfish.hk2.api.ServiceLocator;
5 | import org.glassfish.hk2.api.TypeLiteral;
6 | import org.glassfish.hk2.utilities.binding.AbstractBinder;
7 | import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
8 | import org.glassfish.jersey.server.internal.inject.AbstractValueFactoryProvider;
9 | import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider;
10 | import org.glassfish.jersey.server.internal.inject.ParamInjectionResolver;
11 | import org.glassfish.jersey.server.model.Parameter;
12 | import org.glassfish.jersey.server.spi.internal.ValueFactoryProvider;
13 |
14 | import javax.inject.Inject;
15 | import javax.inject.Singleton;
16 | import java.security.Principal;
17 | import java.util.Optional;
18 |
19 | import io.dropwizard.auth.Auth;
20 |
21 | @Singleton
22 | public class AuthValueFactoryProvider extends AbstractValueFactoryProvider {
23 |
24 | @Inject
25 | public AuthValueFactoryProvider(MultivaluedParameterExtractorProvider mpep,
26 | ServiceLocator injector)
27 | {
28 | super(mpep, injector, Parameter.Source.UNKNOWN);
29 | }
30 |
31 | @Override
32 | public AbstractContainerRequestValueFactory> createValueFactory(final Parameter parameter) {
33 | if (parameter.getAnnotation(Auth.class) == null) {
34 | return null;
35 | }
36 |
37 | if (parameter.getRawType() == Optional.class) {
38 | return new OptionalContainerRequestValueFactory(parameter);
39 | } else {
40 | return new StandardContainerReqeustValueFactory(parameter);
41 | }
42 | }
43 |
44 | private static class StandardContainerReqeustValueFactory extends AbstractContainerRequestValueFactory {
45 | private final Parameter parameter;
46 |
47 | StandardContainerReqeustValueFactory(Parameter parameter) {
48 | this.parameter = parameter;
49 | }
50 |
51 | /**
52 | * @return {@link Principal} stored on the request, or {@code null} if no object was found.
53 | */
54 | public Object provide() {
55 | Principal principal = getContainerRequest().getSecurityContext().getUserPrincipal();
56 |
57 | if (principal == null) {
58 | throw new IllegalStateException("Cannot inject a custom principal into unauthenticated request");
59 | }
60 |
61 | if (!(principal instanceof AuthPrincipal)) {
62 | throw new IllegalArgumentException("Cannot inject a non-AuthPrincipal into request");
63 | }
64 |
65 | if (!parameter.getRawType().isAssignableFrom(((AuthPrincipal)principal).getAuthenticated().getClass())) {
66 | throw new IllegalArgumentException("Authenticated principal is of the wrong type!");
67 | }
68 |
69 | return parameter.getRawType().cast(((AuthPrincipal) principal).getAuthenticated());
70 | }
71 | }
72 |
73 | private static class OptionalContainerRequestValueFactory extends AbstractContainerRequestValueFactory {
74 | private final Parameter parameter;
75 |
76 | OptionalContainerRequestValueFactory(Parameter parameter) {
77 | this.parameter = parameter;
78 | }
79 |
80 | /**
81 | * @return {@link Principal} stored on the request, or {@code null} if no object was found.
82 | */
83 | public Object provide() {
84 | Principal principal = getContainerRequest().getSecurityContext().getUserPrincipal();
85 |
86 | if (principal != null && !(principal instanceof AuthPrincipal)) {
87 | throw new IllegalArgumentException("Cannot inject a non-AuthPrincipal into request");
88 | }
89 |
90 | if (principal == null) return Optional.empty();
91 | else return Optional.of(((AuthPrincipal)principal).getAuthenticated());
92 | }
93 | }
94 |
95 | @Singleton
96 | private static class AuthInjectionResolver extends ParamInjectionResolver the type of principals the authenticator returns
14 | */
15 | public interface Authenticator authenticate(C credentials) throws AuthenticationException;
29 | }
--------------------------------------------------------------------------------
/src/main/java/org/whispersystems/dropwizard/simpleauth/BasicCredentialAuthFilter.java:
--------------------------------------------------------------------------------
1 | package org.whispersystems.dropwizard.simpleauth;
2 |
3 | import com.google.common.io.BaseEncoding;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import javax.annotation.Priority;
8 | import javax.ws.rs.InternalServerErrorException;
9 | import javax.ws.rs.Priorities;
10 | import javax.ws.rs.WebApplicationException;
11 | import javax.ws.rs.container.ContainerRequestContext;
12 | import javax.ws.rs.core.HttpHeaders;
13 | import java.io.IOException;
14 | import java.nio.charset.StandardCharsets;
15 | import java.util.Optional;
16 |
17 | import io.dropwizard.auth.AuthenticationException;
18 | import io.dropwizard.auth.basic.BasicCredentials;
19 |
20 | @Priority(Priorities.AUTHENTICATION)
21 | public class BasicCredentialAuthFilter extends AuthFilter principal = authenticator.authenticate(credentials);
47 |
48 | if (principal.isPresent()) {
49 | requestContext.setSecurityContext(new AuthSecurityContext (principal.get(), false));
50 | return;
51 | }
52 | } catch (AuthenticationException e) {
53 | LOGGER.warn("Error authenticating credentials", e);
54 | throw new InternalServerErrorException();
55 | }
56 | }
57 | }
58 | }
59 | }
60 | } catch (IllegalArgumentException e) {
61 | LOGGER.warn("Error decoding credentials", e);
62 | }
63 |
64 | throw new WebApplicationException(unauthorizedHandler.buildResponse(prefix, realm));
65 | }
66 |
67 | public static class Builder extends AuthFilter.AuthFilterBuilder newInstance() {
71 | return new BasicCredentialAuthFilter<>();
72 | }
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/org/whispersystems/dropwizard/simpleauth/WebApplicationExceptionCatchingFilter.java:
--------------------------------------------------------------------------------
1 | package org.whispersystems.dropwizard.simpleauth;
2 |
3 | import com.google.common.annotations.VisibleForTesting;
4 | import com.google.common.base.Preconditions;
5 |
6 | import java.io.IOException;
7 |
8 | import javax.annotation.Priority;
9 | import javax.ws.rs.Priorities;
10 | import javax.ws.rs.WebApplicationException;
11 | import javax.ws.rs.container.ContainerRequestContext;
12 | import javax.ws.rs.container.ContainerRequestFilter;
13 |
14 | /**
15 | * A {@link ContainerRequestFilter} decorator which catches any {@link
16 | * WebApplicationException WebApplicationExceptions} thrown by an
17 | * underlying {@code ContextRequestFilter}.
18 | */
19 | @Priority(Priorities.AUTHENTICATION)
20 | class WebApplicationExceptionCatchingFilter implements ContainerRequestFilter {
21 | private final ContainerRequestFilter underlying;
22 |
23 | public WebApplicationExceptionCatchingFilter(ContainerRequestFilter underlying) {
24 | Preconditions.checkNotNull(underlying, "Underlying ContainerRequestFilter is not set");
25 | this.underlying = underlying;
26 | }
27 |
28 | @Override
29 | public void filter(ContainerRequestContext requestContext) throws IOException {
30 | try {
31 | underlying.filter(requestContext);
32 | } catch (WebApplicationException err) {
33 | // Pass through.
34 | }
35 | }
36 |
37 | @VisibleForTesting
38 | ContainerRequestFilter getUnderlying() {
39 | return underlying;
40 | }
41 | }
--------------------------------------------------------------------------------
/src/test/java/org/whispersystems/dropwizard/simpleauth/AuthDynamicFeatureTest.java:
--------------------------------------------------------------------------------
1 | package org.whispersystems.dropwizard.simpleauth;
2 |
3 |
4 | import org.junit.Test;
5 | import org.mockito.ArgumentCaptor;
6 |
7 | import javax.ws.rs.container.ContainerRequestFilter;
8 | import javax.ws.rs.container.ResourceInfo;
9 | import javax.ws.rs.core.FeatureContext;
10 |
11 | import java.util.Optional;
12 |
13 | import io.dropwizard.auth.Auth;
14 | import io.dropwizard.auth.AuthenticationException;
15 | import io.dropwizard.auth.basic.BasicCredentials;
16 | import static junit.framework.TestCase.assertEquals;
17 | import static junit.framework.TestCase.assertTrue;
18 | import static org.mockito.Mockito.*;
19 |
20 | public class AuthDynamicFeatureTest {
21 |
22 | @Test
23 | public void testPrincipalTypes() throws NoSuchMethodException {
24 | AuthFilter stringPrincipal = new BasicCredentialAuthFilter.Builder