ASP.NET (MVC) Users, Roles and Users in Roles - c#

I have had a mini-battle over the past two days and although I have solved it, its not in the way I expected.
ASP.NET 4.5 on Visual Studio 2013.
If you create a new project and fire it up for the first time, and register, everything is fine.
It creates a table called AspNetUsers and puts your in that table.
It also creates other tables (including - but not limited to)
- AspNetRoles - stores roles
- AspNetUserRoles - stores users in roles
So I used Database first to create Models and Controllers for all 3 tables so I can manage them from an interface (I know there used to be something built in to VS2010 but it seems to have disappeared - no worries as I learnt how to do DB first)
Problem is when I added a user to a role, the data showed in my database but when I tried to lockdown a controller method using the decorator:
[Authorize(Roles="admin")]
public string test()
{
return "hello";
}
everything started to go pear shaped.
So I tested User.IsInRole("admin") and to my dismay it returned false!
So I went down the journey of web.config (this tells the role manager to point at my DefaultConnection (which I am using for everything)
<roleManager enabled="true" defaultProvider="SqlRoleManager">
<providers>
<add name="SqlRoleManager"
type="System.Web.Security.SqlRoleProvider"
connectionStringName="DefaultConnection"
applicationName="ConferenceOrg" />
</providers>
</roleManager>
Then in code I used:
string role = "admin";
if (!Roles.RoleExists(role))
{
Roles.CreateRole(role);
}
else
{
string[] usrs = { User.Identity.Name };
if (!User.IsInRole(role))
{
Roles.AddUsersToRole(usrs, role);
}
}
Initially this didn't work because a stored procedure didn't exist.
So, I went to C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\Shortcuts and fired up the Native Tools Cmd prompt (x86) and used that to add the stored procedures etc
Now the code above works but it has created a whole bunch of tables in addition to the original set.
The code creates the role in the new aspnet_roles table and it now creates my user in BOTH AspNetUsers and aspnet_users
So my next move is to just delete the tables I created with database first and redo it for the aspnet_ tables instead...
Not happy but it will work. Any thoughts on my situation?
Am I doing it right?

Your project is mixed with Legacy SQL Membership Provider and New ASP.Net Identity.
You only need one Membership which is New ASP.Net Identity.
In ASP.Net Identity, you do not need to configure any setting in web.config (except AppStartup for owin).
How to Solve it
Now your database is a mess. Since your application is new and no existing users, it is easily to recreate a new Application and new Database, and start all over again.
Then make sure both User and Role are assigned to AspNetUserRoles table.

Related

Forms Authentication — Where are roles stored?

Where are the roles below stored?
Roles.AddUserToRole(user.UserName, "customer");
Roles.IsUserInRole(user.UserName, "admin");
If I add a user to a role using the code then the membership persists. I cant see a built in database in my project and I have not manually specified a database. Am I going mad?
Information about role storage may be picked from web.config
This db might have been created for you by asp.net itself, and is called
aspnetdb
Please look at this as well:
https://msdn.microsoft.com/en-us/library/system.web.security.roles(v=vs.110).aspx

Getting Role Manager to work with an existing database

I'm using ASP.NET MVC with Entity Framework and I'm trying to figure out how to get the Role Manager working. After I initially registered a user, triggering EF to build the user management / identity tables, I ended up moving them (AspNetUsers, AspNetRoles, AspNetUserRoles, AspNetUserClaims, and AspNetUserLogins) over to my existing database and setting the default connection to point there as well. All was fine and dandy until I decided I needed to implement the Role Manager and after adding the following entry to my web.config,
<roleManager enabled="true" defaultProvider="RoleManager">
<providers>
<add name="RoleManager"
type="System.Web.Security.SqlRoleProvider"
connectionStringName="DefaultConnection"
applicationName="MyApp" />
</providers>
</roleManager>
and attempting to create a role (Roles.CreateRole("Admin") [Side question, is there any way to do this outside of code in VS 2013?]), I was faced with the error: Could not find stored procedure 'dbo.aspnet_CheckSchemaVersion'. So I naturally assumed the tables were not the only database objects that were generated, but after checking out the original mdf, turns out that indeed, that's all there was. I'm at a complete loss at this point and any help would be greatly appreciated.
Update: I've looked up solutions related to using the aspnet_reqsql tool to add Role Manager functionality, but the tables this generates seem incompatible. E.g., dbo.aspnet_Users instead of dbo.AspNetUsers.
It seems that the tables AspNetUsers, AspNetRoles, AspNetUserRoles, AspNetUserClaims, and AspNetUserLogins are part of the newer ASP.NET Identity system, whereas aspnet_Users, etc. are part of the old system (Membership I believe). Roles.CreateRole is from Membership, not Identity. For the Identity system, the roleManager web.config entry is unecessary, and a role would be created using Microsoft.AspNet.Identity.RoleManager. E.g.,
RoleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(new MyDbContext()));
var roleresult = RoleManager.Create(new IdentityRole(roleName));

Switch connectionstring by visiting subfolder EF

Rewritten my previous question without confusing code samples.
This is what i'm trying to achieve (or have done so far)
I created a webapplication to be viewed at www.myschool.com
I want to use subdomains so different schools can login to their personal school database (no central db)
I have 2 subfolders in my webapplication "school1" and "school2"
When a school visits www.myschool.com/school1 it should load the webapplication in the root folder by using the connectionstring provided in web.config from it's subfolder (school1 has web.config, school2 has web.config)
When www.myschool.com is entered (no subdomain) it should show the database "school1" as default
The databases are sqlserver databases and I use EF with .edmx file "SchoolModel" wich contains the entity model for the individual school databases, those have equal tabbles, fields, ... .
What currently happens:
When visiting www.myschool.com it shows the application with database from default school1
When entering www.myschool.com/school1 it just shows HTTP Error 403.14 - Forbidden because that folder only contains a web.config with another connectionstring for that specific school.
In the end a school (school1 or school2) should be able to enter school1.myschool.com or school2.myschool.com to view the webapplication loaded with content of it's personal database.
Using central database to serve both is not an option in this particular application.
Thanks for helping me setting this up

Authorization/Roles in MVC5

I'm having trouble using authorization/roles in MVC5 (VS2013).
Authentication works pretty much out of the box (that's to say just by using Visual Studio to create the default MVC project). I change the DefaultConnection connection string to a valid (but non-existent) database. I then register a new user and the database is automatically created, with tables such as AspNetUsers and AspNetRoles.
However, I can't seem to do anything with roles. The first thing to do seemed to be to add a role with C# code like:
Roles.CreateRole("Admin");
I get an exception with the message:
'The Role Manager feature has not been enabled.'
I enable it in web.config with:
<roleManager enabled="true"/>
And now get the exception:
'Unable to connect to SQL Server database.'
This used to work very easily with System.Web.Security.SqlRoleProvider, but not with the new provider that comes as default with MVC5. There are lots of very complex articles on this, but it seems to me that it is something so essential and straightforward that there must be a simple way to get it working.
Many thanks for any help.
I've solved this now. It turns out that the Roles class is completely irrelevant to role management in MVC5, at least in terms of the out-of-the-box configuration.
The Roles class and Membership class are still there, with the Provider configured to SqlMembershipProvider.
However, this is NOT the provider used by the AccountController, which does not use the Membership class at all; it uses Microsoft.AspNet.Identity.UserManager.
While the generated AccountController provides plenty of examples of using UserManager, it does nothing related to roles.
The equivalent class for Roles is Microsoft.AspNet.Identity.RoleManager. There is full documentation for this in MSDN
I suggest referring to this article as it shows how you can create roles. Once you've created whatever roles are required, you can use the UserManager.AddToRole or UserManager.AddToRoleAsync method to add a user to a particular role.

ASP.NET MVC authentication using custom database instead of ASPNETDB?

I already have a User table in my primary application database with an email address (which will act as the user name) and a password. I would like to authenticate using my database instead of the default authentication database (ASPNETDB).
Questions:
Is this a bad idea? Is it a huge can of worms to use my own DB for authentication?
How much work am I adding by doing this? I already have code for hashing the password and a query that will check if the email and password match the DB. So, I wouldn't be starting from scratch.
What would I need to do to use my database instead of ASPNETDB? I'm hoping this can be described in a few simple steps, but if not, could you point me to good source?
Update
I'm still looking for a little more detail here on my third question. Do I need to write my own MembershipProvider? What changes do I need to make to my web.config file? Will the [Authorize] attribute still work if I write my own solution? Can I use the automatically-generated AccountController with some minor modifications or do I basically need to rewrite the account controller from scratch?
It's quite simple, you need to derrive MembershipProvider and implement the ValidateUser method. Take a look at this post. I'm using custom membership provider with Postgres and MVC just fine.
I'll answer your updated questions:
Do I need to write my own MembershipProvider?
If you (a) want to continue using Forms Authentication, and (b) have an authorization table structure that doesn't follow the same conventions as the ASPNETDB, then yes. If you don't need FormsAuth (see below), then you can do away with the MembershipProvider entirely, but I wouldn't recommend it. Or, if you're using the exact same security tables as ASPNETDB but just want to point it to a different database, you can continue using the default provider and simply change its configuration.
What changes do I need to make to my web.config file?
If you are using your own custom MembershipProvider, then you need to register it in the <providers> section of the <membership> element and change the defaultProvider property. If you are using the standard AspNetSqlProvider then you probably just need to change the connection string.
Will the [Authorize] attribute still work if I write my own solution?
Yes, if you stick to Forms Authentication (either use the AspNetSqlProvider or write and register your own membership provider). No, if you abandon Forms Authentication (again, not recommended).
Can I use the automatically-generated AccountController with some minor modifications or do I basically need to rewrite the account controller from scratch?
You should rewrite the AccountController anyway - don't leave demo code in a production app. But if you must - yes, the AccountController will work under the same conditions as above.
No. And I would suspect most people do not trust that cruddy mechanism
Not much at all, especially since you have the table already.
Take a look at this for example: http://forums.asp.net/t/1250726.aspx
Hi ,
Just follow these simple steps :
First, you can delete the .mdf file in App_Data folder. Since we don’t need any of these tables.Then, we need to update the default connection string in the web.config to point to our database.
<connectionStrings>
<add name=”DefaultConnection” connectionString=”Data Source=SERVER\INSTANCENAME;Initial Catalog=DBNAME;Integrated Security=True” providerName=”System.Data.SqlClient” />
</connectionStrings>
Third, Open Nuget Package Manager and write the following commands:
Enable-Migrations
Add-Migration Init
Update-Database
Check out your database, all ASP.NET membership tables with Prefix Asp have been create and then you can test it out by running your application and execute membership actions such as Signing up or Signing in to your application.
Created tables after running above commands:
AspNetRoles
AspNetUserClaims
AspNetUserLogins
AspNetUserRoles
AspNetUsers
__MigrationHistory
Source : https://blogs.msmvps.com/marafa/2014/06/13/how-to-create-asp-net-mvc-authentication-tables-in-an-existing-database/
We're doing exactly this in one of our applications, and find it quite simple. We have an authentication service (called from the controller) that handles the mechanics of hashing the entered password to see if it is a match, then simply returns a bool for a method we call "IsValidLogon".
In our case, the purpose was to keep the management of what should be a pretty trivial task as lightweight as possible.
We bascially ignored ASPNETDB entirely. If we get a valid response from our user/password check, we simply call the standard FormsAuthentication.RedirectFromLoginPage(username, createCookieBool);
Hope that helps.
just building the same, so answer to 1 must be NO :)
I'm using the standard asp.net forms authentication, where i use the FormsAuthentication.RedirectFromLoginPage(username, createCookieBool) method to log a user in.
I gave a user a unique guid (you can use any other user id) and i'm storing it in the UserName parameter along with the username (to display on the masterpage: Html.Encode(Page.User.Identity.Name.Split("|".ToCharArray())[1]))
In each controller/method in which i must know which user is logged on (via User.Identity.Name, split the string and get the userguid).
Also i decorate those routines with the [Authorize] attribute.

Categories