Composite constraints
It is possible to create composite constraints by creating a tree of the existing constraint types. Because of the inflexibility of Java annotations, combined with a preference for re-usability, composite constraints are objects built and registered in code.
The available constraint types are
- subject present
- subject not present
- restrict
- pattern
- dynamic
- constraint tree
The first five are exactly as covered elsewhere in this documentation; a constraint tree contains a list of constraints along with the AND/OR operator that should be applied to them. Because ConstraintTree
is also a Constraint
, sub-trees can be used to form arbitrarily complex constraints.
To create and register a composite constraint, the easiest way is to use an eager singleton, and inject it with instances of CompositeCache
and ConstraintBuilders
. The ConstraintBuilders
instance contains everything you need to create constraints. Once you have a constraint, you need to register it with the composite cache.
import be.objectify.deadbolt.java.cache.CompositeCache;
import be.objectify.deadbolt.java.composite.ConstraintBuilders;
import be.objectify.deadbolt.java.composite.ConstraintTree;
import be.objectify.deadbolt.java.composite.Operator;
import be.objectify.deadbolt.java.models.PatternType;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class CompositeConstraints
{
@Inject
public CompositeConstraints(final CompositeCache compositeCache,
final ConstraintBuilders builders)
{
compositeCache.register("curatorOrSubjectNotPresent",
new ConstraintTree(Operator.OR,
builders.subjectNotPresent().build(),
builders.pattern("curator.museum.*",
PatternType.REGEX).build()));
}
}
In your controllers, you can now use the Composite
annotation to apply the constraint to either the controller class or a specific method, in the same way you would any other Deadbolt annotation.
@Composite("curatorOrSubjectNotPresent")
public class Foo extends Controller
{
...
}
Finally, register a binding for CompositeConstraints
. You can do this in the same place as you register your HandlerCache
, as described in Integrating Deadbolt.
public class CustomDeadboltHook extends Module
{
@Override
public Seq<Binding<?>> bindings(final Environment environment,
final Configuration configuration)
{
return seq(bind(HandlerCache.class).toInstance(new MyHandlerCache(new MyDeadboltHandler())),
bind(CompositeConstraints.class).toSelf().eagerly());
}
}
Constraint re-use
Constraints contain only configurational state, and so can be used in multiple constraint trees.
final Constraint c1 = new ConstraintTree(// some constraints);
final Constraint c2 = new ConstraintTree(// some constraints);
final Constraint c3 = new ConstraintTree(// some constraints);
compositeCache.register("fooConstraint",
new ConstraintTree(Operator.OR,
builders.subjectNotPresent().build(),
c1,
c3));
compositeCache.register("barConstraint",
new ConstraintTree(Operator.AND,
c2,
c3));
Building constraints
You can see in the examples above that constraints have .build()
hanging off the end of each call to builders
. This is because further customization of the constraints is possible, allowing you to override the reasonable (i.e. harmless) defaults that are pre-set.
Each constraint has various options; one that is present for every constraint is content(Optional<String> content)
, which allows you to provide a hint for the onAuthFailure
method of your Deadbolt handler implementations if authorization should fail. Other options include providing additional information for dynamic and pattern constraints, via meta(Optional<String> meta)
and inverting the meaning of pattern constraints via invert(boolean invert)
.
All of these options have the same meaning as those found in controller constraint annotations and template parameters.
Updated less than a minute ago