
be.objectify.deadbolt.java.actions.AbstractDeadboltAction Maven / Gradle / Ivy
/*
* Copyright 2010-2012 Steve Chaloner
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package be.objectify.deadbolt.java.actions;
import be.objectify.deadbolt.core.models.Subject;
import be.objectify.deadbolt.java.ConfigKeys;
import be.objectify.deadbolt.java.DeadboltExecutionContextProvider;
import be.objectify.deadbolt.java.DeadboltHandler;
import be.objectify.deadbolt.java.ExecutionContextProvider;
import be.objectify.deadbolt.java.JavaAnalyzer;
import be.objectify.deadbolt.java.cache.HandlerCache;
import be.objectify.deadbolt.java.cache.SubjectCache;
import be.objectify.deadbolt.java.utils.ReflectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.Configuration;
import play.libs.F;
import play.mvc.Action;
import play.mvc.Http;
import play.mvc.Result;
import play.mvc.Results;
import java.util.Optional;
/**
* Provides some convenience methods for concrete Deadbolt actions, such as getting the correct {@link DeadboltHandler},
* etc. Extend this if you want to save some time if you create your own action.
*
* @author Steve Chaloner ([email protected])
*/
public abstract class AbstractDeadboltAction extends Action
{
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDeadboltAction.class);
private static final String ACTION_AUTHORISED = "deadbolt.action-authorised";
private static final String ACTION_UNAUTHORISED = "deadbolt.action-unauthorised";
private static final String ACTION_DEFERRED = "deadbolt.action-deferred";
private static final String IGNORE_DEFERRED_FLAG = "deadbolt.ignore-deferred-flag";
final JavaAnalyzer analyzer;
final SubjectCache subjectCache;
final HandlerCache handlerCache;
final Configuration config;
final DeadboltExecutionContextProvider executionContextProvider;
public final boolean blocking;
public final long blockingTimeout;
protected AbstractDeadboltAction(final JavaAnalyzer analyzer,
final SubjectCache subjectCache,
final HandlerCache handlerCache,
final Configuration config,
final ExecutionContextProvider ecProvider)
{
this.analyzer = analyzer;
this.subjectCache = subjectCache;
this.handlerCache = handlerCache;
this.config = config;
this.executionContextProvider = ecProvider.get();
this.blocking = config.getBoolean(ConfigKeys.BLOCKING_DEFAULT._1,
ConfigKeys.BLOCKING_DEFAULT._2);
this.blockingTimeout = this.config.getLong(ConfigKeys.DEFAULT_BLOCKING_TIMEOUT_DEFAULT._1,
ConfigKeys.DEFAULT_BLOCKING_TIMEOUT_DEFAULT._2);
}
/**
* Gets the current {@link DeadboltHandler}. This can come from one of two places:
* - a handler key is provided in the annotation. A cached instance of that class will be used. This has the highest priority.
* - the global handler defined in the application.conf by deadbolt.handler. This has the lowest priority.
*
* @param handlerKey the DeadboltHandler key, if any, coming from the annotation.
* @param the actual class of the DeadboltHandler
* @return an option for the DeadboltHandler.
*/
protected DeadboltHandler getDeadboltHandler(final String handlerKey) throws Throwable
{
LOGGER.debug("Getting Deadbolt handler with key [{}]",
handlerKey);
return handlerCache.apply(handlerKey);
}
/** {@inheritDoc} */
@Override
public F.Promise call(final Http.Context ctx) throws Throwable
{
F.Promise result;
Class annClass = configuration.getClass();
if (isDeferred(ctx))
{
result = getDeferredAction(ctx).call(ctx);
}
else if (!ctx.args.containsKey(IGNORE_DEFERRED_FLAG)
&& ReflectionUtils.hasMethod(annClass, "deferred") &&
(Boolean)annClass.getMethod("deferred").invoke(configuration))
{
defer(ctx,
this);
result = delegate.call(ctx);
}
else
{
result = execute(ctx);
}
return result;
}
/**
* Execute the action.
*
* @param ctx the request context
* @return the result
* @throws Throwable if something bad happens
*/
public abstract F.Promise execute(final Http.Context ctx) throws Throwable;
/**
* @param subject
* @param roleNames
* @return
*/
protected boolean checkRole(Optional subject,
String[] roleNames)
{
return analyzer.checkRole(subject,
roleNames);
}
/**
* @param subject
* @param roleNames
* @return
*/
protected boolean hasAllRoles(Optional subject,
String[] roleNames)
{
return analyzer.hasAllRoles(subject,
roleNames);
}
/**
* Wrapper for {@link DeadboltHandler#onAuthFailure} to ensure the access failure is logged.
*
* @param deadboltHandler the Deadbolt handler
* @param content the content type hint
* @param ctx th request context
* @return the result of {@link DeadboltHandler#onAuthFailure}
*/
protected F.Promise onAuthFailure(final DeadboltHandler deadboltHandler,
final String content,
final Http.Context ctx)
{
LOGGER.warn("Deadbolt: Access failure on [{}]",
ctx.request().uri());
F.Promise result;
try
{
result = deadboltHandler.onAuthFailure(ctx,
content);
}
catch (Exception e)
{
LOGGER.warn("Deadbolt: Exception when invoking onAuthFailure",
e);
result = F.Promise.pure(Results.internalServerError());
}
return result;
}
/**
* Gets the {@link be.objectify.deadbolt.core.models.Subject} from the {@link DeadboltHandler}, and logs an error if it's not present. Note that
* at least one actions ({@link Unrestricted} does not not require a Subject to be present.
*
* @param ctx the request context
* @param deadboltHandler the Deadbolt handler
* @return the Subject, if any
*/
protected F.Promise> getSubject(final Http.Context ctx,
final DeadboltHandler deadboltHandler)
{
return subjectCache.apply(deadboltHandler,
ctx)
.map(option -> {
if (!option.isPresent())
{
LOGGER.info("Access to [{}] requires a subject, but no subject is present.",
ctx.request().uri());
}
return option;
});
}
/**
* Marks the current action as authorised. This allows method-level annotations to override controller-level annotations.
*
* @param ctx the request context
*/
protected void markActionAsAuthorised(Http.Context ctx)
{
ctx.args.put(ACTION_AUTHORISED,
true);
}
/**
* Marks the current action as unauthorised. This allows method-level annotations to override controller-level annotations.
*
* @param ctx the request context
*/
protected void markActionAsUnauthorised(Http.Context ctx)
{
ctx.args.put(ACTION_UNAUTHORISED,
true);
}
/**
* Checks if an action is authorised. This allows controller-level annotations to cede control to method-level annotations.
*
* @param ctx the request context
* @return true if a more-specific annotation has authorised access, otherwise false
*/
protected boolean isActionAuthorised(Http.Context ctx)
{
Object o = ctx.args.get(ACTION_AUTHORISED);
return o != null && (Boolean) o;
}
/**
* Checks if an action is unauthorised. This allows controller-level annotations to cede control to method-level annotations.
*
* @param ctx the request context
* @return true if a more-specific annotation has blocked access, otherwise false
*/
protected boolean isActionUnauthorised(Http.Context ctx)
{
Object o = ctx.args.get(ACTION_UNAUTHORISED);
return o != null && (Boolean) o;
}
/**
* Defer execution until a later point.
*
* @param ctx the request context
* @param action the action to defer
*/
protected void defer(Http.Context ctx,
AbstractDeadboltAction action)
{
if (action != null)
{
LOGGER.info("Deferring action [{}]",
this.getClass().getName());
ctx.args.put(ACTION_DEFERRED,
action);
}
}
/**
* Check if there is a deferred action in the context.
*
* @param ctx the request context
* @return true iff there is a deferred action in the context
*/
public boolean isDeferred(Http.Context ctx)
{
return ctx.args.containsKey(ACTION_DEFERRED);
}
/**
* Get the deferred action from the context.
*
* @param ctx the request context
* @return the deferred action, or null if it doesn't exist
*/
public AbstractDeadboltAction getDeferredAction(final Http.Context ctx)
{
AbstractDeadboltAction action = null;
Object o = ctx.args.get(ACTION_DEFERRED);
if (o != null)
{
action = (AbstractDeadboltAction)o;
ctx.args.remove(ACTION_DEFERRED);
ctx.args.put(IGNORE_DEFERRED_FLAG,
true);
}
return action;
}
public F.Promise> preAuth(final boolean forcePreAuthCheck,
final Http.Context ctx,
final DeadboltHandler deadboltHandler)
{
return forcePreAuthCheck ? deadboltHandler.beforeAuthCheck(ctx)
: F.Promise.pure(Optional.empty());
}
public static F.Promise sneakyCall(final Action> action,
final Http.Context context)
{
try
{
return action.call(context);
}
catch (Throwable t)
{
throw sneakyThrow(t);
}
}
private static RuntimeException sneakyThrow(final Throwable t)
{
if (t == null)
{
throw new NullPointerException();
}
sneakyThrow0(t);
return null;
}
@SuppressWarnings("unchecked")
private static void sneakyThrow0(final Throwable t) throws T
{
throw (T)t;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy