FluentSecurity 2.0 support for action with parameters - asp.net-mvc-4

In my .net mvc 4 app I am using the latest release of FluentSecurity (1.4) in order to secure my actions.
Here is an example that illustrates my problem:
Suppose I have a controller with 2 edit actions (get and post):
public class MyController : Controller
{
//
// GET: /My/
public ActionResult Edit(decimal id)
{
var modelToReturn = GetFromDb(id);
return View(modelToReturn);
}
[HttpPost]
public ActionResult Edit(MyModel model)
{
Service.saveToDb(model);
return View(model);
}
}
Now, I would like to have a different security policy for each action. To do that I define (using fluent security):
configuration.For<MyController>(x => x.Edit(0))
.AddPolicy(new MyPolicy("my.VIEW.permission"));
configuration.For<MyController>(x => x.Edit(null))
.AddPolicy(new MyPolicy("my.EDIT.permission"));
The first configuration refers to the get while the second to the post.
If you wonder why I'm sending dummy params you can have a look here and here.
Problem is that fluent security can't tell the difference between those 2, hence this doesn't work.
Couldn't find a way to overcome it (I'm open for ideas) and I wonder if installing the new 2.0 beta release can resolve this issue.
Any ideas?

It is currently not possible to apply different policies to each signature in FluentSecurity. This is because FluentSecurity can not know what signature will be called by ASP.NET MVC. All it knows is the name of the action. So FluentSecurity has to treat both action signatures as a single action.
However, you can apply multiple policies to the same action (you are not limited to have a single policy per action). With this, you can apply an Http verb filter for each of the policies. Below is an example of what it could look like:
1) Create a base policy you can inherit from
public abstract class HttpVerbFilteredPolicy : ISecurityPolicy
{
private readonly List<HttpVerbs> _httpVerbs;
protected HttpVerbFilteredPolicy(params HttpVerbs[] httpVerbs)
{
_httpVerbs = httpVerbs.ToList();
}
public PolicyResult Enforce(ISecurityContext securityContext)
{
HttpVerbs httpVerb;
Enum.TryParse(securityContext.Data.HttpVerb, true, out httpVerb);
return !_httpVerbs.Contains(httpVerb)
? PolicyResult.CreateSuccessResult(this)
: EnforcePolicy(securityContext);
}
protected abstract PolicyResult EnforcePolicy(ISecurityContext securityContext);
}
2) Create your custom policy
public class CustomPolicy : HttpVerbFilteredPolicy
{
private readonly string _role;
public CustomPolicy(string role, params HttpVerbs[] httpVerbs) : base(httpVerbs)
{
_role = role;
}
protected override PolicyResult EnforcePolicy(ISecurityContext securityContext)
{
var accessAllowed = //... Do your checks here;
return accessAllowed
? PolicyResult.CreateSuccessResult(this)
: PolicyResult.CreateFailureResult(this, "Access denied");
}
}
3) Add the HTTP verb of the current request to the Data property of ISecurityContext and secure your actions
SecurityConfigurator.Configure(configuration =>
{
// General setup goes here...
configuration.For<MyController>(x => x.Edit(0)).AddPolicy(new CustomPolicy("my.VIEW.permission", HttpVerbs.Get));
configuration.For<MyController>(x => x.Edit(null)).AddPolicy(new CustomPolicy("my.EDIT.permission", HttpVerbs.Post));
configuration.Advanced.ModifySecurityContext(context => context.Data.HttpVerb = HttpContext.Current.Request.HttpMethod);
});

Related

Custom OpenIddictApplication field always returns null

I am using OpenIDDict and extending OpenIddictEntityFrameworkCoreApplication to include my own custom field:
public class TenantApplication : OpenIddictEntityFrameworkCoreApplication<long, TenantAuthorization, TenantToken> {
public long? TenantID { get; set; }
}
public class TenantAuthorization : OpenIddictEntityFrameworkCoreAuthorization<long, TenantApplication, TenantToken> { }
public class TenantScope : OpenIddictEntityFrameworkCoreScope<long> { }
public class TenantToken : OpenIddictEntityFrameworkCoreToken<long, TenantApplication, TenantAuthorization> { }
I register under AddDbContext:
builder.Services.AddDbContext<ApplicationDbContext>(options => {
options.UseNpgsql(connectionString);
options.UseOpenIddict<TenantApplication, TenantAuthorization, TenantScope, TenantToken, long>();
});
and also under AddCore:
// Register the OpenIddict core components.
.AddCore(options =>
options.UseEntityFrameworkCore()
.UseDbContext<ApplicationDbContext>()
.ReplaceDefaultEntities<TenantApplication, TenantAuthorization, TenantScope, TenantToken, long>();
options.UseQuartz();
})
In my Worker, I create the application record if it doesn't exist, but TenantID is always inserted with null:
if (await manager.FindByClientIdAsync("postman", cancellationToken) is null)
{
await manager.CreateAsync(new TokenOpenIddictApplicationDescriptor
{
TenantID = 2,
ClientId = "postman",
ClientSecret = "388D45FA-B36B-4988-BA59-B187D329C207",
DisplayName = "My client application",
Permissions =
{
Permissions.Endpoints.Token,
Permissions.GrantTypes.ClientCredentials
}
});
Similarly, manager.FindByClientIdAsync also returns the Application instance with a null TenantID (after I manually set it in the db). What step am I missing?
I figured this out on my own by diving into the OpenIDDict source code. In order to extend the Application fields, I extended OpenIddictApplicationManager and OpenIddictEntityFrameworkCoreApplicationStore. OpenIddictApplicationManager has a PopulateAsync method which converts a OpenIddictApplicationDescriptor to an Application instance. You need to override this method so that your custom Application fields will be set.
When initializing everything in Program.cs you also need to add your custom store and replace the application manager.
options.AddApplicationStore<TenantOpenIddictEntityFrameworkCoreApplicationStore>();
options.ReplaceApplicationManager<TenantOpenIddictApplicationManager>();
Maybe there is a better way of doing this, but this worked for me. It would be nice if there was some more easier to find examples with more complex scenarios.

Authorize with Claims instead of Policies in ASP.Net Identity

I have to migrate an app with custom authorizaton based on the presence of "keys" and "doors". Basically a number of keys are assigned to a user and that user can('t) do things / open doors based on the keys he got.
The obvious solution is moving to Claims-based authorization of ASP.Net Core Identity. Each key become a claim. The point is that I would like to check directly for the presence of the claim to open the door and not for the Policy. This to avoid to write (lots of as there are hundreds of keys) code.
So, from:
Startup.cs:
options.AddPolicy("Key1", policy => policy.RequireClaim("Key1"));
Controller:
[Authorize(Policy = "Key1")]
To something like:
Controller:
[Authorize(Claim = "Key1")]
Which is the best way to achieve this?
The recommend way is to use Policy based authorization , you can click here for similar discussion .
You can use custom authorization filter to meet your requirement , if you just check whether claim type exists in user's claims , you can try below code sample :
ClaimRequirementFilter.cs :
public class ClaimRequirementFilter : IAuthorizationFilter
{
readonly Claim _claim;
public ClaimRequirementFilter(Claim claim)
{
_claim = claim;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var hasClaim = context.HttpContext.User.Claims.Any(c => c.Type == _claim.Type);
if (!hasClaim)
{
context.Result = new ForbidResult();
}
}
}
ClaimRequirementAttribute.cs :
public class ClaimRequirementAttribute : TypeFilterAttribute
{
public ClaimRequirementAttribute(string claimType ) : base(typeof(ClaimRequirementFilter))
{
Arguments = new object[] { new Claim(claimType , "") };
}
}
And use like :
[ClaimRequirement("key")]
If you also need to restrict value of claim , you can follow the code sample from above link .

Play Framework handle Authorization not authentication

I am developing an application with Play Framework 2.2 and Java
I have implemented the authentication module just like the following tutorial
http://www.playframework.com/documentation/2.1.0/JavaGuide4
In a nutshell implemented a class Secured as
public class Secured extends Security.Authenticator{
#Override
public String getUsername(Context ctx) {
return ctx.session().get("email");
}
#Override
public Result onUnauthorized(Context ctx) {
return redirect(routes.Users.login());
}
}
and then in controllers I added this line to the methods of controllers
#Security.Authenticated(Secured.class)
public static Result methodOfController(){
//some codes here
return ok( someView.render());
}
As you can see it's just authentication not authorization, for example it checks if user is logged in but never checks if this is email of admin
My question is this: How should I add access rights to these class, or namely how can I add authorization to this authentication
please provide me with a descriptive answer that shows what modifications should I make to this class, controllers and even some other parts of project ( maybe models ) to have a proper authorization
please don't provide links to websites or weblogs unless they are focused on a very similar issue
You can look at a solution like Deadbolt that provides a solution for this, or you can roll your own. The main idea in Java is to use Action composition to create custom action annotations. Thus you could check for if a user is authenticated and then if the user is authorized for the requested resource.
I have written a simple authorization action composition for our project.
Before your actions or controllers you can add a line like the following:
#Auth({"GeneralManager","Manager"})
With the line above only the the people with the role "GeneralManager" or "Manager" can access the action or controller. The implementation of "AuthAction" can be like this:
public class AuthAction extends Action<Auth> {
public F.Promise<SimpleResult> call(Http.Context context) throws Throwable
{
String[] params = configuration.value();
int c = params.length;
boolean found = false;
if(params.length == 0) {
found = true;
}
// Loop the given parameters(role names) to check that the user belongs to one of them
for (String code: params) {
// validate types
int roleCount = Role.find.where().eq("code",code).findRowCount();
if(roleCount == 0) {
throw new Exception("Auth code is not found.");
}
if(user.role.code.equals(code)) {
found = true;
}
}
// if the role is not found for the user, it means the user is not authorised
if(!found) {
// no access, redirect to home
return F.Promise.pure(redirect("/"));
}
// execute the action
return delegate.call(context);
}
}

Breeze OData: Get single entry with GetEntityByKey (EntitySetController)

Reading the documentation in Breeze website, to retrieve a single entity have to use the fetchEntityByKey.
manager.fetchEntityByKey(typeName, id, true)
.then(successFn)
.fail(failFn)
Problem 1: Metadata
When trying to use this method, an error is displayed because the metadata has not yet been loaded. More details about the error here.
The result is that whenever I need to retrieve a single entity, have to check if the metadata is loaded.
manager = new breeze.EntityManager(serviceName);
successFn = function(xhr) {}
failFn = function(xhr) {};
executeQueryFn = function() {
return manager.fetchEntityByKey(typeName, id, true).then(successFn).fail(failFn);
};
if (manager.metadataStore.isEmpty()) {
return manager.fetchMetadata().then(executeQueryFn).fail(failFn);
} else {
return executeQueryFn();
}
Question
How can I extend the breeze, creating a Get method to check if metadata is loaded, and if not, load it?
Problem 2: OData and EntitySetController
I would use the OData standard (with EntitySetController) in my APIs.
This page in Breeze documentation shows how, then follow this tutorial to deploy my app with OData.
The problem as you can see here and here, is that the EntitySetController follows the odata pattern, to retrieve an entity must use odata/entity(id), or to retrieve all entities you can use `odata/entity'.
Example
In controller:
[BreezeController]
public class passosController : EntitySetController<Passo>
{
[HttpGet]
public string Metadata()
{
return ContextProvider.Metadata();
}
[HttpGet, Queryable(AllowedQueryOptions = AllowedQueryOptions.All, PageSize = 20)]
public override IQueryable<T> Get()
{
return Repositorio.All();
}
[HttpGet]
protected override T GetEntityByKey(int key)
{
return Repositorio.Get(key);
}
}
When I use:
manager = new breeze.EntityManager("/odata/passos");
manager.fetchEntityByKey("Passo", 1, true)
.then(successFn)
.fail(failFn)
The url generated is: /odata/passos/Passos?$filter=Id eq 1
The correct should be: /odata/passos(2)
Question
How can I modify Breeze for when use fetchEntityByKey to retrieve entity use odata/entity(id)?

Custom error pages in mvc 4 application, setup with Windows authentication

I have an intranet application setup with windows authentication. Like in most applications, certain parts of the application are accessible to specific roles only. When a user not in desired role would try to access that area, he should be shown a friendly "You do not have permission to view this page" view.
I searched and looked at several resources that guides to extend the Authorize Attribute. I tried that approach, but it simply doesn't work. I still get the IIS error message and the breakpoint in this custom attributes never gets hit. The breakpoint in my extended attibute doen't get hit even when a user in role visits the page. So, I am wondering if I am missing anything ?
This is what I have -
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeRedirect : AuthorizeAttribute
{
private const string IS_AUTHORIZED = "isAuthorized";
public string RedirectUrl = "~Areas/Errors/Http401";
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool isAuthorized = base.AuthorizeCore(httpContext);
httpContext.Items.Add(IS_AUTHORIZED, isAuthorized);
return isAuthorized;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
var isAuthorized = filterContext.HttpContext.Items[IS_AUTHORIZED] != null ? Convert.ToBoolean(filterContext.HttpContext.Items[IS_AUTHORIZED]) : false;
if(!isAuthorized && filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.RequestContext.HttpContext.Response.Redirect(RedirectUrl);
}
}
}
CONTROLLER -
[AuthorizeRedirect]
[HttpPost, ValidateInput(true)]
public ActionResult NewPart(PartsViewModel vmodel) {..}
Any ideas?
Thanks
I think you could use custom error pages instead. Use AuthorizeAttribute to restrict access by callers to an action method.
[Authorize (Roles="Editor, Moderator", Users="Ann, Gohn")]
public ActionResult RestrictedAction()
{
// action logic
}
Then you could use one of the ways those are proposed by #Marco. I like handle HTTP status code within Application_EndRequest. So, it is possible to solve your problem using by following:
protected void Application_EndRequest()
{
int status = Response.StatusCode;
if (Response.StatusCode == 401)
{
Response.Clear();
var rd = new RouteData();
rd.DataTokens["area"] = "Areas";
rd.Values["controller"] = "Errors";
rd.Values["action"] = "Http401";
IController c = new ErrorsController();
c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));
}
}
To clearly specifiey what happens to an existing response when the HTTP status code is an error, you should use existingResponse attribute of <httpErrors> element in your configuration file. If you want to the error page appears immediately, then use Replace value, in otherwise - PassThrough (see details in my issue).