We are developing an ASP.NET MVC Application that currently uses it's own database ApplicationData for the domain models and another one Membership for the user management / membership provider.
We do access restrictions using data-annotations in our controllers.
[Authorize(Roles = "administrators, managers")]
This worked great for simple use cases.
As we are scaling our application our customer wants to restrict specific users to access specific areas of our ApplicationData database.
Each of our products contains a foreign key referring to the region the product was assembled in.
A user story would be:
Users in the role NewYorkManagers should only be able to edit / see products that are assembled in New York.
We created a placeholder table UserRightsRegions that contains the UserId and the RegionId.
How can I link both the ApplicationData and the Membership databases in order to work properly / having cross-database-key-references? (Is something like this even possible?)
All help is more than appreciated!
In my opinion, you should be able to integrate your database with the standard aspnet_db reliably, but I would advise against duplicating or replacing the aspnet_users table.
That is the focal point of all the providers that use the aspnet_db schema, including custom providers that may augment but do not implement custom replacement.
To maximize reuse of strong tested infrastructure code in the provider stack/API it is best to go with that flow.
You will want to be very attentive to any modified membership core functions and ensure that the way your new constraints behave in an expected fashion in each case.
The facet of the membership story that I have found needs the most attention is deleting a user, and a simple modification/addition to the delete user sproc can manage this capably.
It sounds like you might need to create your own customized Membership Provider. You can probably (not positive here) extend the existing one so you don't have to completely reinvent it. Here is a video from ASP.net that describes how to do that. Google "asp.net membership provider" for tons more.
You can try rolling your own membership or just extend is like Dave suggests.
Create your own [Users] Table which can be populated based off the aspnet_Membership table. So therefore you could have more control over it.
You could also just implement a more involved Profiles system. The .NET team has improved the way profiles are stored now, so instead of "blobicizing" them, you can set them up to be stored in an actual table now [thank god].
Table Profile Provider
If you find the right articles, it's really easy to extend the membership provider to allow for extra functionality. I've moved my users table to my main SQL server table and have written my own role manager that gets values from a separate table. What it sounds like you need to do is to set up a table in your users DB with the location of each user, then create a method on the user object something like "GetLocation()" that returns the user's location from the DB, you could then user that to filter your data from your main DB. Here's a few articles I had kicking aroundin my bookmarks, see if they help, if you have a look on the main ASP.NET site or google for membership provider extending articles, there are plenty around.
http://msdn.microsoft.com/en-us/library/ms998347.aspx
https://web.archive.org/web/20211020202857/http://www.4guysfromrolla.com/articles/120705-1.aspx
http://msdn.microsoft.com/en-us/library/aa479048.aspx
As the others have pointed out there are many good resources available that can help you with creating your custom provider using the existing database.
It looks like you are headed in the right direction with mapping tables. I think the one piece you are missing is Distributed Queries. This link is specific to Sql Server 2008. There is a link there to the documentation for Sql Server 2005 if that is what you are using.
Related
I've successfully built many applications that use the ASP.NET Membership Provider. I'm diving into a new project that is multi-tenant and I'm making scalable by partitioning user accounts across multiple SQL Server databases.
Each database has the Memberships schema installed, and I have no problem setting any one of the databases as my Membership provider by adding the information in my Web.Config file. However, my goal is to use a master database as a look-up table. This database has a table called "Accounts" and I store the membership database for that account in a field called "MebershipPartition" that is associated with users on that account in that table (Each account can have many users).
Here is the basic architecture, with extraneous information removed:
What I am trying to do is set my Membership provider to that particular provider database via an updated connection string (or even just the databaseName since this is all on the same SQL Server) AFTER I do the lookup. This means that it has to happen outside of the Web.Config and the Global.asax file.
I also cannot pre-populate my Web.Config with a list of available connection strings because the system may generate a new Membership Database whenever certain criteria is met, such as reaching a certain cap on a previous database or of a particular account requires that it's users are isolated to an individual database. So this has to be completely dynamic.
In my research I've tried a number of methods which are detailed in the following posts:
StackOverflow, ASP.NET Forums and ASP.NET Forums
None of the above solutions work as desired. Either they rely on the solution to execute too early on in the application life-cycle, or they use hard coded values for the connection string and only solve the problem of removing the reliance on the Web.Config.
IDEALLY I would like to expose a single method that I can call at any time from a C# class to simply switch the database that the Membership system uses as needed, for example:
string Membership_DB_1 = "Server=[server];Database=database1;User ID=[userid];Password=[password];Trusted_Connection=False;Encrypt=True;"
string Membership_DB_2 = "Server=[server];Database=database2;User ID=[userid];Password=[password];Trusted_Connection=False;Encrypt=True;"
string Membership_DB_3 = "Server=[server];Database=database3;User ID=[userid];Password=[password];Trusted_Connection=False;Encrypt=True;"
Membership.SwitchConnectionString(Membership_DB_1);
Membership.SwitchConnectionString(Membership_DB_2);
Membership.SwitchConnectionString(Membership_DB_3);
I'd like to thank the community in advance, I have spent many days (and now weeks) wrestling with this scenario and I am near the point of pivoting to another solution!
UPDATED:
I've also been looking into the following solution, but again - it requires setting up the Web.Config with all potential connection strings in advance, which is not desirable from a management perspective:
Another Hard Coded Solution
Is this just not possible? Next I'll be looking into rolling my own version of the Membership Provider by using the Provider Toolkit Samples provided by Microsoft, but there aren't many samples or documentation available, and the actual downloads pages are missing in action! Scott Guthrie posted about it in 2005, but as you can see the reference in the article is now missing. I ended up finding a Download Link to the MSI here, but I'm not sure that the code is well maintained or even still relevant...
I was trying to avoid having to get this deep into the Provider components, but I plan to keep marching on! Will keep this thread updated as I go for any others looking to do the same!
Your proposed approach for switching connection strings:
Membership.SwitchConnectionString(Membership_DB_1);
doesn't sound practicable. Don't forget, there is a single static default provider which may be accessed concurrently from multiple request threads, and if you call this method you'll be changing the provider for all threads, which is unlikely to be what you want.
I must admit I don't see how your proposed architecture will increase scalability, but if you do want to do this, I think you're going to have to write a custom Provider.
I'm really unsure about how to handle this.
What I think I need is to have the usernames people register with ( through the default Provider ) to be copied to the UserName column of my other database, which has a junction table for a many:many relationship between User and Badge.
I've tried using SQL to copy the table over, but that's not really working out, as I don't know what table exactly I'm supposed to copy..
Any help would be dearly appreciated, and if more info is needed, please ask!
I'm not sure what your circumstances are, but as you're saying that the users are registered through the default Provider, I guess that the tables may have been generated by the aspnet_regiis tool?
If so, the users are stored in a table called aspnet_Users and the user names are in the column UserName.
I may have misinterpreted your question, but is this what you were asking for?
This really depends on your requirements which aren't really clear. If you always want your data to be up to date in your other database then syncing by copying the whole table isn't the way to go.
If you don't mind that you get the usernames on a later moment in time then syncing is the way to go. For example if you use the other database for BI purposes only, then a simple job for example in the night will do fine. Like Christoffer said you will need the UserName column from the aspnet_User table for this.
Now if you want it to be always up to date then you have two options:
Hacky and not advised by me. Extend the stored procs which are called by the default membership provider by also updating the other database. For example you could alter dbo.aspnet_Membership_CreateUser. Check your database for all aspnet membership stored procedures.
Implement your own membership provider which calls your own stored procedures or the default stored procedures + additional queries for updating the other database. This one is the most work and would be my choice. For examples of custom implementations see this one and this one.
Both options will need you to get familiar with the asp.net membership infrastructure.
So I'm using Forms Authentication on my site, and I've set up all the tables and stored procedures in SQL Server. The only thing is, I really don't think I need all these tables, and I'm not a big fan of the table names either.
For instance, I'm using the authentication for employees, so it would be nice to change the table name from "aspnet_Users" to "Employees". And I don't really need the Personalization tables. But I don't know if that would break anything.
Is it possible to modify/delete tables and stored procedures without messing everything up?
Steven, I take it you ran the aspnet_regsql.exe command line tool to add these database objects? Alternatively, you can add just the necessary tables/views/stored procedures for different parts of Membership by running the applicable SQL scripts, which you'll find in the %WINDIR%\Microsoft.Net\Framework\version folder (where version is the .NET version you are using, like v4.0.30319).
There you'll find files named InstallCommon.sql, InstallMembership.sql, InstallRoles.sql, InstallProfile.sql, InstallSqlState.sql, and so on. You'll need to run InstallCommon.sql and then just those other files you need. So if you need just Membership and Roles, you'd run InstallCommon.sql, InstallMembership.sql, and InstallRoles.sql. In this way your database would not include the tables/views/sprocs for profile, SQL state, and so on.
All that being said, I'd just leave in all of the database objects that ASP.NET added. It's probably more work than it's worth to add just the subset of interest and, who knows, you may need to implement profile or Health Monitoring later so why not have these other database objects in place and
ready to go.
And to answer your first question - No, there is no way to change the table name from aspnet_Users to Employees. However, it's not uncommon to create your own table (perhaps called Employees) that stores information about an employee. This table, then, would have a foreign key back to aspnet_Users that links an employee to a particular login account. See Storing Additional User Information for a look at how this can be accomplished.
Happy Programming!
Why not write a simple SqlMembershipProvider?
Edit: No, the default providers expect that schema. I would not recommend modifying it.
Have you considered just leaving the authentication stuff in it's own database?
You can change all references to aspnet_Users to Employees in the .sql scripts mentioned by Scott Mitchell but it isn't recommended. The Membership / Roles API uses stored procs. So if you don't change storedProc names you will be fine.
The issues might be if any future changes to membership / Roles providers and/or API.
Also say in future you need to give access to some users who are not the Employees, the renaming will lose its meaning.
I have an existing database with users and administrators in different tables.
I am rewriting an existing website in ASP.net and need to decide - should I merge the two tables into one users table and just have one provider, OR leave the tables separated and have two different providers.
Administrators, they need the ability to create, edit and delete users. I am thinking that the membership/profile provider way of editing users (i.e.
System.Web.Profile.ProfileBase pro = System.Web.Profile.ProfileBase.Create("User1");
pro.Initialize("User1", true);
txtEmail.Text = pro["SecondaryEmail"].ToString();
is the best way to edit users because the provider handles it? You cannot use this if you have two separate providers? (because they are both looking at different tables).
Or should I make a whole lot of methods to edit the users for the administrators?
UPDATE:
Making a custom membership provider look at both tables is fine, but then what about the profile provider? The profile provider GetPropertyValues and SetPropertyValues would be going on the same set of properties for users and admins.
Probably you should merge the two tables into one and use a RoleProvider to make the distinction between administrators and "normal" users.
Alternatively, you could implement your own, custom membership provider, which would use both tables.
Mike,
My advice is to go ahead and merge your two 'membership' tables and segregate admins from non with roles.
(This is, I know a duplicate of the previous answer but I feel the need to explain..)
In this way, unless you have some compelling reason to implement custom providers, you will be able to leverage the stock providers and database structure to provide a robust user management story for your website without writing any code (that you have to test and maintain).
.2 pesos....
I am looking into building an authentication in my ASP.NET application with the following requirements.
A user has exactly one Role (i.e. Admin, SalesManager, Sales, ....)
A role has a set of permissions to CRUD access a subset of existing objects. I.e.
"Sales has CREAD, READ, WRITE permission on object type "Products" but not DELETE"
Somehow I like the permissions to be in a hierarchy with inheritance so that I for i.e. Admin don't need to specify all available objects.
The system must quickly be able to answer the question "Does user X have permission to do Y to object Z"
All database managed (MSSQL), implemented in C#/ASP.NET
I would like to get feedback on these requirements? Any ideas how to implement this using ASP.NET framework(as much as possible)? (However, I'm also interested to know how this can be achieved without Memberships)
I think what you need to do here is implement a set of permissions query methods in either your business objects or your controller. Examples: CanRead(), CanEdit(), CanDelete()
When the page renders, it needs to query the business object and determine the users authorized capabilities and enable or disable functionality based on this information. The business object can, in turn, use Roles or additional database queries to determine the active user's permissions.
I can't think of a way to declaratively define these permissions centrally. They need to be distributed into the implementation of the functions. If you want do improve the design, however, you could use dependency injection to insert authorizers into your business objects and thus keep the implementations separate.
There's some code that uses this model in Rocky Lhotka's book. The new version isn't in Google yet.
I think one of the best implementations that I think will fulfill your requirements is documented here. The only issue is that this hooks into NHibernate, but you can use this as a template to create your own permissions implementation and simply hook into your own event model rather than that of NHibernates Interceptors.
I am working on such a system myself and will blog it once I am happy with it.
The membership API provided since ASP.NET 2.0 should suit your requirements well. The only thing I'm afraid it doesn't directly support is hierarchical roles. However you can easily use normal role based security with another manually written hierarchical roles table to achieve the required things.
You can read on how to set up ASP.NET Membership here: http://msdn.microsoft.com/en-us/library/yh26yfzy.aspx
It allows you to group folders / pages etc into Groups / Users. I think you will find this sufficient!
The hiarchy is easily managed by extending the generated databases and procedures.
I would build the user/role relationship so users can have more than 1 role. I see a 1-1 relationship and I get nervous because I know that even if we don't see a need for it now, someone is someday going to want someone to be both a Sales user and a Customer Service user.
In our customer system, we use roles to layer on stuff like "delinquentCustomer." That way, the original permissions are still valid--as soon as they pay their bill. Worth considering this approach.