Skip to content

Instantly share code, notes, and snippets.

@joantune
Created November 11, 2014 11:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joantune/600979e1f993fc5dc00b to your computer and use it in GitHub Desktop.
Save joantune/600979e1f993fc5dc00b to your computer and use it in GitHub Desktop.
Several needed Java files for a quick and dirty fix on maintaining the context on a Play Framework 2.x with SecureSocial
SecuredActionCorrected.java:
package auth.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import play.mvc.With;
import securesocial.core.java.Authorization;
import auth.DummyAuthorization;
import auth.actions.SecuredCorrected;
@With(SecuredCorrected.class)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
/**
* An annotation to mark actions as protected by SecureSocial
* When the user is not logged in the action redirects the browser to the login page.
*
* If the isAjaxCall parameter is set to true SecureSocial will return a forbidden error
* with a json error instead.
*/
public @interface SecuredActionCorrected {
/**
* Specifies whether the action handles an ajax call or not. Default is false.
*
* @return
*/
boolean ajaxCall() default false;
/**
* The Authorization implementation that checks if the user is allowed to execute this action.
* By default, all requests are accepted.
*
* @return
*/
Class<? extends Authorization> authorization() default DummyAuthorization.class;
/**
* The parameters that are passed to the Authorization.isAuthorized implementation
*
* @return
*/
String[] params() default {};
}
SecuredActionCorrected.java:
/**
*
*/
package auth.actions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.libs.F.Promise;
import play.libs.Json;
import play.libs.Scala;
import play.mvc.Http;
import play.mvc.Http.Context;
import play.mvc.SimpleResult;
import scala.Option;
import scala.util.Either;
import securesocial.core.Authenticator;
import securesocial.core.Identity;
import securesocial.core.IdentityProvider;
import securesocial.core.UserService$;
import securesocial.core.java.Authorization;
import securesocial.core.java.SecureSocial.Secured;
import securesocial.core.providers.utils.RoutesHelper;
import util.Util;
import auth.annotations.SecuredActionCorrected;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* @author joantune
*
*/
public class SecuredCorrected extends Secured {
public static final Logger Logger = LoggerFactory.getLogger(SecuredCorrected.class);
static final String ORIGINAL_URL = "original-url";
/**
* The user key
*/
public static final String USER_KEY = "securesocial.user";
/**
* Generates the error json required for ajax calls calls when the
* user is not authenticated
*
* @return
*/
private static ObjectNode ajaxCallNotAuthenticated() {
ObjectNode result = Json.newObject();
result.put("error", "Credentials required");
return result;
}
/**
* Generates the error json required for ajax calls calls when the
* user is not authorized to execute the action
*
* @return
*/
private static ObjectNode ajaxCallNotAuthorized() {
ObjectNode result = Json.newObject();
result.put("error", "Not authorized");
return result;
}
@Override
public Promise<SimpleResult> call(Context ctx) throws Throwable {
boolean shouldSetToNull = false;
Authenticator debugAuthenticator = null;
Identity debugUser = null;
try {
shouldSetToNull = CorrectedUtils.fixHttpContext(ctx);
final Authenticator authenticator = CorrectedUtils.getAuthenticatorFromRequest(ctx);
final Identity user = authenticator != null ? CorrectedUtils.currentUser(authenticator) : null;
debugUser = user;
debugAuthenticator = authenticator;
if (user == null) {
if (Logger.isDebugEnabled()) {
Logger.debug("[securesocial] anonymous user trying to access : " + ctx.request().uri());
}
if (((SecuredActionCorrected) configuration).ajaxCall()) {
return Promise.pure((SimpleResult) unauthorized(ajaxCallNotAuthenticated()));
} else {
ctx.flash().put("error", play.i18n.Messages.get("securesocial.loginRequired"));
ctx.session().put(ORIGINAL_URL, ctx.request().uri());
return Promise.pure(redirect(RoutesHelper.login().absoluteURL(ctx.request(), IdentityProvider.sslEnabled())));
}
} else {
Authorization authorization = ((SecuredActionCorrected) configuration).authorization().newInstance();
if (authorization.isAuthorized(user, ((SecuredActionCorrected) configuration).params())) {
ctx.args.put(USER_KEY, user);
CorrectedUtils.touch(authenticator);
return delegate.call(ctx);
} else {
if (((SecuredActionCorrected) configuration).ajaxCall()) {
return Promise.pure((SimpleResult) forbidden(ajaxCallNotAuthorized()));
} else {
return Promise.pure(redirect(RoutesHelper.notAuthorized()));
}
}
}
} catch (Throwable ex) {
//ok, let's just log this in more detail
Logger.error("Got an error while trying to authenticate. Authenticator: " + Util.toString(debugAuthenticator)
+ " Identity: " + Util.toString(debugUser), ex);
throw ex;
} finally {
//leave it null as it was before, just in case.
if (shouldSetToNull) {
Http.Context.current.set(null);
}
}
}
}
CorrectedUtils.java:
package auth.actions;
import play.libs.Scala;
import play.mvc.Http;
import scala.Option;
import scala.util.Either;
import securesocial.core.Authenticator;
import securesocial.core.Identity;
import securesocial.core.UserService$;
public class CorrectedUtils {
static boolean fixHttpContext(Http.Context ctx) {
// As of Play 2.0.3:
// I don't understand why the ctx is not set in the Http.Context thread local variable.
// I'm setting it by hand so I can retrieve the i18n messages and currentUser() can work.
// will find out later why this is working this way, if you know why this is not set let me know :)
// This is looks like a bug, Play should be setting the context properly.
if (Http.Context.current.get() == null) {
Http.Context.current.set(ctx);
return true;
}
return false;
}
public static Identity currentUser(Authenticator authenticator) {
Identity result = null;
if (authenticator != null) {
Option<Identity> optionalIdentity = UserService$.MODULE$.find(authenticator.identityId());
result = Scala.orNull(optionalIdentity);
}
return result;
}
static void touch(Authenticator authenticator) {
Authenticator.save(authenticator.touch());
}
/**
* Retrieves the authenticator from the request
*
* @param ctx the current context
* @return the Authenticator or null if there isn't one or has expired.
*/
static securesocial.core.Authenticator getAuthenticatorFromRequest(Http.Context ctx) {
Http.Cookie cookie = ctx.request().cookies().get(Authenticator.cookieName());
Authenticator result = null;
if (cookie != null) {
Either<Error, Option<Authenticator>> maybeAuthenticator = Authenticator.find(cookie.value());
if (maybeAuthenticator.isRight()) {
result = Scala.orNull(maybeAuthenticator.right().get());
if (result != null && !result.isValid()) {
Authenticator.delete(result.id());
ctx.response().discardCookie(Authenticator.cookieName(), Authenticator.cookiePath(),
Scala.orNull(Authenticator.cookieDomain()), Authenticator.cookieSecure());
result = null;
}
}
}
return result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment