MVC roles with custom properties - c#

I'm looking into how to implement authorization in MVC 4 (.NET 4.5), and have specifically been reading about SimpleMembership. Is there any typical or accepted way in MVC to have roles that have additional properties aside from a name?
For example, suppose you were designing a CMS, and wanted to be able to have a role called something like Writer that let a user make modifications. However, you also want the role to be restrictive to a single page. The only way that I know of to do that would be to have a separate role for each page, where each role might be named something like Writer_<PageID>. Is there any pattern that's nicer than this, or is that pretty much all we can do?
Ideally, I'm wondering if there'd be some way to be able to have something remotely like:
public ActionResult EditPage(Page page) {
WriterRole role = new WriterRole(page);
if (!User.IsInRole(role)) {
return NotAuthorized();
}
// Edit...
}
Instead of:
public ActionResult EditPage(Page page) {
string role = "Writer_" + page.Id;
if (!User.IsInRole(role)) {
return NotAuthorized();
}
// Edit...
}

What I would do is have one Writer role then check the UserId to see if the person owns the editable resource.
[Authorize(Roles = "Writer")]
public ActionResult EditPage(Page page) {
if (User.UserId == page.UserId) { ... }
}

Related

Optimize Roles authorization in ASP.NET MVC

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

ASP.NET Identity - extending the _LoginPartial

I'm finding this difficult to summarise, hence the poor question name. On my .net web application, which is using Identity, there is a login partial that was auto generated that displays the users 'username' property like so:
#Html.ActionLink(User.Identity.GetUserName(), "Index", "Manage", routeValues: null, htmlAttributes: new {title = "Manage"})
After profiling the SQL database that is holding the user data, I noticed that this call to GetUserName() doesn't actually make a call to the database to retrieve the name. I am looking to access a navigation property of the 'ApplicationUser' class that holds the url for a thumbnail image I want to be displayed.
However I am hoping I can achieve this without the need to call the database for the URL on every page request. An example of this would be on this site, where your profile image is displayed on every page as part of the layout.
The application user class has this property.
public class ApplicationUser : IdentityUser
{
public UserProfile Profile { get; set; }
}
And the user profile class holds this property.
public class UserProfile
{
[Key, ForeignKey("User")]
public string UserId { get; set; }
public ApplicationUser User { get; set; }
[Required]
public string ThumbnailUrl { get; set; }
...
}
How can I do this? Or is there a much better way of trying to achieve this.
You can just add them as claims.
When authenticating you should fetch the User from the DB and add all the properties you would need use later on.
Assuming that you are using CreateIdentity when logging in a user, that should return a ClaimsIdentity object.
var userIdentity = await userManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
userIdentity.AddClaim(new Claim("Thumbnail", user.ThumbnailUrl));
Then you need to create a extension class, let's say IdentityExtensions:
public static class IdentityExtensions{
public static string Thumbnail(this IIdentity identity){
try{
return ((ClaimsIdentity) identity).FindFirst("Thumbnail").Value;
}
catch(Exception ex){
// handle any exception the way you need
}
}
}
Finally, on your views you should be able to use #User.Identity.Thumbnail()
You're right it doesn't make a call... the reason is because it's holding that data in the client's cookies... If it didn't the server would have a meltdown if it had to keep running back and forth to grab such trivial data for large scale systems... so it's cached.
The way it's done in ASP.NET MVC is via the use of the IPrinciple interface... I'll point you in the right direction to learn more about it...
CodeProject: How to implement a custom IPrincipal in MVC
Best thing todo is to create a second table (or add a column to the current table), add a file upload feature to the registration form. When registration is successful add the picture to the db with EntityFramework. Create a page to return the picture by the userid so you can include it in a page somewhere.
you can use this idea
http://blogs.msdn.com/b/webdev/archive/2013/10/16/customizing-profile-information-in-asp-net-identity-in-vs-2013-templates.aspx

Create class to return Session User Information

Hello :) I am building an MVC5/EF6 system that has stores information about students with a number of user types . When the user logs in certain information about the user is stored in Session; UserID, UserTypeID etc. Different users have different privileges, and I often need to get the user information from Session within my ActionResult methods in each controller:
private Student GetCurrentStudentInfo()
{
var currentuser = (SessionModel)Session["LoggedInUser"];
var student = _db.Student.Find(currentuser.UserID);
return student;
}
I have this method at the bottom of my controllers, which I call from each method depending on when I need this information. It gets the userID from the current logged in user and returns the profile information. I would like to be able to either:
Make this method available to all my controllers
Or create a class variable that I can use at the top of my controller, which would return this info:
public class RegistrationWizardController : Controller
{
private readonly DefaultContext _db = new DefaultContext();
private UserInfo _userInfo = new UserInfo();
}
I am very new to MVC and coding in general, so any help/opinions/other suggestions are welcome. Thanks!
You have a couple of options.
The first (and easier) of the two is to make all of your controllers inherit from a common base controller. To do this, make a base controller that extends from the default controller:
public class BaseController : Controller
{
protected Student GetCurrentStudentInfo() //protected so we can access this method from derived classes
{
var currentuser = (SessionModel)Session["LoggedInUser"];
var student = _db.Student.Find(currentuser.UserID);
return student;
}
}
Now, change your controllers to inherit the base controller you just created:
public class RegistrationWizardController : BaseController
{
public ActionResult AnAction()
{
var student = this.GetCurrentStudentInfo(); //calls the method inherited from BaseController
}
}
The other option you have is to use Dependency Injection. This is a bit more complicated, and much more abstract than the previous method. There are a bunch of Dependency Injection frameworks, my favorite is Ninject (http://www.ninject.org/). This would probably be closer to the "Industry Standard" of doing something like this, and I would encourage you to at least look into it, but I think a sample would be a little out of scope for this question (do some side reading first). If you do decide to implement it and get stuck, post another question.

MVC 3 dynamic authorization of multiple roles and users

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

application roles with hierarchical organization roles

our business has a number of sites that we manage and each of these sites have sites they are responsible for and so on. So everything is hierarchical as far as permissions go for our software too. If a person at site-X wants to edit stuff for site-X and any sub-site-X they should be allowed to. We also have applications roles, mainly admin, that will allow a person to edit everything as well as maintain the application.
I'm currently working on dealing with permissions for this application and I've got everything working, but I really hate it. Its clunky, not very testable and doesn't seem like its in the right place for my MVC application. I was hoping someone would have some thoughts on how I can refactor this code and make it most importantly more testable and perhaps make it a bit more usable.
Thank you in advance.
public class OuController : BaseController {
private readonly IOrganizationUnitRepository repo;
public OUController(IOrganizationUnitRepository repo) {
this.repo = repo;
}
public ActionResult Details(string site) {
//Get the site we are viewing
var ou = repo.GetOuByName(site);
//make sure the site really exists
if (ou != null) {
//Get all the roles for the current user via the role provider
//will return the sites they are able to manage along with
//any application roles they have
var roles = ((RolePrincipal)User).GetRoles().ToList();
//Get all the parents of the current ou, this will include itself
var parents = repo.GetParents(ou, new List<OU>());
//create a new viewmodel object
//ou is used for details obviously
//parents are used for a breadcrumb
var model = new OrganizationalViewModel(ou, parents);
//if a user has no roles, there is no way he can possibly edit
if (roles.Any()) {
if(roles.Contains(InfoRoles.Administrator.ToString())) {
model.CanEdit = true;
} else if(parents == null) {
//If there are no parents, check if this ou is in users list of roles
model.CanEdit = roles.Contains(ou.DisplayName);
} else {
//check to see if any of the roles i have are parents of the current ou
model.CanEdit = parents.Any(c => roles.Contains(c.DisplayName));
}
}
return View("Details", model);
}
return View("NotFound");
}
}
}
Anything that looks like this:
((RolePrincipal)User).GetRoles().ToList()
... belongs in a class of its own (with an interface method like "GetCurrentRoles"), so it can be easily mocked.
Furthermore, this:
//if a user has no roles, there is no way he can possibly edit
if (roles.Any()) {
if(roles.Contains(InfoRoles.Administrator.ToString())) {
return true;
} else if(parents == null) {
//If there are no parents, check if this ou is in users list of roles
return roles.Contains(ou.DisplayName);
} else {
//check to see if any of the roles i have are parents of the current ou
return parents.Any(c => roles.Contains(c.DisplayName));
}
... belongs in a utility class in a method called something like CanRolesEditOrganizationalView(IEnumerable<RolePrinciple> roles, ...). That way your controller can just say:
var roles = _sessionManager.GetCurrentRoles();
...
model.Edit = _orgViewRightsUtil.CanRolesEditOrganizationalView(roles, ...);

Categories