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!
Related
I am creating dynamic connection strings in my project. They're created on the fly with the information provided specifically for every user. When the application first fires off, if a database doesn't exist (first time user logs on), a new database is created without problems with this initializer:
public DataContext() : base()
{
// ProxyCreation and LazyLoading doesn't affect the situation so
// comments may be removed
//this.Configuration.LazyLoadingEnabled = false;
//this.Configuration.ProxyCreationEnabled = false;
string conStr = GetDb();
this.Database.Connection.ConnectionString = conStr;
}
The problem is, with this method, I have to restart the application pool on the server and the new user should be the first accessor to the application.
I need the same thing without a requirement of restarting the app. Is that possible?
(This is a SPA using AngularJS on MVC views and WebApi as data provider - May be relevant somehow, so thought I should mention)
I already tried this, but this creates an error for EF and the application doesn't start at all...
You could try a little bit different approach to connect directly (and create) the right database.
public class DataContext : DbContext
{
public DataContext(DbConnection connection) : base(connection, true) { }
}
Here you create the DbContext already with the right connection.
Take also care because you need to specify to migrations that the right connection should be used (not the Web.Config connection but the connection that raised the database creation).
See the second overload here https://msdn.microsoft.com/en-US/library/hh829099(v=vs.113).aspx#M:System.Data.Entity.MigrateDatabaseToLatestVersion.
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.
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.
Getting a bit disappointed with all the weird exception I keep running into while working with WebSecurity. Also the poor integration with OAuth doesn't make it look prettier. Considering to drop the concept and write the whole user management manually...
Anyway, I am using WebSecurity to administer users and passwords. Now I try to implement the part where accounts can be deleted. Weirdly enough this method is not on the static class WebSecurity. Apparently I need to delete accounts via the SimpleMembershipProvider.
var provider = new SimpleMembershipProvider();
provider.DeleteAccount(username);
The deleteAccount method throws an invalidoperation exception with the following message:
You must call the "WebSecurity.InitializeDatabaseConnection" method before you call
any other method of the "WebSecurity" class. This call should be placed in an
_AppStart.cshtml file in the root of your site.
Well that's weird since I already have this in my _ViewStart (otherwise I wouldn't have been able to create the accounts in the first place).
if (!WebSecurity.Initialized)
{
WebSecurity.InitializeDatabaseConnection(
"DefaultConnection",
"Users",
"UserId",
"UserName",
true);
}
What am I doing wrong this time?
Have you tried:
Membership.DeleteUser(username);
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.