Instantly share code, notes, and snippets.
Created
November 11, 2014 11:44
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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