I created a website and published it to Azure. Now I want to protect some pages with authentication so I added the following code in the method Seed of Migrations.Configuration and published it to Azure.
However, the code is run even on local. The table AspNetRoles is still empty on both local and Azure SQL server. I tried to use Update-Database -Script -SourceMigration:0 to generate all the SQL statements but there is no SQL inserting the initial data to these tables of Asp.Net identity.
// Create role
var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
const string roleName = "CanEdit";
var role = roleManager.FindByName(roleName);
if (role == null)
{
role = new Microsoft.AspNet.Identity.EntityFramework.IdentityRole(roleName);
var roleresult = roleManager.Create(role);
}
var memberEmails = Properties.Settings.Default.CanEditMembers.Split(';');
foreach (var email in memberEmails)
{
var user = userManager.FindByName(email.Trim());
if (user != null)
{
var rolesForUser = userManager.GetRoles(user.Id);
if (!rolesForUser.Contains(role.Name))
{
var result = userManager.AddToRole(user.Id, role.Name);
}
}
}
Basically I want to insert 'CanEdit' to the AspNetRoles (I guess) and insert some other values (Users and their associations with the role) to these AspNetUser.... tables. Some of my Controls are already decorated with attribute [Authorize(Roles = "CanEdit")].
Related
This is truly one of the strangest issues I've run into.
I have a Web API which uses EF. I have an audit table which takes an ApplicationUser. I create the new object, add it to the collection and then call SaveChangesAsync(). The weird part is, I get "User name MyUserName is already taken." error.
using (var context = new ApplicationDbContext())
{
var user = context.Users.Single<ApplicationUser>(x => x.UserName == model.UserName);
var sid = context.SessionIds.FirstOrDefault(x => x.Id == model.SessionId);
var audit = new Audit
{
Data = model.Data,
User = user,
IpAddress = Helper.GetClientIp(Request),
Session = sid != null ? sid : ItsMyChance.Entities.Entities.SessionId.Create(scoreModel.UserName, scoreModel.GameId)
};
context.Audits.Add(audit);
await context.SaveChangesAsync();
}
Update
This code has been working for years. The difference is I upgrade from .NET 4.5 to .NET 4.61
Update 2
I also tried the following but still receive the same error
[ForeignKey("User")]
public string UserId { get; set; }
public ApplicationUser User { get; set; }
Update 3
Trying to track this issue down I call
var entries = context.ChangeTracker.Entries();
It returns several entries, 1 for each object, including User. User shows Added and another as Unchanged. I can't figure out how this is happening.
In addition, I added the following before making any changes but there's no effect.
context.Configuration.AutoDetectChangesEnabled = false;
Since You are adding the complete user object in Audit , so SaveChangesAsync will save a new Entry for Audit and User also and since a user with same username already exists that's why you are getting this error. What you should do is just assign just the UserId (Whatever is referral key in Audit table for User) in Audit object
var audit = new Audit
{
Data = model.Data,
UserId = user.Id,
IpAddress = Helper.GetClientIp(Request),
Session = sid != null ? sid : ItsMyChance.Entities.Entities.SessionId.Create(scoreModel.UserName, scoreModel.GameId)
};
I have a function that finds a user in a database and adds a role. The role is added in the Asp.net identity table. I am trying to update the role that also exists in another table in a different database. I've written the logic but I'm not quite there yet. So my question is, how do I correctly add the role that is being added to the aspnetuserroles table, to my custom table located in a different database? Any assistance would be helpful.
public async Task<bool> AddARole(string email, string role)
{
//get the users email address and role from the Person table
var currentUser = _context.PersonTable.Where(x => x.Email == email && x.UserRole == role);
var UserManager = _serviceProvider
.GetRequiredService<UserManager<ApplicationUser>>();
var userExists = await UserManager.FindByEmailAsync(email);
if (userExists!= null)
{
await UserManager.AddToRoleAsync(user, role);
//update the person's role
_context.PersonTable.Update(currentUser);
}
_identityContext.SaveChanges();
//save the changes
await _context.SaveChangesAsync();
return true;
}
I have a Web Api solution that makes use of ASP.NET Identity (v2.1) and Entity Framework v6.1. Inside the Seed() method of the Configuration.cs file I have code that creates my first Identity user. This code makes use of the Identity framework to hash the password, create the security stamp, etc. These are all things I cannot do via SQL so adding to the Up() method does not seem like an option.
protected override void Seed(ApplicationDbContext context)
{
// Create the admin user
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(new ApplicationDbContext()));
var user = new ApplicationUser()
{
UserName = "adminuser",
Email = "adminuser#mycompany.com",
EmailConfirmed = true,
FirstName = "John",
LastName = "Does",
JoinDate = DateTime.Now.AddYears(-1)
};
manager.Create(user, "SuperSecurePassword321!");
if (roleManager.Roles.Count() == 0)
{
roleManager.Create(new IdentityRole { Name = "Admin" });
roleManager.Create(new IdentityRole { Name = "Employee" });
roleManager.Create(new IdentityRole { Name = "Customer" });
}
var adminUser = manager.FindByName("adminuser");
manager.AddToRoles(adminUser.Id, new string[] { "Admin" });
}
I need to use FTP to publish this (no control over this). Any suggestions on how to run this code once it is deployed and the database is schema is setup?
Options I have considered:
I have thought about creating an API endpoint that when called can
kick off this code, however, this endpoint would have to allow
anonymous access since it would be creating this first user and
the roles used in the system. I would then need to somehow disable
or remove this endpoint later.
Script the database and include the data and then restore that to
the database server targeted for deployment.
Seed() gets called when the database is accessed the first time. What's wrong with that automatic behavior?
If you want to call it manually, try something like this in protected void Application_Start():
Database.SetInitializer(new YourInititalizer());
var dbContext = new TheContextYouAreUsing();
dbContext.Database.Initialize(force: true);
I am having trouble figuring out how to seed additional users and roles into my MVC5 application, using EF6 code first. In order to debug the Seed method from the Configure.cs since update-database was not working, I wrote this controller,
public ActionResult test() {
var context = new ApplicationDbContext();
var roleStore = new RoleStore<IdentityRole>(context);
var roleManager = new RoleManager<IdentityRole>(roleStore);
roleManager.Create(new IdentityRole { Name = "basic" });
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new UserManager<ApplicationUser>(userStore);
var adminthere = context.Users.Any(n => n.UserName == "Admin");
var basicthere = context.Users.Any(n => n.UserName == "Basic");
// Create Dummy basic account
if (!basicthere) {
var basicUser = new ApplicationUser { UserName = "Basic" };
userManager.Create(basicUser, "test");
var _id = basicUser.Id;
userManager.AddToRole(basicUser.Id, "basic");
}
return View();
}
The debugger throws an exception at the userManager.AddToRole(basicUser.Id, "basic"); call saying "UserID not found"? Here is a screenshot including variable values from the debug session:
What is the problem? Also, the exact same code (changing the words "basic" for "Admin") works for seeding the database with the Admin user in role "admin". Why?
EDIT EDIT: moved edit I posted here previoulsy to a real answer below.
As the comments suggested I will post my this as an answer:
The line of code userManager.Create(basicUser, "test"); didn't succeed - the passwort must at least have 6 characters. So while creating the basicUser ApplicationUser instance worked (and hence the _id was not null) I didn't have an IdentityUser of that _id. On admin it succeeded previously bc. I had a different pwd that I didn't want to post here ...
I created a new ASP.NET MVC-5 application with Individual User Accounts and then updated all the Nuget packages in the solution. Now I'm trying to follow some of the guidelines shown in some tutorials but I encountered some problems.
The first one is that a class called ApplicationRoleManager which is being used throughout the application wasn't created (the ApplicationUserManager was created).
The second problem is more about Entity-Framework: I've seen that for seeding the database with a user and role many people create a static constructor in the ApplicationDbContext class:
static ApplicationDbContext()
{
Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
}
So I added it, and the implementation of the ApplicationDbInitializer is:
public class ApplicationDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext>
{
protected override void Seed(ApplicationDbContext context)
{
InitializeIdentityForEF(context);
base.Seed(context);
}
//Create User=Admin#Admin.com with password=Admin#123456 in the Admin role
public static void InitializeIdentityForEF(ApplicationDbContext db)
{
var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
const string name = "admin#admin.com";
const string password = "Admin#123456";
const string roleName = "Admin";
//Create Role Admin if it does not exist
var role = roleManager.FindByName(roleName);
if (role == null)
{
role = new IdentityRole(roleName);
var roleresult = roleManager.Create(role);
}
var user = userManager.FindByName(name);
if (user == null)
{
user = new ApplicationUser { UserName = name, Email = name };
var result = userManager.Create(user, password);
result = userManager.SetLockoutEnabled(user.Id, false);
}
// Add user admin to Role Admin if not already added
var rolesForUser = userManager.GetRoles(user.Id);
if (!rolesForUser.Contains(role.Name))
{
var result = userManager.AddToRole(user.Id, role.Name);
}
}
After adding everything I opened the Package Manager Console and typed Enable-Migrations, then Add-Migration someName and then Update-Database.
the results were that the database was created successfully but no data was inserted to the database.
After noticing the data wasn't inserted I moved the Seed logic to the Index method of the home controller and the data was inserted after running the application.
I also needed to add this line: app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
to the Startup.Auth.cs file.
So my questions are:
Do I really need to enter the ApplicationRoleManager class
manually?
How do I make the seed method work?
UPDATE
I've changed the Seed method to:
protected override void Seed(ApplicationDbContext context)
{
var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
//since there is no ApplicationRoleManager (why is that?) this is how i create it
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
const string name = "admin#admin.com";
const string password = "Admin#123456";
const string roleName = "Admin";
//Create Role Admin if it does not exist
var role = roleManager.FindByName(roleName);
if (role == null)
{
role = new IdentityRole(roleName);
var roleresult = roleManager.Create(role);
}
//app hangs here...
var user = userManager.FindByName(name);
if (user == null)
{
user = new ApplicationUser { UserName = name, Email = name };
var result = userManager.Create(user, password);
result = userManager.SetLockoutEnabled(user.Id, false);
}
// Add user admin to Role Admin if not already added
var rolesForUser = userManager.GetRoles(user.Id);
if (!rolesForUser.Contains(role.Name))
{
var result = userManager.AddToRole(user.Id, role.Name);
}
base.Seed(context);
}
So now, the Admin role is created but when getting to var user = userManager.FindByName(name); the application hangs with no exception or any message...
When using migrations you can use the built in initializer and the Seed method:
Database.SetInitializer<ApplicationDbContext>(new
MigrateDatabaseToLatestVersion<ApplicationDbContext,
APPLICATION.Migrations.Configuration>());
and in APPLICATION.Migrations.Configuration (this was created by the Enable-Migrations command):
protected override void Seed(ApplicationDbContext context)
{
// seed logic
}
As a role manager you can also use the RoleManager<ApplicationRole> base implementation.
I also was a bit confused about hanging of application in this case. The problem can be solved in this way
var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUserManager>(db));
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(db));
And for anyone using the ApplicationUser with Integer foreign key, the code is this one:
var userManager = new ApplicationUserManager(new ApplicationUserStore(context));
var roleManager = new ApplicationRoleManager(new ApplicationRoleStore(context));
This works great for default MVC 5 project.
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context));
It doesn't appear that the solutions posted address the issue of the app hanging on the call of userManager.FindByName(name). I'm running into the same problem. It worked a few hours ago on my local. I published to Azure and it started hanging. When I tested my local again it all of a sudden started hanging at that step. No error is returned and no timeout (at least after waiting 10-15 minutes). Does anyone have any tips to address Yoav's ultimate question?
I have some other very simple seeding processes that run before adding roles, and db.Foo.AddOrUpdate(foo) calls are running without error, but not actually saving anything to the database.
I just spent a deeply unpleasant half day dealing with this. I finally managed to get the damn thing to fire:
public static void InitializeIdentityForEF(ApplicationDbContext context)
{
context.Configuration.LazyLoadingEnabled = true;
//var userManager = HttpContext.Current
// .GetOwinContext().GetUserManager<ApplicationUserManager>();
//var roleManager = HttpContext.Current
// .GetOwinContext().Get<ApplicationRoleManager>();
var roleStore = new RoleStore<ApplicationRole, int, ApplicationUserRole>(context);
var roleManager = new RoleManager<ApplicationRole, int>(roleStore);
var userStore = new UserStore<ApplicationUser, ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>(context);
var userManager = new UserManager<ApplicationUser, int>(userStore);
...
It's the end of an extremely long day, and I suspect someone's going to tell me why I shouldn't do this. The rest of my Seed method fires beautifully, however, using non-async methods (FindByName/Create).
Sir goobering,
You struggles have helped me get passed this problem, I had to do it a little different though.
context.Configuration.LazyLoadingEnabled = true;
//var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
//var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
const string name = "admin#example.com";
const string password = "Admin#123456";
const string roleName = "Admin";
***var userManager = new ApplicationUserManager(new UserStore<ApplicationUser>(context));
var roleManager = new ApplicationRoleManager(new RoleStore<IdentityRole>(context));***
//Create Role Admin if it does not exist
var role = roleManager.FindByName(roleName);
if (role == null) {
role = new IdentityRole(roleName);
var roleresult = roleManager.Create(role);
}