package org.multibit.mbm.auth.cookie; import com.google.common.base.Optional; import com.sun.jersey.api.core.HttpContext; import com.sun.jersey.server.impl.inject.AbstractHttpContextInjectable; import com.yammer.dropwizard.auth.AuthenticationException; import com.yammer.dropwizard.auth.Authenticator; import org.multibit.mbm.auth.Authority; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Cookie; import javax.ws.rs.core.Response; import java.util.Map; import java.util.UUID; /** * <p>Injectable to provide the following to {@link CookieClientRestrictedToProvider}:</p> * <ul> * <li>Performs decode from HTTP request cookie</li> * <li>Carries cookie authentication data</li> * </ul> * * @since 0.0.1 */ class CookieClientRestrictedToInjectable<T> extends AbstractHttpContextInjectable<T> { private final Authenticator<CookieClientCredentials, T> authenticator; private final String sessionTokenName; private final String rememberMeName; private final Authority[] requiredAuthorities; private boolean isPublic=false; /** * @param authenticator The Authenticator that will compare credentials * @param sessionTokenName The session token cookie name (short lived with full access) * @param rememberMeName The "remember me" cookie name (long lived but reduced privilege) * @param requiredAuthorities The required authorities as provided by the RestrictedTo annotation */ CookieClientRestrictedToInjectable( Authenticator<CookieClientCredentials, T> authenticator, String sessionTokenName, String rememberMeName, Authority[] requiredAuthorities) { this.authenticator = authenticator; this.sessionTokenName = sessionTokenName; this.rememberMeName = rememberMeName; this.requiredAuthorities = requiredAuthorities; // Check for public access (session token creation) for (Authority authority: requiredAuthorities) { if (Authority.ROLE_PUBLIC.equals(authority)) { isPublic=true; break; } } } public Authenticator<CookieClientCredentials, T> getAuthenticator() { return authenticator; } public String getSessionTokenName() { return sessionTokenName; } public String getRememberMeName() { return rememberMeName; } public Authority[] getRequiredAuthorities() { return requiredAuthorities; } @Override public T getValue(HttpContext httpContext) { Map<String, Cookie> cookies = httpContext.getRequest().getCookies(); try { Optional<UUID> sessionToken = Optional.absent(); Optional<UUID> rememberMeToken = Optional.absent(); // Configure the UUIDs based on cookie values (must be valid UUIDs) Cookie sessionTokenCookie = cookies.get(sessionTokenName); Cookie rememberMeTokenCookie = cookies.get(rememberMeName); if (sessionTokenCookie != null) { sessionToken = Optional.of(UUID.fromString(sessionTokenCookie.getValue())); } if (rememberMeTokenCookie != null) { rememberMeToken = Optional.of(UUID.fromString(rememberMeTokenCookie.getValue())); } // Build the credentials (authenticator will handle absence) final CookieClientCredentials credentials = new CookieClientCredentials( sessionToken, rememberMeToken, requiredAuthorities, isPublic); // Authenticate final Optional<T> result = authenticator.authenticate(credentials); if (result.isPresent()) { return result.get(); } } catch (IllegalArgumentException e) { CookieClientRestrictedToProvider.LOG.warn(e, "Error decoding credentials (not a UUID)"); } catch (IllegalStateException e) { CookieClientRestrictedToProvider.LOG.warn(e, "Error decoding credentials (no session token)"); } catch (AuthenticationException e) { CookieClientRestrictedToProvider.LOG.warn(e, "Error authenticating credentials"); throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR); } // Must have failed to be here // Simply throw a 401 and leave it to the app to handle throw new WebApplicationException(Response.Status.UNAUTHORIZED); } }