2
votes

I'm about to go insane, so I'll try getting some help one more time... I'm using Visual Studio Express 2012 for Web to create an internet project using C# + MVC4 + Razor + Entity Framework + ASP.NET 4.5. I need to do the following: automatically create an "admin" user (who will be authorized all over the site) and then create some user roles. I've read about SimpleMembership, roles and everything all over the web, but nothing seems to give me a straightforward method to make the whole thing work.

This is what I've done so far:

1- Created a DataContext class:

public class DataContext : DbContext
{
public DataContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
}

2- Created an initializer class with what I assume would get me the admin user and Admins role created:

public class DataContextDbInitializer : DropCreateDatabaseAlways<DataContext>
{
protected override void Seed(DataContext context)
{
var roles = (Webmatrix.WebData.SimpleRoleProvider)System.Web.Security.Roles.Provider;
var membership = (Webmatrix.WebData.SimpleMembershipProvider)System.Web.Security.Membership.Provider;
if (!roles.RoleExists("Admins")) {
roles.CreateRole("Admins");
}
if (membership.GetUser("admin", false) == null) {
membership.CreateUserAndAccount("admin", "123456");
}
if (!roles.GetRolesForUser("admin").Contains("Admins")) {
roles.AddUsersToRoles(new[] { "admin" }, new[] { "Admins" });
} 
}
}

(I've tried inheriting from DropCreateDatabaseIfModelChanges but that doesn't help).

3- Added two lines to the App_Start method in Global.asax:

WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
Database.SetInitializer<DataContext>(new DataContextDbInitializer());

I also tried using WebSecurity.InitializeDatabaseConnection in the Seed method in my DataContextDbInitializer class, but didn't work.

4- Removed the [InitializeSimpleMembership] annotation from the AccountController, so I can initialize it from the very beginning of the application life cycle (using WebSecurity.InitializeDatabaseConnection in App_Start, as I've explained in number 3). I tried adding [InitializeSimpleMembership] on top of the Index method in the HomeController and removing WebSecurity.InitializeDatabaseConnection from App_Start, but that doesn't help either.

5- In Web.config I have authentication method as Forms (default) and also left the default connection string:

<connectionStrings>
<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-TPFinal-20130121210447;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-TPFinal-20130121210447.mdf" providerName="System.Data.SqlClient" />
</connectionStrings>

And added this inside the system.web tag:

<roleManager enabled="true" cacheRolesInCookie="true" />

6- Then, to test if everything works, I use the [Authorize(Roles = "Admins")] annotation on top of the About() method in the HomeController. If things were working as expected, this should force me to log in as admin/123456 in order to be able to see the "About" page, controlled by HomeController/About.

7- I haven't added EF migrations or anything else. All the other stuff is by default as VS2012 automatically created it. So I run my application and click on the "About" link, and I get presented with the login form (that means that the [Authorize(Roles = "Admins")] annotation is doing what it's expected to do). I attempt to log in as admin/123456 but the log in page is reloaded over and over, every time I click on the log in button.

A few things I have noticed:

-if I add a breakpoint in the Seed method, it seems it's not getting called.

-when I use DropCreateDatabaseAlways and run the application again, I'm able to log in as admin/123456, which makes me think again that my whole DataContextDbInitializer class is not even being used, since I assume the DB should be created from scratch, which would delete the admin user.

I don't know what else to read, what else to try... I'm new to asp.net (needed I say that?) and just going nuts. Thanks!!

3

3 Answers

1
votes

Finally, I managed to make the whole thing work!

My seed method now looks like this:

WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);

if (!Roles.RoleExists("Admins"))
{
    Roles.CreateRole("Admins");
}
if (!WebSecurity.UserExists("admin"))
{
    WebSecurity.CreateUserAndAccount("admin", "123456");
}
if (!Roles.GetRolesForUser("admin").Contains("Admins"))
{
    Roles.AddUsersToRoles(new[] { "admin" }, new[] { "Admins" });
}
base.Seed(context);

My App_Start in Global.asax looks like this:

Database.SetInitializer(new DataContextDbInitializer());
DataContext c = new DataContext();
c.Database.Initialize(true);

I'm not sure if this is actually doing something, but I have this inside the the system.web tag in Web.config:

<roleManager enabled="true" cacheRolesInCookie="true" />

I also removed [InitializeSimpleMembership] from AccountController, and the UsersContext class in AccountModels. I moved this bit to my own context class:

public DbSet<UserProfile> UserProfiles { get; set; }

Then, to test if everything works, I use the [Authorize(Roles = "Admins")] annotation on top of the About() method in the HomeController. If things are working as expected, this should force me to log in as admin/123456 in order to be able to see the "About" page, controlled by HomeController/About. Which does ;)

Thanks for the help! It contributed to me understanding a bit more what was going on.

1
votes

In MVC4 a default Internet Application has Authentication built-in. A class called InitializeSimpleMembershipAttribute is located in a Filters directory. As the name suggests this class Initializes the Simple Membership Database. If you look at the constructor you'll see the following line:

 WebSecurity.InitializeDatabaseConnection("UserContext", "UserProfile", "UserId", "UserName", autoCreateTables: true);

Below this line you can insert the following code to create a default user:

// Create admin user.
                    if (!WebSecurity.UserExists("admin"))
                    {
                        WebSecurity.CreateUserAndAccount("admin", "12345678!");
                    }

Just another way to do things.

0
votes

Try like this:

public class DataContextDbInitializer : DropCreateDatabaseAlways<DataContext>
{
    protected override void Seed(DataContext context)
    {
        if (!Roles.RoleExists("Admins"))
        {
            Roles.CreateRole("Admins");
        }
        if (!WebSecurity.UserExists("admin"))
        {
            WebSecurity.CreateUserAndAccount("admin", "123456");
        }
        if (!Roles.GetRolesForUser("admin").Contains("Admins"))
        {
            Roles.AddUsersToRoles(new[] { "admin" }, new[] { "Admins" });
        }
    }
}

and in your Application_Start:

Database.SetInitializer<DataContext>(new DataContextDbInitializer());
using (var ctx = new DataContext())
{
    ctx.Database.Initialize(true);
}
WebSecurity.InitializeDatabaseConnection(
    "DefaultConnection", 
    "UserProfile", 
    "UserId", 
    "UserName", 
    autoCreateTables: true
);