Extending SimpleMembership to support Multi-Tenancy - asp.net

I have a multi-tenant app that's using SimpleMembership. Each User in my system is linked to one or more Tenants. I've extended Users simply by adding the required fields to the User model (and Tenants):
public class User{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public virtual ICollection<Tenant> Tenants { get; set; }
}
This is working great. But now I'd like to have Roles be tenant-specific, where some role types MUST have a TenantId defined. This isn't as simple as the user problem, as each of the following will be affected:
Adding Roles, Checking Roles:
if (!Roles.Privider.RoleExists("Moderator")) // I now want to include TenantId here
{
roles.CreateRole("Moderator"); // and here
}
Assigning/Checking Roles Using Provider:
if (!Roles.IsUserInRole("Admin", "SystemAdministrator")) // and here
{
roles.AddUsersToRoles(new[] { "Admin" }, new[] { "SystemAdministrator" }); // and here
}
Role Attributes:
[System.Web.Http.Authorize(Roles = "SystemAdministrator")]
public class AdminApiController : BaseApiController
User.IsInRole:
if (User.IsInRole("Administrator"))
{
I'm pretty new to ASP.NET, so I'm not sure where to begin here. Should I be overriding the SimpleMembership Role Provider somehow, or should I look into writing my own Role columns, classes, etc? It would feel wrong to hand-code anything around authentication... Any pointers around this would be much appreciated.

The first problem I see with implementing this is the use of the AuthorizeAttribute because attributes require constant information defined at compile time. With your tenant-based approach I would think which tenant to check would need to be determined during run-time. So the first thing I would do is take the approach described in this article on Decoupling You Security Model From The Application Model With SimpleMembership. Now you decorate this attribute with a resource and operation (which will be static) and you can configure what roles are assigned to the resource/operation at run-time in the database. This gives you a lot more flexibility in designing your security model as you add tenants.
Changing the database model for anything but the UserProfile table in SimpleMembership is not possible (See this QA). So adding tenant ID's to roles is not possible without writing your own membership provider. If you want to stick with using SimpleMembership one solution is to handle this in your naming convention for roles, where you include the role name and tenant name or ID. For example, if you have two tenants that have the admin role you would have two roles that are named "Administrator_Tenant1" and "Administrator_Tenant2". If you need to display this to any users that assign roles you could clean up the name by stripping out the tenant ID/Name for viewing.
If this is a new project and you are not tied to SimpleMembership you may want to look at using at Microsoft's latest membership system called ASP.NET Identity. This membership system replaces SimpleMembership in MVC 5. ASP.NET Identity was built to be easily customized and you should be able to change the role model. There is an article on customizing ASP.NET Identity here.

Related

How to implement the Identity Roles Manager in MVC

I'm very new to MVC (as in this is my first project working in it, I apologize for any misguided or bad explanations) and I am not fully understanding how to make a user role. I know there are lots of these questions on here but none of them have been able to help my understanding of the role manager.
I am using ASP NET identity to make my users, edit them, the whole deal. I have all the Identity generated tables in my database with all their relationships. Now, I can make users but I have no real idea with how to get those users roles.
I figured that roles were just a part of the Account model/controller, like login and registration, where I just needed to alter a few lines of code to get it to work in my project with my own db. But I can barely find any code files that have a mention of roles. Anything I've seen through Google has just flown over my head (namely since that code first and seeding stuff is a little much for me to swallow as an admitted newbie and someone who added a database and used the code-first generator in visual studio)
Is there something in the Account or Identity files controller/model I need to alter or write in or is this like creating CRUD actions for my own tables where I make a controller based on my model and add in roles from there?
I am using Identity 2.0 with MVC 4.5. Thanks in advance for the help!
---- Edit ------
I've done a bunch more Googling and I think that when I made my project I wasn't given the code behind for RoleController or ApplicationRoleManager, etc. Is this normal or did I set something up wrong?
I managed to figure out my problem and with a lot of trying to convert c# into vb here we go!
I ended up seeding my roles into my database. In the startup.vb---in the root directory not the views directory---write the following:
Imports Owin
Imports Microsoft.Owin
Imports Microsoft.AspNet.Identity
Imports Microsoft.AspNet.Identity.EntityFramework
<Assembly: OwinStartupAttribute(GetType(Startup))>
Partial Public Class Startup
Public Sub Configuration(app As IAppBuilder)
ConfigureAuth(app)
createRoles()
End Sub
Private Sub createRoles()
Dim UserManager As New UserManager(Of ApplicationUser)(New UserStore(Of ApplicationUser)(New ApplicationDbContext))
Dim RoleManager As New RoleManager(Of IdentityRole)(New RoleStore(Of IdentityRole)(New ApplicationDbContext))
Dim role = New IdentityRole()
If Not RoleManager.RoleExists("Administration") Then
role.Name = "Administration"
RoleManager.Create(role)
UserManager.AddToRole("user id", "Administration")
End If
If Not RoleManager.RoleExists("User") Then
role.Name = "User"
RoleManager.Create(role)
UserManager.AddToRole("user Id", "User")
End If
End Sub
End Class
I added in a new sub procedure to my Startup class to create my roles (administration and user). In addition, the two imports of Identity and Identity.EntityFramework need to be added to the file for this to work.
In the createRoles Sub, we need to define a new UserManager (to add the roles to users) and RoleManager (to add roles to the db). Inside of the two Managers, the 'ApplicationDbContext' will be replaced with your DbContext (which is found in the IdentityModel in the Model folder) After that we need to add a new IdentityRole object, also so we can add a role.
Next, we check if a certain role exists 'administration', it doesn't exist so we add a name to the the role object and use the RoleManager to create it. Since I already have users in my database I can use the UserManager to add the user a role. I grabbed my user id from the database and wrote that in and then the name of the role.
I do this again for the other role I want to add 'User'. Build the project and volia! You can users with roles and now you can apply those roles to your page controller to have pages only accessed by 'administration' or 'user'.
I commented out the call to the function after I ran it so it wouldn't call it every build but since those roles exist it won't add anymore
You can apply those role to your pages in your controllers like so:
<Authorize(Roles:="Administration")>
Public Function yourPage() as ActionResult
Return View()
End Function
<Authorize(Roles:="Administration, User")>
Public Function yourPage2() as ActionResult
Return View()
End Function
I hope this can help someone else out in the future!

ASP .NET 5 MVC 6 Identity 3 Roles Claims Groups [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I'm currently looking for a solution to use an advanced Roles/Group Permission management in ASP .NET 5 MVC 6 with Identity 3. I started a new Preview Starter Web Project with a integrated easy login system.
Now I need a complex "users permission management" with following functions:
users can be in multiple groups/roles
a group/role have many access objects (e.g. CanAccessUser, CanEditUser...)
these access objects (maybe claims?) of each group/roles complement each other
(optional for the ultimate solution): additionally => access objects(maybe claims) can be assigned independently by a group to a user
I have seen that identity already broadly provides a fitting for me table structure . (e.g. AspNetUsers, AspNetUserRoles, AspNetRoles, AspNetRoleClaims),
But I'm missing a good example / documentation to use them.
For MVC 5, I used this example: Users have many groups, a group can have many roles (Roles are the Access Objects in source code for classes / functions)
ASP.NET Identity 2.0: Implementing Group-Based Permissions Management
Exists for these requirements already a working example that you do not have to reinvent the wheel.
We were in the same boat here, without much in terms of reading apart from the source of course...
We ended up implementing Policies. Policies being a group of Claims that are required for authorization to be satisfied. these Policies can then be applied to Controllers.
You can define your Policies in Startup.cs, ConfigureServices:
services.AddAuthorization(options =>
{
options.AddPolicy("SalesSenior", policy =>
{
policy.RequireClaim("department", "sales");
policy.RequireClaim("status", "senior");
});
});
We defined Roles, assigned 1 or more Claims to them and assigned Roles to Users allowing them to be checked against the appropriate Policy on hitting a Controller.
You can inject the IAuthorizationService into a Controller or Attribute as so:
public class SalesDashboardController: Controller
{
private readonly IAuthorizationService _authz;
public VarianceOverviewController(IAuthorizationService authz)
{
_authz = authz;
}
...
}
You can then use the IAuthorizationService to check the validity of a users claims...
if (await _authz.AuthorizeAsync(User, "SalesSenior"))
{
// User is authorized
}
This article was my main source for this stuff and was a great primer for me. Good luck!
If you are looking for a sample project there are not that many out there at the moment. The first place to look is on the aspnet GitHub project pages.
Luckily, the ASP.NET Identity sub-project has a sample project that you can take a look at here, however it may not cover all your requirements. Note that this is using the latest beta.
This thread helped me get something working, but it's sad that this is not better documented.
Here are my attempts at improving that. Asp.net.Identity (3.0.0.0-rc1-final)
in Startup.cs --> ConfigurationServices
//Define your policies here, they are strings associated with claims types, that have claim strings...
//they need to be in AspNetUserClaims table, user id, department, Dev to be allowed access to the Dev policy
//add the auth option, below that makes it work, and in the api controller, add the
//[Authorize("Dev")] attribute
services.AddAuthorization(
options =>
{
options.AddPolicy("Dev", policy => { policy.RequireClaim("department", "Dev"); });
});
services.AddMvc();

Extending current User class to handle Identity

We are using forms authentication on our ASP.NET website but are wanting to upgrade to the new Identity Provider. Currently we are using the database first approach and are ultimately wanting to just extend our current User table (not aspnet_users) to use the new identity format. We are using StructureMap to inject our context into our business logic classes. For instance our User service currently has this as its constructor:
private readonly SiteModelContainer _context;
public UserService(SiteModelContainer context)
{
this._context = context;
}
And in our IoC registry we have this:
var ecsbuilder = new EntityConnectionStringBuilder();
ecsbuilder.Provider = "System.Data.SqlClient";
ecsbuilder.ProviderConnectionString = #"data source=***;initial catalog=***;persist security info=True;User ID=***;Password=***;multipleactiveresultsets=True;App=EntityFramework";
ecsbuilder.Metadata = #"res://*/Data.***.csdl|res://*/Data.***.ssdl|res://*/Data.***.msl";
string connectionString = ecsbuilder.ToString();
For<SiteModelContainer>().Use<SiteModelContainer>().Ctor<string>("connectionString").Is(connectionString);
For<IUserService>().Use<UserService>();
...all the rest of our services
We are also using database first with EDMX and entity framework. Previously we just used ASP.NET authentication as it came out the box and had a separate user table to store profile information, but would like to have everything working off one users class instead.
1)Is it possible to extend our userservice to handle everything related to using Identity? So that Identity uses the same context that we inject into our classes? If so, I am unable to find any articles about it?
2) Are we able to extend our User object if it is created in the EDMX file?
Thanks
I have migrated 2 fairly large projects from MembershipProvider into Asp.Net Identity and both of the times I ended up rewriting most parts of the user-management and everything that touched user. A fairly chunky rewrites.
What you ask for is possible, but hard and very time consuming. You may start from this question - the OP have got his db-first project running with identity. And we had a discussion in comments with some links that might help you.

How to use Membership provider with EF Code First?

I have models based on EF Code First and I want to use them with the default MembershipProvider, but I don't know how to write the model correctly, so it won't erase all my data on recreating the tables when there were changes made to the model.
Have a look at this project
http://codefirstmembership.codeplex.com/
It has entity classes for users and roles, as well as a roleprovider and membershipprovider implementation. If you include the users and roles in your datacontext class the tables will be created in your database.
Your question has two parts.
How to use asp.net membership API with EF code first?
How to preserve existing data when model changes?
as for How to preserve existing data when model changes, as far as with EF 4.0/ asp.net mvc 3, database migrations are not yet supported. You will have to move to asp.net mvc 4.0/ EF 4.3 where database migrations are supported or use similar alternatives , but its still beta release.
asp.net mvc 4.0 database migration in scott gu's blog
Now coming to the point on how to use asp.net membership provider with EF code first. There are couple of challenges :
We cannot/should not do an join with asp.net membership provider tables. Its not recommended, so my suggestion will be to create a "adapter class" for asp.net membership provider classes. For ex :
public class UserAdapter
{
// all user related attributes. Not stored in membership schema, but your schema
public string UserProxyName;
// some attributes stored in membership schema
[NotMapped]
public string Email {
get
{
Membership.GetUser(UserProxyName).Email;
}
}
// some attributes stored in membership schema and not in your schema
[NotMapped]
public string[] UserRoles
{
get
{
return Roles.GetRolesForUser(UserProxyName);
}
}
}
Now for updating information , you may write some functions in Model itself, however i would suggest create a UserRepository with repository design pattern to handle user CRUD operations.
Second challenge is how to create the database on first run. As seeding becomes an issue, if you want to seed user information then seperately running aspnet_regsql is not efficient as membership schema is expected before the seeding happens. I came across this nice article , with some fine tuning it worked for me :
asp.net membership and EF
Currently (EF 4.1 CTP) EF Code First doesn't have that option. It always drops a table if you made changes to model.
Update:
EF 4.1 RTM allows you to create a custom database initializer and specify creation of db objects and data seeding.
If you are using SQL Server, then check this :
http://www.paragm.com/ef-v4-1-code-first-and-asp-net-membership-service/
There is also my library which basically allows you to define how almost everything should be configured including: key type, where the your context object is, and where your user/role entities are located. Extendable using abstract base classes or interfaces. Also works quite well out of the box with repository pattern / unit of work / IoC Containers.
Source: https://github.com/holyprin/holyprin.web.security
NuGet: https://nuget.org/packages/Holyprin.Web.Security
In my DbContext.cs file I have a Seed function where I call the ApplicationServices.InstallServices() to install the ASP.NET Membership to my database. Now everytime my initializer drop the database it recreates ASP.NET Membership schema again.
public class PanelInitializer : DropCreateDatabaseAlways<PanelContext>
{
protected override void Seed(PanelContext context)
{
//Install ASP.NET Membership
ApplicationServices.InstallServices(SqlFeatures.Membership | SqlFeatures.RoleManager);
new List<Panel>
{
The ApplicationServices class
using System.Configuration;
using System.Data.SqlClient;
using System.Web.Management;
namespace Lansw.Panels.DataAccess.Contexts
{
public class ApplicationServices
{
readonly static string DefaultConnectionString = ConfigurationManager.AppSettings["DefaultConnectionString"];
readonly static string ConnectionString = ConfigurationManager.ConnectionStrings[DefaultConnectionString].ConnectionString;
readonly static SqlConnectionStringBuilder MyBuilder = new SqlConnectionStringBuilder(ConnectionString);
public static void InstallServices(SqlFeatures sqlFeatures)
{
SqlServices.Install(MyBuilder.InitialCatalog, sqlFeatures, ConnectionString);
}
public static void UninstallServices(SqlFeatures sqlFeatures)
{
SqlServices.Uninstall(MyBuilder.InitialCatalog, sqlFeatures, ConnectionString);
}
}
}
Thanks to #ImarSpaanjaars http://imar.spaanjaars.com/563/using-entity-framework-code-first-and-aspnet-membership-together.

Multi-Tenant in windows azure asp.net MVC2

Anyone knows how can i start to develop a multitenant site in MVC2, in a way it run on Windows Azure?
I search a lot about this question, and i allways find theoric explanations, everybody says it can be easily done, but i dont find any sample...
Can someone explain me where to start?
Thanks,
João
It depends on how you plan on implementing multitenancy (eg. using authorization with common urls, subdomains, custom domains, or any combination). But you should be able to do just about any approach with Azure and MVC2. If you plan on using a custom domain for each tenant, versus a subdomain, you will need to be happy with using CNAME entries (not A records) to point each custom domain to Azure but that usually is not a problem.
MVC offers many extension points where you can implement multitenancy in its various flavors. The main goal is to uniquely identify the user by either a login or the url.
We have an MVC2 application running in Azure that parses the request url to differentiate the tenant. There are many ways to do this. We took the approach of extending the Controller class to provide our app with the unique tenant information so we could use it as needed to make appropriate repository calls to display the proper views etc.
Here is a sample of what a MultiTenant Controller might look like:
public class MultiTenantController : Controller {
public string TenantCode { get; set; }
protected override void OnActionExecuting(ActionExecutingContext filterContext) {
TenantCode = GetTenantCode(filterContext.HttpContext.Request);
}
private string GetTenantCode(System.Web.HttpRequestBase request) {
string host = new RequestParser(request.Url.AbsoluteUri).Host;
return _tenantService.GetTenantCodeByHostAddress(host);
}
}
NOTES:
The RequestParser function
above is just any implementation
that knows how to parse urls in a
safe manner.
_tenantService
can access some kind of persistent
store (Azure Tables in our case) to
get the TenantCode from the host
address in the url.
All of your controllers would inherit from the above class. Then, to differentiate between tenants you just refer to the TenantCode within your controller like so:
public class HomeController : MultiTenantController {
...
public ViewResult Index() {
var vm = _homeService.GetHomePageViewModelForTenant(TenantCode);
return View(vm);
}
}
Using the above implementation you could serve different sites or data to urls like the following:
http://subtenant1.yourdomain.com
http://subtenant2.yourdomain.com
http://www.customtenantdomain.com
Your backend store (eg. Table Storage) just needs to cross reference host names with the tenant like the table below. In the code above GetTenantCode would access the data.
HostName TenantCode
---------------------- --------------
subtenant1 Tenant1ID
subtenant2 Tenant2ID
www.customtenantdomain Tenant3ID
For www.customtenantdomain.com to work, the tenant needs a CNAME entry for www in their DNS records for customtenantdomain.com that points to your Azure Web Role's address.
Its hugely complex and not something to be taken on lightly. However take a look at the source code for Microsoft's Orchard project. This has full multi-tenancy capabilities if thats what you need: http://orchard.codeplex.com/
And they have a build that works in Azure too.
In this guide we cover aspects of this and it includes a full sample using MVC 2.
link text
First , all answers are very very helpful.It's changing your decision what you want setting up your multitenancy.I mean the most important thing is Identifying all tenant in your app so there is a lot of way for solution.For example you can hold your tenant via subdomains or URL surfing.And also maybe you can store your data multitenat database.
There are very very helpul posts are written by Steve Morgan.
I only help you for set startup multi- tenancy.Here are the blogs :
Identifying the Tenant in Multi-Tenant Azure Applications - Part 1
Identifying the Tenant in Multi-Tenant Azure Applications - Part 2
Identifying the Tenant in Multi-Tenant Azure Applications - Part 3
And here are the Multi-Tenant Data Strategies for Windows Azure :
Multi-Tenant Data Strategies for Windows Azure – Part 1
Multi-Tenant Data Strategies for Windows Azure – Part 2

Resources