1

In a public facing web app multi tenant app, I'm using ASP Identity 2.x.

I let 3rd party's e.g. non-profits/comps self-register, create and populate their own roles and users. The code below is fine, if its an intranet scenarios where everyone belongs to the same company, it does not for work multiple non-profits

The non-profits registrants (understandably so) are naming roles with the same names, i.e. Managers and Employees etc. which are common/same across the database.

How can I extend ASP Identity to separate the roles per organization in a multi-tenant fashion, can you help me understand the design and how extend this, do I need a sub-role? i.e.

  1. what do I do to ensure roles are scoped per organization at the database, so that different org's can have the same role names?
  2. and, what do I do at the middle tier, i.e. usermanager, role manager objects level (middle tier)

    //Roles/Create
    [HttpPost]
    public ActionResult Create(FormCollection form)
    {
        try
        {
            context.Roles.Add(new Microsoft.AspNet.Identity.EntityFramework.IdentityRole()
            {   \\ Question - can I add another level here like company??
                Name = form["RoleName"]
            });
            context.SaveChanges();
            ViewBag.ResultMessage = "Role created successfully";
            return RedirectToAction("RoleCreated");
        }
        catch
        {
            return View();
        }
    }`
    
  3. Question- When adding a role, how do I separate the role to know which Role is from Which company when I add to the user?

    public ActionResult RoleAddToUser(string UserName, string RoleName)
    {
        ApplicationUser user = context.Users.Where(u => u.UserName.Equals(UserName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
        var account = new AccountController();
        account.UserManager.AddToRole(user.Id, RoleName);
    
        ViewBag.ResultMessage = "Role created successfully !";
    
        // prepopulat roles for the view dropdown
        var list = context.Roles.OrderBy(r => r.Name).ToList().Select(rr => new SelectListItem { Value = rr.Name.ToString(), Text = rr.Name }).ToList();
        ViewBag.Roles = list;   
    
        return View("ManageUserRoles");
    }
    
  4. how do I get the list of Roles and Users for that non-profit?

    public ActionResult ManageUserRoles()
    {
        var list = context.Roles.OrderBy(r => r.Name).ToList().Select(rr => new SelectListItem { Value = rr.Name.ToString(), Text = rr.Name }).ToList();
        ViewBag.Roles = list;            
        return View();
    }`
    
trailmax
  • 34,305
  • 22
  • 140
  • 234
aggie
  • 798
  • 2
  • 8
  • 23

1 Answers1

1

I'm guessing you do have some sort of TenantId or CompanyId that is an identifier to the tenant you are working with.

IdentityRole and IdentityUser are framework objects that is recommended to inherit to add your own properties. You should do just that:

public class MyApplicationRole : IdentityRole
{
    public int CompanyId { get; set; }
}           


public class MyApplicationuser : IdentityUser
{
    public int CompanyId { get; set; }
}

You can also add reference to a company object here and link it as a foreign key. This might make your life easier retrieving company objects related to the user.

Based on the objects above I'll try answering your questions:

  1. When roles are created you can append a CompanyId to the name as a prefix. Something like <CompanyId>#CustomerServiceRole. This will avoid name clashes. At the same time add CompanyId to the MyApplicationRole class. Prefixed identifier will avoid clashes, identifier in the object will make the roles discoverable by the company. Alternatively you can implement RoleValidator and do the validation based on unique role name within a company. But then you will have to change the code that creates user identity when users are logged in, as role ids are not stored into the cookie.

  2. I think this is self explanatory from the previous answer:

    context.Roles.Add(new Microsoft.AspNet.Identity.EntityFramework.IdentityRole()
    {   
        CompanyId = companyId, // TODO Get the company Id
        Name = companyId.ToString() + "#" + form["RoleName"]
    });
    
  3. Self explanatory, see code snippets above.

  4. Depending how you link your roles to companies the query will be different. Basically you'll need to do a join between companies and matching roles based on CompanyId and then filter companies by the non-profit flag.

trailmax
  • 34,305
  • 22
  • 140
  • 234
  • now the question is when I do user/rolemanager, is it going to get everything in the DB or just the scoped company roles? how can I just get the roles in that company. – aggie Oct 29 '15 at 23:07