-
Notifications
You must be signed in to change notification settings - Fork 43
SecurityContext
When a policy is enforced it is passed an instance of ISecurityContext. ISecurityContext contains information about the current state of things. Such as "Is the user authenticated?" and "What are the roles of the current user?".
As of version 2.0, ISecurityContext contains a Data property. The Data property is an ExpandoObject that allows you to add custom data to it. You can then get to it from within a policy when it is enforced.
By default the Data property contains the RouteValueDictionary for the current request (x.Data.RouteValues). This allows you to write policies that use route data to determine if a policy is met or not.
In order to add data to the Data property of ISecurityContext you need to specify a modifyer. This is done by calling configuration.Advanced.ModifySecurityContext(context => {}). In the closure you can access the Data property and add you custom data. So if you wanted to add information about the HTTP method used you could do something like this:
configuration.Advanced.ModifySecurityContext(context =>
context.Data.HttpMethod = HttpContext.Current.Request.HttpMethod
);
FluentSecurity allows you to create and use custom security contexts. Simply implement the ISecurityContext interface and register the instance in your IoC-container. When FluentSecurity needs an instance of ISecurityContext it will ask your container for it. It can be resolved multiple times per HTTP request so make sure you set the appropriate lifecycle in your container. Note that registering a context in your container will require you to manually add route values to the Data property. See the next section for an easier and better approach.
With the addition of the Data property on ISecurityContext it's become much easier to create policies that can access route values etc. But wouldn't it be nice to be able to work with typed contexts? Well, you can! Let's say you want an easy way to access the value of the id route parameter. Here's an example:
public class ContextWithId : SecurityContextWrapper
{
public string Id { get; private set; }
public ContextWithId(ISecurityContext securityContext) : base(securityContext)
{
Id = Data.RouteValues["Id"];
}
}public class PolicyConsumingId : SecurityPolicyBase<ContextWithId>
{
public override PolicyResult Enforce(ContextWithId securityContext)
{
var currentId = securityContext.Id;
... Do stuff with the id ...
}
}As you can see it's as easy as creating a context inheriting from SecurityContextWrapper (or MvcSecurityContext). To use a typed context in a policy you need to inherit from SecurityPolicyBase<T> where T is the type of context. This means that you can have different types of contexts for each policy. In order for this to work properly you need to make sure your context has a single constructor argument of type ISecurityContext.
You generally don't want to register contexts in your IoC-container! It is possible but it is not recommended as it has a few known side effects:
- You will manually have to add route values to the Data property
- You will have to implement the interface from scratch
The MvcSecurityContext class is simply a helper context that gives you an easy way to access route values. So instead of typing Data.RouteValues you get a property called RouteValues on your context that is of type RouteValueDictionary. MvcSecurityContext can be used as is or be inherited from.