We are trying to implement security in with our predefined set of permissions, which will serves the purpose whether to execute action method, show the view OR not, hide specific control(Like button,textbox etc) etc. So, while user getting logged in into the application we have the data of users role and there permissions.
So, my question is whether we should go for ActionFilter OR Authorize Filter? Initially we have tried with ActionFilter, but my action filter is getting called though the particular action is NOT executed/called.
Action Filter
using Microsoft.AspNetCore.Mvc.Filters;
namespace LMS.Web.Core.Classes
{
public class SecurityFilter : ActionFilterAttribute
{
private string permissionName;
private Permissions permissions;
public SecurityFilter(string m_permissionName)
{
permissionName = m_permissionName;
}
public override void OnActionExecuted(ActionExecutedContext context)
{
base.OnActionExecuted(context);
}
public override void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
}
}
}
and this action filter I have referred on one action method
[Route("Course/Info")]
[SecurityFilter("some permission name")]
public ActionResult CourseDetails()
{
return View();
}
So, while logging into the application the action filter is getting called. why this is so ?
We want to use the filter on view and controller side. So, basically we are looking like this
[Route("Course/Info")]
[SecurityFilter(PermissionName = "some permission")]
public ActionResult CourseDetails()
{
return View();
}
public class SecurityFilter : ActionFilterAttribute
{
public string PermissionName { get; set; }
public SecurityFilter(SessionServices _session)
{
session = _session;
}
public SecurityFilter()
{
//Unable able to remove the default constructor
// because of compilation error while using the
// attribute in my controller
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (session.GetSession<List<OrganizationUserRolePermission>>("OrganizationUserRolePermission") != null)
{
List<OrganizationUserRolePermission> permissionList = session.GetSession<List<OrganizationUserRolePermission>>("OrganizationUserRolePermission");
checkPermission = permissionList.Any(m => m.PermissionName == PermissionName);
if(!checkPermission)
{
// Redirect to unauthorized access page/error page
}
}
base.OnActionExecuting(context);
}
}
and whatever the permission we passed to the filter will check whether user has the permission OR not. Also, we are trying to inject session service into filter, but getting session null.
I'm not sure about your use case to pass the SessionServices
instance to filter attribute constructor but this is not possible as any
argument to Attribute invocation should be a compile-time constant
value.
Reference
Attribute parameters are restricted to constant values of the following types:
- Simple types (bool, byte, char, short, int, long, float, and double)
- string
- System.Type
- enums
- object (The argument to an attribute parameter of type object must be
a constant value of one of the above types.)
- One-dimensional arrays of any of the above types
Rather you could retrieve the stored session data inside the OnActionExecuting method directly to check the needed permissions.
Ideally Authorize attribute would be more appropriate in your case to check the user permissions to allow access to any view. I believe ActionFilter might be more suitable in case of any logging before/after the action execution.
Regarding the below
So, while logging into the application the action filter is getting called. why this is so ?
Please check the Filter Registration in your application code. Ideally if the filter is applied to any specific action (e.g. CourseDetails in your case) then it will be called only on that particular action execution.
Alternatively please include the Login action in your question so that we could check for the problem if any.
I hope this would help find a solution in your case!
Related
I am making an asp.NET MVC5 intranet website for my company and as I have just finished implementing authorization and a dynamic menu according to user roles in Active Directory, I need to apply restrictions for access. I have read on MSDN / Technet that you can apply authorizations by using [Authorize(Role="<YourRole>")] and I make it work perfectly. Now my problem is that I have a 20 differents roles, and each Role is linked with Documents and Categories in my MSSQL Database. This means that if you want to access a document or a ressource, you must first find the corresponding entry in the DB (If you need I can explain this further). So If I implement Authorizations with the [Authorize] attribute, I will have to check my DB to see if the row exists, and if does I add it. I have started to do that with a static class :
public static class CustomRoles
{
public const string Role1 = "Role1";
public const string Role2 = "Role2";
//And so on ...
}
Then in my controller action methods:
[Authorize(Roles=CustomRoles.Role1+","+CustomRoles.Role2)]
public ActionResult Index(){}
You can imagine doing this for each role will be long and tedious.
So my question : do you know of any better / simpler way to do this? Because I have to manually check each document (thousands!) and look then in another table what profiles are associated, and then apply the corresponding authorization. And technically, my dynamic menu is supposed to take care of this since you cannot see what is not available to you, but then again, using the URL you can access anything in this way if authorizations aren't implemented
And also : Not all roles are registered in my DB, most users have around 140 roles, but there are likely just 1 or 2 that are registered in the database. Is this going to create some performance issues? I know I can handle this when I create my Claims and filter out the ones not belonging to the DB but I'd prefer not to.
One workaround you could do is by using overidden OnActionExecuting method in the ActionFilter instead of decorators AuthorizeAttribute to check if a user is authorized to do certain action or not.
Yep, you still need to carefully check of your role authorization, but not sparsely, as you only need to check all of them in one place, namely your action filter (or, to be more precise, your single switch). Also, putting everything in one place enable you to avoid redundancy and make the logic more compact whenever possible.
Example:
The ActionFilter:
public class AuthorizeActionFilterAttribute : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
IPrincipal user = HttpContext.Current.User; //get the current user
//Get controller name and action
string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
string actionName = filterContext.ActionDescriptor.ActionName;
//All roles will be defined here!
List<string> rolesAccepted = new List<string>();
switch (controllerName){
case "Controller1": //suppose these three happen to have the same rules
case "Controller2":
case "Controller3":
//Describe roles accepted for certain controller and action
rolesAccepted = new List<string> { "Role1", "Role2" };
break;
case "Controller4": //suppose these have specific restrictions only for some actions
if (actionName == "action1") {//can also use switch
rolesAccepted = new List<string> { "Role3", "Role4" };
} else {
rolesAccepted = new List<string> { "Role5", "Role6", "Role7" };
}
break;
....
}
//Redirect to login if non of the user role is authorized
if (!rolesAccepted.Any(x => user.IsInRole(x)){
filterContext.Result = redirectToLogin();
return;
}
}
private ActionResult redirectToLogin() {
return new RedirectToRouteResult(
new RouteValueDictionary(new { controller = "Account", action = "Login" })
);
}
}
Then you only need to put the AuthorizeActionFilterAttribute in every controller (and not roles in every single action) as you have handled all authorization in the single place:
[AuthorizeActionFilter]
public class Controller1 : Controller {
...
}
[AuthorizeActionFilter]
public class Controller2 : Controller {
...
}
... and so on
Hi I want to pass parameters to the custom policy in asp.net core. As i know so far i can only do
[Authorize(Policy="Over21")]
My Action(){
}
but i want to do
[Authorize(Policy="Over21",Para="AnotherValue")]
My Action(){
}
so i decided to extend Authorize attribute and then do this.
public class MyAuthorizationAttribute : AuthorizeAttribute
{
public string Permission;
public MyAuthorizationAttribute(string policy, string permission)
: base(policy)
{
Permission = permission;
}
}
Then do this
[MyAuthorization("MyPolicy","CanDoPermission")]
My Action(){
}
The custom authorization handler is still called when i debug and so now i want a way to get the permission property from the AuthorizationHandlerContext context. I realize that i can find this property when i drill down during debug
context->Resource->Filters
Among this list i find all objects of type AuthorizeFilter and further drill down to AuthorizeData->Resultview this also is a list that contains my MyAuthorization object. I use a debut tool to do this but i dont know how to get MyAuthorization instance that applies to the specific action being called?
This is the furthest i get.
var mvcContext = context.Resource as AuthorizationFilterContext;
foreach (var filterMetadata in mvcContext.Filters)
{
if (filterMetadata.GetType().Name != "AuthorizeFilter") continue;
var claimAttribute = filterMetadata as AuthorizeFilter;
if (claimAttribute != null)
{
var data = claimAttribute.AuthorizeData;
}
Am not sure what to do next to get this object. I am hoping there is an easier way to get these values from the context.
I have a .net core application with several REST operations (see code below), similar to the following:
namespace Controllers
{
[Route("system")]
public class SystemController : Controller
{
// This is a public access method
[HttpGet("dictionaries/{name}")]
public List GetDictionary(HttpRequestMessage requestMsg, string name)
{
// etc
}
// This function shall be accessible only by an admin role
[AdminRole]
[HttpPost("dictionaries/{name}")]
public IActionResult PostDictionary(HttpRequestMessage requestMsg, string name)
{
// etc
}
}
}
I want to flag some operations to be accessible only by certain roles (i.e. admin). An elegant way to do it is using attributes.
Now I want to determine what would be the correct Middleware implementation for trapping the C# method to be invoked according to the URL, and fetch the role attribute (if any) by using reflection, so I can block unauthorized calls.
Please advice.
I want to draw attention that approach below is only if you for some reason does not want to use build-in Role based Authorization (as marked in comment to question).
If you create a global Action Filter (that is a part of MVC and so can operate with "controller logic"), you can get all needed information from ActionExecutingContext:
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
//Provides information about an action method, such as its name, controller, parameters, attributes, and filters.
var actionDescriptor = context.ActionDescriptor;
//Gets the controller instance containing the action.
var controller = context.Controller;
// Gets the arguments to pass when invoking the action. Keys are parameter names.
var actionArgs = context.ActionArguments;
}
...
}
context.ActionDescriptor can be casted to ControllerActionDescriptor. That allows to directly use the following properties:
public class ControllerActionDescriptor : ActionDescriptor
{
public string ControllerName { get; set; }
public virtual string ActionName { get; set; }
public MethodInfo MethodInfo { get; set; }
public TypeInfo ControllerTypeInfo { get; set; }
...
}
Not sure that you can use middleware for that, as controllers is part of MVC middleware. If you place your middleware before it, there is no "controller" logic at that pipeline step yet. And if after - it is too late for what you want.
I am using MVC4 and what I want to be able to do is to check the status of a user's friend to see whether they are online or not for chat purposes. I have explored the WebSecurity and MEmebership classes but cannot see any functionality within these. Do I have to do something with getting the sessions from the IIS server?
Thanks in advance
There is MembershipUser.IsOnline Property but if this is not useful you can use signlR to check the current status and may be store it somewhere.
Else you can do an ajax call every few minutes to check user status and also store it somewhere.
One possible solution I am suggesting you is to create a global filter that populates LastActionTime field of every user and then use this field to determine whether the user is online or not:
First create a new property of type DateTime? called LastActionTime in your User class in case you are using Code-First approach. In case you are using Database-First approach just add nullable DateTime column in you Users table and update your local model.
public class User
{
// ...
public DateTime? LastActionTime { get; set; }
// ...
}
Then create a new class called LogUserActionTimeFilter and implement it's OnActionExecuting and OnActionExecuted method. If you have any services or DbContexts, initialize them in the constructor of the class.
public class LogUserActionTimeFilter : IActionFilter
{
private readonly IUserAuthService userAuthService;
public LogAdminRequestFilter()
: this(new UserAuthService())
{
}
public LogAdminRequestFilter(IUserAuthService userAuthService)
{
this.userAuthService = userAuthService;
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
this.userAuthService.SaveCurrentTimeAsLastActionTime(
filterContext.HttpContext.User.Identity.Name);
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
The SaveCurrentTimeAsLastActionTime method just sets LastActionTime to DateTime.Now of the given user and saves changes. It should be easy to implement.
When done with the filter you can apply it to specific actions, controllers (e.g. in the chat only) or globaly:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new LogUserActionTimeFilter());
}
}
When you need to determine if a user is online or not just check its LastActionTime column/property and if it is less than 5 minutes (for example) before DateTime.Now then the user may be considered online.
I recently starded developing for MVC 3 but have experience in both C# and ASP.NET since earlier. So i'll start with what i'm trying to accomplish. I've developed a small site for hosting articles. I've implemented SQLServer based membership managament to the site. Now i want to create a credentials system that restricts and allows the right users to create, delete and update articles. There is one simple solution to this and that is to do it like this:
[Authorize(Roles="Admin")]
public ActionResult UpdateArticle(ArticleModel model, int articleid)
{
return View();
}
Now this is really simple. I simply say that only members that are in the role "Admin" are allowed to update an article. But that's just to static. So i created a credentials table in my database that in the end tells me that "Article 5 can be edited by roles 1,2,3 & 4 and by users A, b & C". So far so good. But how would i implement that with the Authorize solution?
I would like to do something like this:
[Authorize(getAuthorizedusers("update",this.articleid))]
where getAuthorizedusers returns which users and roles are authorized to update the article with the articleid that was passed to it.
So I have (at least) two problems here:
-Getting the Authorize method to accept multiple users and roles.
-Passing the supplied articleid, that was sent to the UpdateArticle method, to the getAuthorizedusers method.
You can create your own custom attribute that inherits from AuthorizeAttribute and override the OnAuthorize method to do what you need.
This should get you started:
public class ArticleAuthorizeAttribute : AuthorizeAttribute
{
public enum ArticleAction
{
Read,
Create,
Update,
Delete
}
public ArticleAction Action { get; set; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
//do custom authorizization using Action and getting ArticleID
//from filterContext.HttpContext.Request.QueryString or
//filterContext.HttpContext.Request.Form
}
}
The usage would look like this:
[ArticleAuthorize(Action=ArticleAuthorizeAttribute.ArticleAction.Update)]
Edit: After looking into this a bit more, it looks like you can't pass this.articleID in to the attribute. However, you do have access to the parameters from filterContext.HttpContext.Request through the QueryString property or the Form property, depending on how you are passing the values. I have updated the code sample appropriately.
A more complete example can be found here
To check for authorization using user role and user list you would do something like this:
var allowedUsers = new List<string>();
//populate allowedUsers from DB
If (User.IsInRole("Update") || allowedUsers.Contains(User.Identity.Name))
{
//authorized
}
Alternatively, you can do both checks against the DB directly in a single method to keep from making two calls.
Here's a much easier way to accomplish the same thing:
[Authorize]
public ActionResult UpdateArticle(ArticleModel model, int articleid)
{
// if current user is an article editor
return View();
// else
return View("Error");
}
I got it working as I wanted when I overrode the AuthorizeCore method and authorizes the way I want to.
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
IPrincipal user = httpContext.User;
if (!user.Identity.IsAuthenticated)
{
return false;
}
if ((_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase)) && (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole)))
{
return false;
}
return true;
}