ASP.NET MVC - Dynamic Authorization - asp.net-mvc

I am building a simple CMS in which roles are set dynamically in the admin panel. The existing way of authorizing a controller method, adding [Authorize(Roles="admin")] for example, is therefore no longer sufficient. The role-action relationship must be stored in the database, so that end users can easily give/take permissions to/from others in the admin panel. How can I implement this?

If you want to take control of the authorization process, you should subclass AuthorizeAttribute and override the AuthorizeCore method. Then simply decorate your controllers with your CmsAuthorizeAttribute instead of the default.
public class CmsAuthorizeAttribute : AuthorizeAttribute
{
public override virtual bool AuthorizeCore(HttpContextBase httpContext)
{
IPrincipal user = httpContext.User;
IIdentity identity = user.Identity;
if (!identity.IsAuthenticated) {
return false;
}
bool isAuthorized = true;
// TODO: perform custom authorization against the CMS
return isAuthorized;
}
}
The downside to this is that you won't have access to ctor-injected IoC, so you'll have to request any dependencies from the container directly.

That is exactly what the ASP.NET membership / profile stuff does for you. And it works with the Authorize attribute.
If you want to roll your own you could create a custom action filter that mimics the behavior of the standard Authorize action filter does. Pseudo code below.
public MyAuthorizeAttribute : ActionFilterAttribute
{
public string MyRole { get; set; }
public void OnActionExecuting(ControllerContext context)
{
if (!(bool)Session["userIsAuthenticated"])
{
throw new AuthenticationException("Must log in.");
}
if (!Session["userRoles"].Contains(MyRole))
{
throw new AuthenticationException("Must have role " + MyRole);
}
}
}

The role - action relationship must be
stored in the database
You will have to check your security within the controller method, unless you want to subclass AuthorizeAttribute so that it looks up the roles from the database for you.

Related

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);
}
}

How to add a overload to MVCs Authorize Attribute?

I have used [Authorize] attribute a lot in the past, and it allows you to also do things like this:
[Authorize(Users = "test")]
However, I would like to add another one,
[Authorize(IsPermitted= PermissionsEnum.ThePermission)]
I have the logic written out that would decide if the user was permitted for that permission, but I'm not sure how to add that overload to the authorize attribute.
I would prefer not to make a entirely separate authorize attribute if possible.
Well, as #Dave A has said on the comments, you can extend the native Authorize attribute and implement your own authorization method, for sample:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
// create your custom property
public PermissionsEnum IsPermitted { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool authorized = // create your own validation and return a bool value
if (authorized)
{
return false;
}
// if you want to have the nativa validation, call it from the base method
return base.AuthorizeCore(httpContext);
}
}
and remember to decorate your controllers/actions with your Custom Authorize Attribute, for sample:
[MyAuthorize(IsPermitted = PermissionsEnum.Sales)]
public class OrderController : Controller
{
// actions...
}
Unfortunately you can not. The only possible properties that you can set are
Users and Roles. So you have to make a separate Attribute class.

User Role Association per Domain Entity

I have the following structure in my DB:
DomainEntities:
+EntityID
+Name
+ParentID
+...
Users:
+UserID
+Username
+...
Roles:
+RoleID
+Name
UserRolesAssociation:
+RoleID
+UserID
+EntityID
So i want to use MVC's built in authorization attribute to filter action in my controllers that are made by different members.
I what to be able to say if user1 makes a delete action on entity1 or any entity under it i can see if he has the right role to do that and filter the action accordingly.
What would be the best practice to tackle that topic ?
Should i create my own permissions engine that will provide me the answers i need or can i use the existing capabilities ?
What would be the best practice to tackle that topic ?
A custom [Authorize] seems like a good place to implement this logic.
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var authorized = base.AuthorizeCore(httpContext);
if (!authorized)
{
// the use ris not authenticated or not authorized - no need to continue
return false;
}
string username = httpContext.User.Identity.Name;
// read the entity id that this user is attempting to manipulate
string entityId = (string)httpContext.Request.RequestContext.RouteData.Values["id"] ?? httpContext.Request["id"];
return IsAllowed(username, entityId);
}
private bool IsAllowed(string username, string entityId)
{
// You know what to do here - hit the database and check whether
// the current user is the owner of the entity
throw new NotImplementedException();
}
}
and then:
[HttpDelete]
[MyAuthorize]
public ActionResult Delete(int id)
{
...
}

Help with asp.net mvc authorization

Im using asp.net mvc built in authorize filter.
My only problem with it is that I dont want it to redirect my user to a login page when they dont have permission to perform a certain action... It always takes them to the login page even though ther are already logged on (but not with admin role).. I would like to be able to decide where to take them after they tried to perform an action ther arent allowed to..anyone?
Subclass AuthorizeAttribute and override the HandleAuthorizationFailed() method. The default logic of this method is that it sets the context's result to an HttpUnauthorizedResult, but you could do anything you want from this method. Then attribute the target method with this new attribute.
As Levi said you need to create your own custom AttributeFilter by overriding AthorizeAttribute. Something like
public class CustomAuthorizeAttribute : AuthorizeAttribute {
public string Url { get; set; }
public override void OnAuthorization(AuthorizationContext filterContext) {
if (!filterContext.HttpContext.User.Identity.IsAuthenticated) { //or custom authorization logic
filterContext.HttpContext.Response.Redirect(Url);
}
base.OnAuthorization(filterContext);
}
}
[CustomAuthorizeAttribute(Url="/Admin/AccessDenied")]
public ActionResult Admin() {
return View();
}
Taken from this similar question

Configure authorized roles dynamically via a config file in MVC Application

I current have the following attribute decorating one of the action method.
[Authorize(Roles = "Admin")]
public ActionResult DoAdminTask()
{
//Do something
return View();
}
Currently, only users in the Admin role can invoke this method, but this will change. Is there anyway I can store a list of authorised roles in a config file, rather than hard coding it into the source?
EDIT: Roles will change over time, and more than 1 role will need access.
i.e. Users in either role A OR role B can access.
No way to do this with the standard authorize attribute, but you could extend the authorize attribute with your own custom authorize attribute and have it use a configuration file to determine the mapping between controller/action and the set of roles.
but you can use something like
public static class AppRoles
{
public const string Users = "UsersRoleName";
public const string Admin = "AdminRoleName";
}
and then Controller can have authorize attribute as
[Authorize(Roles = AppRoles.Admin)]
I felt this question deserved an answer with a code sample... Taking #tvanfosson's suggestion of extending the AuthorizeAttribute class, here's what I came up with (criticism is more than welcome).
AuthorizeFromConfiguration.cs:
public class AuthorizeFromConfiguration: AuthorizeAttribute
{
public new string Roles
{
get {
return base.Roles;
}
set {
var config = new ConfigurationBuilder()
.SetBasePath(Environment.CurrentDirectory)
.AddJsonFile("authorization.json")
.Build();
base.Roles = config[value];
}
}
}
authorization.json:
{
"Parts": {
"Create": "contoso.com\\MyWebApp_CreateNewPart",
"Edit": "contoso.com\\MyWebApp_EditPart"
}
}
Example Usage:
[AuthorizeFromConfiguration(Roles = "Parts:Create")]
public class CreateModel : PageModel
{
//...
}
Note: In my testing, the web-site had to be restarted before any changes to authorization.json file took effect, even when I tried changing the logic so that the JSON file was read on the get accessor instead of the set.

Resources