Role/Permission based forms authorizing/authentication? - asp.net

While looking into forms authorizing/authentication, I found that it is possible to do role based authorizing by adding an array of roles to a FormsAuthenticationTicket. That way I can write
User.IsInRole(role from database)
But is there any way to do the same thing with permissions on a role like :
if (User.IsInRole(role from database)) {
if (User.CanRead()) {
//--- Let the user read
}
if (User.CanWrite()) {
//--- Let the user write
}
}
I have read a couple of articles and forum post's where permission is added to the array instead of the roles, resulting in using
User.IsInRole(permission from database)
However that's not the same thing. Hope someone can give some input on this matter, throw a link to an article or better yet, an code sample.

You're better off changing the way you think about a role. Use the term "permission" or "claim" if that helps. Then create all the roles you need and link a given user to all the necessary roles.
One user can belong to multiple roles. This way, the following simple code will work fine and you don't need to build your own unique way of how things work.
if(User.IsInRole(someRole) && User.IsInRole(someOtherRole))
{
// do something
}
You can make some C# extension methods to make this more readable too:
if(User.IsInSomeRoleAndOtherRole())
{
// do something
}
The extension methods can look something like the following. Create a new class with the following code, then include the class namespace in your code, and you can use the extension method as shown above.
using System.Security.Principal;
namespace MyCompany
{
public static class MyExtensions
{
public static bool IsInSomeRoleAndOtherRole(this IPrincipal principal)
{
if (!principal.IsInRole("someRole"))
return false;
if (!principal.IsInRole("someOtherRole"))
return false;
return true; // the user meets the requirements
}
}
}

Related

Is there a better way to implement role based access in ASP.NET framework?

Basically I've spent the last few days trying to figure out how to add simple Admin and Member roles onto a website I'm developing for a friend. (I am using ASP.NET Framework 5.2.7.0). I know that Microsoft has a nice role based access feature built in which allows you to put something like [Authorize Role=("Admin") at the top of the controller; however I have not been able to get it to work at all and most of the resources I've found are for ASP.NET Core.
I've tried modifying my web.config file to enable the role based access (and hopefully migrate the roles and such to my database). But since I've been unable to figure any of this out, I've tried going a more hacky route. (**I am not an advanced programmer, I've been doing this for about a year now and am in no way a pro). This is what I've basically come up with in my attempt to verify if a user is an admin (which also didn't work).
[Authorize]
public class AdminController : Controller
{
private LDSXpressContext db = new LDSXpressContext();
public ActionResult AdminPortal()
{
IsAdmin();
return View();
}
private ActionResult IsAdmin()
{
string name = User.Identity.Name;
//The User.Identity.Name stores the user email when logged in
var currentUserObject = db.accounts.Where(x => x.clientEmail == name);
Account currentUser = new Account();
foreach (var user in currentUserObject)
{
//I loop through the results, even though only one user should
//be stored in the var CurrentUserObject because it's the only
//way I know how to assign it to an object and get its values.
currentUser = user;
}
if (currentUser.role == 2) //the number 2 indicates admin in my db
{
return null;
}
else
{
//Even when this is hit, it just goes back and returns the
//AdminPortal view
return RedirectToAction("Index", "Home");
}
}
}
Now I'm nearly positive that is is NOT a very secure way to check if a signed in user is an admin, but I was hoping that it would at least work. My idea was when someone attempted to access the AdminPortal, the IsAdmin method would run and check if the user is an admin in the database. If they are, then it returns null and the AdminPortal view is displayed, if they are not an Admin, then they are redirected to the Index view on the home page. However, the AdminPortal page is always displayed to any user and this doesn't seem to work either. I've even stepped into the code and watched it run over the return RedirectToAction("Index", "Home"); action, but then it jumps back to the AdminPortal method and just returns the AdminPortal view. So my question is:
1) If anyone happens to have experience with Role Based access in ASP.NET Framework, I would love some tips on how to get it set up
or,
2) If all else fails and I need to use my hacky method, why does it continue to return the AdminView even when the user is not an admin.
**Note: I know I could create a function that returns true or false if the user is an Admin or not, and then have an if/else statement in the AdminPortal controller that will return on view for true and another for false, however I don't want to have to implement that onto every ActionMethod, it'd be nice to keep it down to one line, or just the [Authorize Role="Admin] above the controller if possible.
Thank you guys so much for any help provided, I've been trying to research and fix this for days now and decided to reach out and ask the community!
At a minimum, you'll want to make some adjustments to what you're doing:
[Authorize]
public class AdminController : Controller
{
public ActionResult AdminPortal()
{
if(IsAdmin())
{
return View();
}
return RedirectToAction("Index", "Home");
}
private bool IsAdmin()
{
bool isAdmin = false;
using(LDSXpressContext db = new LDSXpressContext())
{
string name = User.Identity.Name;
//The User.Identity.Name stores the user email when logged in
// #see https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.singleordefault
var currentUser = db.accounts.SingleOrDefault(x => x.clientEmail.Equals(name, StringComparison.OrdinalIgnoreCase));
// If the email doesn't match a user, currentUser will be null
if (currentUser != null)
{
//the number 2 indicates admin in my db
isAdmin = currentUser.role == 2;
}
}
return isAdmin;
}
}
First off, DbContext instances are meant to used, at most, per the lifetime of an HTTP request. Moving it from the class / controller level and placing it within a using block makes sure that it's properly disposed.
Next, your IsAdmin function really just needs to return a true/false value based on your lookup, and then the AdminPortal action can decide what to do with that result.
Since email seems to be a unique field in your table, use the SingleOrDefault or FirstOrDefault LINQ extension to fetch a single matching record. Which one you use is up to you, but if it's truly a unique value, SingleOrDefault makes more sense (it will throw an exception if more than one row matches). Using the StringComparison flag with the String.Equals extension method makes your search case-insensitive. There are a few culture-specific versions of that, but ordinal matching is what I would normally use, here.
Implementing some version of the Identity framework is a bit too long for an answer here, but it's possible to implement a claims-based authentication scheme without too much work. That's something that probably needs a separate answer, though.

ASP.NET MVC How does AuthorizeAttribute support checking Roles?

In my controllers, I have code like [Authorize(Roles = "Administrators")] annotated above some actions, and I want to know how AuthorizeAttribute uses the Roles parameter (the implementation of the checking mechanism). My goal is to create an extension of this class, called PrivilegeAttribute for example, so that I can annotate actions like [Privilege(Privileges = "read")]. In this class, I would check if the Role of the user has at least one of the privileges in this custom filter (read in this example). I have already created the association between roles and privileges in the code and in the database, and what I want help with is checking whether the role is associated to the privilege.
I tried seeing if that information is there in HttpContextBase.User.Identity but I couldn't find it.
Thank you.
If you don't need your own custom attribute and could live with using someone else attribute, than I would suggest to use the package Thinktecture.IdentityModel.Owin.ResourceAuthorization.Mvc as described here
Blog Post by Dominick Baier
and here
Git Hub Sample Code for the Package
so it basically works like this:
you put an attribute over your action like this:
[ResourceAuthorize("View", "Customer")]
The first argument is the name of the Action to check, the second one is the name of the attribute.
Then you derive from ResourceAuthorizationManager in your code and override the CheckAccessAssync Method
public class MyAuthorization : ResourceAuthorizationManager
{
public override Task<bool> CheckAccessAsync(ResourceAuthorizationContext context)
{
var resource = context.Resource.First().Value;
var action = context.Action.First().Value;
// getting the roles that are connected to that resource and action
// from the db. Context could of course be injected into the
// constructor of the class. In my code I assume that the table
// thank links roles, resources and actions is called Roles ToActions
using(var db = MyContext())
var roles = db.RolesToActions // Use your table name here
.Where(r => r.Resource == resource && r.Action == action).ToList();
foreach(var role in roles)
{
if(context.Principal.IsInRole(role.Name)
{
return Ok();
}
}
return Nok();
}
}
}
So I hope this helps. If you prefer to implement your own attribute however, than the source code from the ResourceAuthorization GitHub Repository should be a good starting point

Display authenticated and anonymous content in a view

I'm newbie in web development.
Consider site that user can be logged in (e.f Facebook log-in)
Assuming I know if a user is logged in or not (I'm on my way to find how ;) - is it possible that on the same view (.cshtml) - part of the elements will be hidden if user anonymous or will be revealed if user is logged-in? you know - something like nice attributes or conditions (in short - to put the logic on the same view and not to manage two .cshtml)
I'm personally not a fan of having if statements in my view's as they can easily begin to get cluttered, especially if you are using roles.
This is the way I prefer to do it to avoid that.
Create an html helper containing the logic like this abstracted away:
namespace System.Web.Mvc
{
public static class HtmlHelperExtensions
{
public static MvcHtmlString UserMessage(this HtmlHelper htmlHelper)
{
string welcomeFormat = "Welcome, {0}";
var isAuthenticated = htmlHelper.ViewContext.HttpContext.User.Identity.IsAuthenticated;
var name = htmlHelper.ViewContext.HttpContext.User.Identity.Name;
var message = isAuthenticated ? string.Format(welcomeFormat, name) : string.Format(welcomeFormat, "anonymous!");
return new MvcHtmlString(message);
}
}
}
Call this method within my view like this:
<p>#Html.UserMessage()</p>
If you are new to web development the code for the Helper extension might look a bit overwhelming but you only end up writing that once and the code you use to call it elsewhere is a lot simpler and re-usable.
Here is an article about Html helpers for more info:
http://www.codeproject.com/Articles/649394/ASP-NET-MVC-Custom-HTML-Helpers-Csharp
Update
Forgot to mention this technique too, which again avoids the if statements.
Create two partial views say _auth.cshtml & _unauth.cshtml.
Create an action that checks if the user is authenticated & returns the relevant partial i.e
public ActionResult FooContent()
{
if (User.Identity.IsAuthenticated)
{
return PartialView("_auth");
}
else
{
return PartialView("_unauth");
}
}
Then call the action from within your view like this:
#Url.Action("FooContent", "Contoller");
This can also be used to check roles and return different partials.
Yes, views can have logic.
Here's an example of code that displays different content to the user depending on if they are logged in or not.
#if (User.Identity.IsAuthenticated)
{
<p>Welcome, #User.Identity.Name!</p>
}
else
{
<p>Welcome, anonymous!</p>
}

Using ASP.net membership to get aspnet_Users in silverlight

Hope somebody can help.
Have looked around on the net but cannot seem to solve (or understand) this.
I have tried the code posted at
http://blogs.msdn.com/b/kylemc/archive/2010/05/10/using-asp-net-membership-in-silverlight.aspx
(not going to repeat the class MembershipServiceUser here as it is quite long and can be seen on the mentioned page)
I have set up the domain service with the class and the code to return the users:
//[RequiresRole("Managers")]
public IEnumerable<MembershipServiceUser> GetAllUsers()
{
return Membership.GetAllUsers().Cast<MembershipUser>().Select(u => new MembershipServiceUser(u));
}
I took out the RequiresRole for testing.
What I seem to be a bit blonde about is the calling of the GetAllUsers() method.
In my code behind I am using:
MembershipDataContext context = new MembershipDataContext();
EntityQuery<MembershipServiceUser> users = context.GetAllUsersQuery();
I am not 100% sure if this is the correct way to use the method or if something else is wrong because
context.GetAllUsersQuery(); returns "Enumeration yielded no results"
One question is also in the code kylmc uses //RequiresRole("Admin")]. Is this a custom role created in the ASP.NET Configuration editor?
Looking at another tutorial regarding using the ASP.NET authentication service in Silverlight, I create a role called "Managers" and added the login user to that role.
Logging in using a user with role Managers doesn't help and results are still not yielded.
Any ideas I could possible look at?
Many thanks
Neill
There are two steps involved with querying.
Get a query object from the Domain Service context (synchronous).
Load the query from the Domain Service context (asynchronous).
Example:
public void Load()
{
// define the query
var query = context.GetAllUsersQuery();
// start running the query, and when the results return call
// OnGetAllUsersLoaded
context.Load(query, OnGetAllUsersLoaded, null);
}
public void OnGetAllUsersLoaded(LoadOperation op)
{
var results = op.Entities;
}

ASP.NET MVC3 Role and Permission Management -> With Runtime Permission Assignment

ASP.NET MVC allows users the ability to assign permissions to functionality (i.e. Actions) at Design Time like so.
[Authorize(Roles = "Administrator,ContentEditor")]
public ActionResult Foo()
{
return View();
}
To actually check the permission, one might use the following statement in a (Razor) view:
#if (User.IsInRole("ContentEditor"))
{
<div>This will be visible only to users in the ContentEditor role.</div>
}
The problem with this approach is that all permissions must be set up and assigned as attributes at design time. (Attributes are compiled in with the DLL so I am presently aware of no mechanism to apply attributes (to allow additional permissions) such as [Authorize(Roles = "Administrator,ContentEditor")] at runtime.
In our use case, the client needs to be able to change what users have what permissions after deployment.
For example, the client may wish to allow a user in the ContentEditor role to edit some content of a particular type. Perhaps a user was not allowed to edit lookup table values, but now the client wants to allow this without granting the user all the permissions in the next higher role. Instead, the client simply wants to modify the permissions available to the user's current role.
What options are strategies are available to allow permissions on MVC Controllers/Views/Actions to be defined outside of attributes (as in a database) and evaluated and applied at runtime?
If possible, we would very much like to stick as closely as we can to the ASP.NET Membership and Role Provider functionality so that we can continue to leverage the other benefits it provides.
Thank you in advance for any ideas or insights.
What options are strategies are available to allow permissions on MVC
Controllers/Views/Actions to be defined outside of attributes (as in a
database) and evaluated and applied at runtime?
A custom Authorize attribute is one possibility to achieve this:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
Roles = ... go ahead and fetch those roles dynamically from wherever they are stored
return base.AuthorizeCore(httpContext);
}
}
and then:
[MyAuthorize]
public ActionResult Foo()
{
return View();
}
As I'm lazy I couldn't be bothered rolling my own attribute and used FluentSecurity for this. In addition to the ability to apply rules at run time it allows a custom way to check role membership. In my case I have a configuration file setting for each role, and then I implement something like the following;
// Map application roles to configuration settings
private static readonly Dictionary<ApplicationRole, string>
RoleToConfigurationMapper = new Dictionary<ApplicationRole, string>
{
{ ApplicationRole.ExceptionLogViewer, "ExceptionLogViewerGroups" }
};
the application roles are then applied like so
SecurityConfigurator.Configure(
configuration =>
{
configuration.GetAuthenticationStatusFrom(() =>
HttpContext.Current.User.Identity.IsAuthenticated);
configuration.GetRolesFrom(() =>
GetApplicationRolesForPrincipal(HttpContext.Current.User));
configuration.ForAllControllers().DenyAnonymousAccess();
configuration.For<Areas.Administration.Controllers.LogViewerController>()
.RequireRole(ApplicationRole.ExceptionLogViewer);
});
filters.Add(new HandleSecurityAttribute());
and then the check is performed by
public static object[] GetApplicationRolesForPrincipal(IPrincipal principal)
{
if (principal == null)
{
return new object[0];
}
List<object> roles = new List<object>();
foreach (KeyValuePair<ApplicationRole, string> configurationMap in
RoleToConfigurationMapper)
{
string mappedRoles = (string)Properties.Settings.Default[configurationMap.Value];
if (string.IsNullOrEmpty(mappedRoles))
{
continue;
}
string[] individualRoles = mappedRoles.Split(',');
foreach (string indvidualRole in individualRoles)
{
if (!roles.Contains(configurationMap.Key) && principal.IsInRole(indvidualRole))
{
roles.Add(configurationMap.Key);
if (!roles.Contains(ApplicationRole.AnyAdministrationFunction))
{
roles.Add(ApplicationRole.AnyAdministrationFunction);
}
}
}
}
return roles.ToArray();
}
You could of course pull roles from a database. The nice thing about this is that I can apply different rules during development, plus someone has already done the hard work for me!
You could also consider doing task/activity based security and dynamically assign permission to perform those tasks to different groups
http://lostechies.com/derickbailey/2011/05/24/dont-do-role-based-authorization-checks-do-activity-based-checks/
You would need to mangle the provider a little bit to work with this but it is possible to stay inline with the .net authorisation
http://www.lhotka.net/weblog/PermissionbasedAuthorizationVsRolebasedAuthorization.aspx
If you need to do Method or Controller based authorization (deny access to the whole method or controller) then you can override OnAuthorization in the controller base and do your ouwn authorization. You can then build a table to lookup what permissions are assigned to that controller/method and go from there.
You can also do a custom global filter, which is very similar.
Another option, using your second approach, is to say something like this:
#if (User.IsInRole(Model.MethodRoles))
{
<div>This will be visible only to users in the ContentEditor role.</div>
}
And then in your controller populate MethodRoles with the roles assigned to that method.

Resources