How do I add permission to a view or controller dynamically? - asp.net-mvc

In all of my asp.net mvc projects up to this point, permissions to controllers are set by using a custom class based on the [Authorize] attribute.
However, what if I wanted an administrator role who could grant access to views instead of going through the trouble of having to touch the controller to add/remove roles, re-complile, and push the changes to production. How would I go about doing this?

as discussed. Try the below.
in the controller as I'm sure you're aware.
[PermissionsFilter("CanAccessMyView")]
public ActionResult ReturnMyView ()
{
//etc..
}
Then, in your custom class
public class PermissionsFilter : AuthorizeAttribute
{
private readonly PermissionManager _permissionsManager;
public PermissionsFilter(string permissionName)
{
_permissionName = permissionName;
_permissionsManager = new PermissionServiceManager();
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!_permissionServiceManager.CanAccessPermission(_permissionName))
{
var urlHelper = new UrlHelper(filterContext.RequestContext);
var url = urlHelper.Action("Unauthorised", "Home");
filterContext.Result = new RedirectResult(url);
}
}
}
Where the permissions manager is querying the database or perhaps session info to see if the user user has access.
Hope that helps.

Related

Alternative way to handle controller action authorization instead of using customattribute

i have webapi action which is decorated with customauthattribute for authorization. This attribute internally checks with db if current user has viewcustomer permissions. Does anyone know better way of handling it instead of using customattribute. may be intercepting somewhere all request and run authorization checks for user/permisson/resource he is trying to access : eg getcustomer for customer id 10. So if user doesnt have access see customer id 10 he should get 403 status.
[CheckPermission(Process.CustomerManagment,Permissions.View)]
public IHttpActionResult GetCustomer(int customerId)
{
}
You can add global filters in the configuration of your web api.
Practically in your startup class (startup.cs or webapi.config) you can call on the httpconfiguration object the following method
var config = new HttpConfiguration();
config.Filters.Add(new MyAuthFilterAttribute());
In this way it will be global for all your api calls.
You should extend the IAuthenticationFilter interface.
take a look here for documentation
webapi documentation
One option would be to create a filter that you can apply globally. For example, something like this. Yes it's horrible but gives you a start:
public class GlobalAuthoriseAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
var actionName = filterContext.ActionDescriptor.ActionName;
switch (controllerName)
{
case "Home":
//All call to home controller are allowed
return;
case "Admin":
filterContext.Result = new HttpUnauthorizedResult();
return;
}
}
}
Now you can add this to your entire app in the App_Start\FilterConfig.cs file:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new GlobalAuthoriseAttribute());
}

Impersonate for one request (Asp.net MVC)

In my ASP.net MVC project I've got (among other roles) moderators and users. I want to give the moderators the option to "see current page as user".
My approach is to create a ActionFilterAttribute and overload OnActionExecuting & OnResultExecuted as the page is then rendered for the given user.
The first idea there was to juggle with the Roles:
OnActionExecuting {
... //various checks, if role exist, if user want to switch
var tempRoles = Roles.getRolesForUser(user);
filterContext.HttpContext.Items["tempRole"] = tempRoles;
Roles.RemoveUserFromRoles(user, tempRoles)
Roles.AddUserToRole(user, targetRole);
}
and then
OnResultExecuted {
//if switched view
{
Roles.RemoveUserFromRole(user,targetRole)
Roles.AddUserToRoles(filterContext.HttpContext.Items["tempRole"])
}
This works, but in a worst case scenario the roles are gone, so i prefer to not touch them...
My second idea was to create a dummy user add him to the userroles sign the moderator into this account with FormsAuthentication.SetAuthCookie(dummyUser, true) and revert everything in the OnResultExecuted, so in a worst case scenario the user is in the dummyRole (where he can logout) and the dummyUser is in the Database.
After debugging and researching I realised that SetAuthCookie requires a Redirect to come into effect - so it doesn't work this way.
The questions:
Is there a way to force SetAuthCookie to come into affect without a redirect
Any other suggestion/approaches how to accomplish this "see page as other user"?
If my first idea is the only solution, how do i make it foolproof?
Ahoi Christian,
you could decorate the class SqlRoleProvider and add it to the role manager.
See Sample Role-Provider Implementation:
http://msdn.microsoft.com/en-us/library/tksy7hd7%28v=vs.100%29.aspx
The decorated SqlRoleProvider could overwrite the IsUserInRole method and thereby implement impersonation functionality.
edit: I have added the code below:
public class MyRoleProvider : SqlRoleProvider
{
private static ConcurrentDictionary<string, string> impersonationList;
public MyRoleProvider() : base()
{
impersonationList = new ConcurrentDictionary<string, string>();
}
public static void startImpersonate(string username, string rolename)
{
impersonationList.TryAdd(username,rolename);
}
public override string[] GetRolesForUser(string username) {
if (impersonationList.ContainsKey(username))
return new string[] { impersonationList[username] };
else
return base.GetRolesForUser(username);
}
public static void stopImpersonate(string username)
{
string rolename;
impersonationList.TryRemove(username, out rolename);
}
}

mvc role based permission using actionfilterattribute

So I'm setting up my permission for an mvc website. And I'm doing a role based permission, having actions in a controller would require different Roles depending on the purpose of the action.
I know that the most recommended would be authorizeattribute (as i want the roles cached) but is it possible to have the same with the actionfilterattribute?
Currently I have an actionfilterattribute similar to this:
public class PermissionRequired : ActionFilterAttribute{
private readonly Role reqrole;
public PermissionRequired(Role reqRole)
{
reqrole = reqRole;
}
public override void OnActionExecuting(ActionExecutingContext filterContext) {
var ctrl = (GeneralController)filterContext.Controller;
if (!ctrl.CurrentUser.InRole(reqrole)) {
//some code to redirect this to a certain page
}
base.OnActionExecuting(filterContext);
}
}
and on the GeneralController to get the current User
public class GeneralController : Controller
private User currentUser;
public User CurrentUser {
get {
if (currentUser != null)
return currentUser;
int currentUserId = Convert.ToInt32(httpContext.User.identity.Name);
if (currentUserId != 0) {
this.currentUser = Tds.Users.FirstOrDefault(u => u.Id == currentUserId)
}
return currentUser;
}
}
and on the controllers that will inherit this attribute
[PermissionRequired(Role.Moderator)]
public class SomeControllerThatNeedsPermission
{
[PermissionRequired(Role.SuperAdmin)]
public ActionResult SomeActionThatNeedsPermission()
{
}
}
so, anybody help is appreciated.. even comments or thoughts are welcome :D
Thanks much!
It seems like you are not using custom membership here. In which case doing this with a actionfilterattribute is somewhat pointless, but nonetheless do able.
This is an excellent article on the same subject - extending the AuthorizeAttribute to perform role based validation and return custom errors...
The value in doing that also only comes across (as explained in the article) when you wish to show users whats going on when the Authorization fails (the 401 is not shown it turns into a 302 internally in the mvc plumbing)

ASP .NET MVC Securing a Controller/Action

If I want only administrator to access the action called "ManagerUser", I know I can do this:
[Authorize( Roles = Constants.ROLES_ADMINISTRATOR )]
public ActionResult ManageUser( string id )
{
}
What if I want to give everyone access except to administrator? I do not want to write all roles up there on function :|.
Any recommendations/way outs?
You can create your own custom Authorize attribute, something like "AuthorizeAllExceptAdmin." Within that class you would simply need to check whether or not the current user was an admin, and if they were reject it, otherwise accept it.
Here's a good tutorial, but you'll probably end up with something like:
public class AuthorizeAllExceptAdmin : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return !httpContext.User.IsInRole(Constants.ROLES_ADMINISTRATOR);
}
}
Then your controller method becomes:
[AuthorizeAllExceptAdmin]
public ActionResult SomethingOnlyNonAdminsCanDo()
{
}
Here's an example of the custom attribute that takes in roles to deny.
public class DoNotAuthorize : AuthorizeAttribute
{
private IEnumerable<string> _rolesToReject;
public DoNotAuthorize(IEnumerable<string> rolesToReject)
{
_rolesToReject = rolesToReject;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
foreach (var role in _rolesToReject)
{
if (httpContext.User.IsInRole(role))
return false;
}
return true;
}
}
Then your controller method becomes:
[DoNotAuthorize(new [] {Constants.ROLES_ADMINISTRATOR})]
public ActionResult SomethingOnlyNonAdminsCanDo()
{
}
I would put some thought into it before choosing one of the above options. If you think you'll have several methods (or entire controllers) with similar authorization requirements (i.e, several actions an admin can not perform) then I would stick with the non-parameterized custom attribute. This way, you can evolve them all together (by only changing the custom attribute) later on. For example, maybe later on you want admins to be able to go into a special mode where they can perform these actions.
Alternatively, if the autorization is more varied amongst the actions, then using the parameterized list makes sense, since they'll evolve relatively independently.
Besides creating a custom AuthorizeAttribute, suggested by manu, you could use PrincipalPermission, with a Deny-SecurityAction:
[PrincipalPermission(SecurityAction.Deny, Role="Administrator")]
In my app I don't use roles so I have to query the database to determine whether the user has access or not. The benefits of the code below is that you can redirect the user to a certain action very easily. I explained the code in my blog post at http://blog.athe.la/2009/12/implementing-permission-via-windows-authentication-in-asp-mvc-using-action-filters/
public class DatabaseRepository()
{
private readonly DatabaseDataContext db = new DatabaseDataContext();
public bool UserHasPermission(string userLogon) {
return (from permission this.db.Permissions
where permission.HasPermissionSw == true
select permission).Contains(userLogon);
}
}
public class UserHasPermission: ActionFilterAttribute
{
private readonly DatabaseRepository databaseRepository = new DatabaseRepository();
private readonly string redirectAction;
public UserHasPermission(string redirectTo)
{
this.redirectAction = redirectTo;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string userLogon = filterContext.HttpContext.User.Identity.Name;
if (!this.databaseRepository.UserHasPermission(userLogon))
{
string routeController = filterContext.Controller.ControllerContext.RouteData.Values["controller"];
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = routeController, action = this.redirectAction }));
}
}
}
Your controller would then look something like this:
[UserHasPermission("NoAccess")]
public ActionResult SecretArea()
{
// run all the logic
return View();
}
public ActionResult NoAccess()
{
return View();
}

Handle security denied in ASP.NET MVC with AspNetSqlRoleProvider

I'm looking to secure different areas of my MVC application to prevent standard user's from accessing admin type views. Currently, if any user is logged in and they attempt to view the About page (out of the box template in visual studio), it will simply redirect them to the login page. I'd prefer the user is informed that they do not have permission to view the page.
[Authorize(Roles="Admin")]
public ActionResult About()
{
return View();
}
It seems redundant to send an already authenticated user to the login page when they don't have permission.
Here is an attribute that I've created that can be used to direct to an unauthorized security action. it also allows you to specify a Reason which will be passed to the Unauthorized action on the Security controller, which you can then use for the view.
You can create any number of properties to customize this to fit your particular application, just make sure to add it to the RouteValueDictionary.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public sealed class ApplySecurityAttribute : ActionFilterAttribute
{
private readonly Permission _permission;
public ApplySecurityAttribute(Permission permission)
: this(permission, string.Empty) {}
public ApplySecurityAttribute(Permission permission, string reason)
{
_permission = permission
Reason = reason;
}
public string Reason { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!PermissionsManager.HasPermission(_permission)) // Put security check here
{
var routeValueDictionary = new RouteValueDictionary
{
{ "controller", "Security" }, // Security Controller
{ "action", "Unauthorized" }, // Unauthorized Action
{ "reason", Reason } // Put the reason here
};
filterContext.Result = new RedirectToRouteResult(routeValueDictionary);
}
base.OnActionExecuting(filterContext);
}
}
Here is the security controller
public class SecurityController : Controller
{
public ViewResult Unauthorized(string reason)
{
var vm = new UnauthorizedViewModel { Reason = reason };
return View(vm);
}
}
Here is the attribute declaration on a controller you wish to secure
[ApplySecurity(Permission.CanNuke, Reason = "You are not authorized to nuke!")]
Here is how PermissionsManager does the check to see if the user has the permissions
public static class PermissionsManager
{
public static bool HasPermission(EZTracPermission permission)
{
return HttpContext.Current.GetCurrentUser().Can(permission);
}
}

Resources