Issues deploying mvc application that has users and needs an update on the database - asp.net

I am trying to seed an admin user to my database so that when I deploy, I have one user that can log in....
This is the seed method that I am trying to run, but it gives me "no suitable method found to override". My application user is the stock application user that visual studio gives you in the template.
protected override void Seed(ApplicationDbContext context)
{
// This method will be called after migrating to the latest version.
var passwordHash = new PasswordHasher();
string password = passwordHash.HashPassword("Password#123");
context.Users.AddOrUpdate(u => u.UserName,
new ApplicationUser
{
UserName = "Steve#Steve.com",
PasswordHash = password,
PhoneNumber = "08869879"
});
}

Class where you override Seed method need inheriting from DropCreateDatabaseAlways<TContext>.
Error :
no suitable method found to override
Mean only that base class not contains Seed method. You can override it like:
internal sealed class Configuration : DbMigrationsConfiguration<YourContext>
{
protected override void Seed(YourContext context)
{
//add data
}
}

Related

Retrieving custom user roles after login - Blazor WASM

I have a Blazor WebAssembly (WASM) app that authenticates users using Okta. After they successfully log in via Okta, I want to authorize the user by calling an API that I wrote to retrieve that users roles and other general user info that we store. This call to get user info must also include the access token retrieved from the Okta log in.
The authentication piece with Okta works fine.
I'm not sure how to correctly call our API to get user info/roles as part of the login process, so that the roles can be added as claims BEFORE being redirected to any other page.
The Okta log in piece is set up using the RemoteAuthenticatorView and added in Program.Main as:
builder.Services.AddOidcAuthentication(options =>
{
options.ProviderOptions.Authority = builder.Configuration.GetValue<string>("Okta:Authority");
options.ProviderOptions.ClientId = builder.Configuration.GetValue<string>("Okta:ClientId");
options.ProviderOptions.ResponseType = "code";
});
What I've tried so far:
Using the OnLogInSucceeded event callback of the RemoteAuthenticatorView. This doesn't work because the user will be redirected to the page they tried to access before the api call completes. Therefore if the page has any Authorize(Roles="Admin") type of restrictions on it, those roles haven't been populated yet.
Using a factory that inherits from AccountClaimsPrincipalFactory. This seems like the correct way, however I'm getting runtime errors anytime I inject certain classes or services into my factory. I think I've narrowed it down to being an issue with an injected service using the IHttpClientFactory. Here's my factory code:
public class ClaimsPrincipalFactory : AccountClaimsPrincipalFactory
{
private IUserService userService { get; set; }
public ClaimsPrincipalFactory(
IAccessTokenProviderAccessor accessor,
IUserService userService
)
: base(accessor)
{
this.userService = userService;
}
public async override ValueTask<ClaimsPrincipal> CreateUserAsync(
RemoteUserAccount account, RemoteAuthenticationUserOptions options)
{
var user = await base.CreateUserAsync(account, options);
var userInfo = await userService.UserInfo();
var identity = user.Identity as ClaimsIdentity;
if (userInfo != null)
{
foreach (var role in userInfo.UserRoles)
{
identity.AddClaim(new Claim(ClaimsIdentity.DefaultRoleClaimType, role.ApplicationRole.Name));
}
}
return user;
}
}
Here is the constructor of my UserService:
public UserService(IHttpClientFactory clientFactory)
{
http = clientFactory.CreateClient("BlazorClient.ServerApi");
}
The CreateClient line causes this runtime error:
crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: ValueFactory attempted to access the Value property of this instance.
System.InvalidOperationException: ValueFactory attempted to access the Value property of this instance.
at System.Lazy`1[[Microsoft.Extensions.Http.ActiveHandlerTrackingEntry, Microsoft.Extensions.Http, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].ViaFactory(LazyThreadSafetyMode mode)
at System.Lazy`1[[Microsoft.Extensions.Http.ActiveHandlerTrackingEntry, Microsoft.Extensions.Http, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
Here is how the httpFactory is set up in my Program file:
builder.Services
.AddHttpClient("BlazorClient.ServerApi", client => client.BaseAddress = new Uri(builder.Configuration.GetValue<string>("ServerApi:BaseAddress")))
.AddHttpMessageHandler<CorsRequestAuthorizationMessageHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("BlazorClient.ServerApi"));
Here is how the Factory is added in Program:
builder.Services.AddApiAuthorization()
.AddAccountClaimsPrincipalFactory<ClaimsPrincipalFactory>();
What is the correct way of doing this? I've been stuck on this issue for literally days and it doesn't seem like it should be this hard (and so hard to find documented info on it).
I was strugling with the same issue and based on your code snippet I might solved it.
What I did is to pass a HttpClientFactory to the generator of the CustomUserFactory, then in the CreateUser func I can create my userService with this factory.
Hope it's an ok solution and helps you as well.
public class CustomUserFactory : AccountClaimsPrincipalFactory<CustomUserAccount>
{
private IUserService _userService { get; set; }
private IHttpClientFactory _httpClientFactory { get; set; }
public CustomUserFactory(IAccessTokenProviderAccessor accessor, IHttpClientFactory httpClientFactory)
: base(accessor)
{
_httpClientFactory = httpClientFactory;
}
public async override ValueTask<ClaimsPrincipal> CreateUserAsync(CustomUserAccount account, RemoteAuthenticationUserOptions options)
{
var initialUser = await base.CreateUserAsync(account, options);
_userService = new UserService(_httpClientFactory);
...

Why is the Seed method called again in my EF code first migrations scenario?

I have a EF code first project and there is how I seed the database
internal sealed class Configuration : DbMigrationsConfiguration<myDB>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
ContextKey = "myDB.Auth.Service.DAL.myDB";
}
protected override void Seed(myDBdb)
{
var mProduct = new Product
{
Name = "default product",
CreatedDate = DateTime.Now
};
db.Products.AddOrUpdate(mProduct);
db.SaveChanges();
}
}
I have a wcf service that uses above code. What I realise is that every time I restart the wcf service (either from visual studio or IIS), above code is get called. As a result, multiple "default product" are added into the database, anyone knows why that happened?
Migration seed runs after every update-database so you need to make your script idempotent by testing for existance or using AddOrUpdate. If you only want to seed on database creation, there is a separate context seed method that only runs when the database is created.
https://blog.oneunicorn.com/2013/05/28/database-initializer-and-migrations-seed-methods/
AddOrUpdate for seeding
Edit:
When you use MigrateDatabaseToLatestVersion initializer, your seed method runs every time your application runs. If you want to control this process, switch your initializer to null:
Database.SetInitializer(new NullDatabaseInitializer<ApplicationDbContext>());
And then just manually run migrations when needed. To take it a step further, you can write your own initializer and do what you want when either the database does not exist or the database needs updating:
Database.SetInitializer(new ValidateDbInitializer<ApplicationDbContext>());
// ref: https://coding.abel.nu/2012/03/prevent-ef-migrations-from-creating-or-changing-the-database/
public class ValidateDbInitializer<TContext> : IDatabaseInitializer<TContext>
where TContext : ApplicationDbContext
{
public void InitializeDatabase(TContext context)
{
if (!context.Database.Exists())
{
throw new InvalidOperationException("The database does not exist. Check your server and connection string.");
}
if (!context.Database.CompatibleWithModel(true))
{
throw new InvalidOperationException("The database is not up to date. You may need to apply update(s).");
}
}
}
First step is to use the Tools menu, select Library Package Manager, then select Package Manager Console. In the Package Manager Console window type the below command.
Enable-Migrations
which will adds folder named as Migrations in your project and also a code file called as Configuration.cs.
in Configuration.cs type the below line
using yourprojectname.Models;
protected override void Seed(yourprojectname.Models.MyServiceContext context)
{
context.MyDB.AddOrUpdate(x => x.Id,
new MyData() { Name = "Mohit", CreatedDate= "14/05/2016" },
new MyData() { Name = "Prabhat", CreatedDate= "15/05/2016" },
);
}
Now type Update-Database
in Package Manager Console window
Try the following:
protected override void Seed(myDBdb)
{
var mProduct = new Product
{
Id = 1,
Name = "default product",
CreatedDate = DateTime.Now
};
db.Products.AddOrUpdate(mProduct);
db.SaveChanges();
}
When you are using the application for initialization the Data for the first time, please use DropCreateDatabaseAlways. e.g. :
public class MyClass : DropCreateDatabaseAlways<connectionstringContextName>
{
protected override void Seed(MyContext context)
{
// Your seed data
}
}

Asp.Net Identity 2.0: The entity type User is not part of the model for the current context

After updating to latest Asp.Net Identity (from 1.0 to 2.0) i got an exception on CRUD of a user from DB:
The entity type User is not part of the model for the current context
// for example here:
var manager = Container.Resolve<UserManager<User>>();
IdentityResult result = manager.Create(user, "Test passwd");
public class User : IdentityUser
{
// Some properties
}
public class AppDbContext : IdentityDbContext<User>
{
static AppDbContext()
{
// Without this line, EF6 will break.
Type type = typeof (SqlProviderServices);
}
public AppDbContext()
: this("DBName")
{
}
public AppDbContext(string connectionString)
: base(connectionString)
{
}
...
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Entity<User>()
.HasMany(c => c.SkippedTopics)
.WithMany(i => i.Users)
.Map(t =>
t.MapLeftKey("User_Id")
.MapRightKey("Topic_Id")
.ToTable("UserSkippedTopics"));
...
}
Before the update the code worked correctly. Any ideas?
UPDATE
The problem was in Unity configuration for UserManager types.
Fix: i created empty AppUserStore: UserStore and registered it as IUserStore:
public class AppUserStore : UserStore<User>
{
public AppUserStore(AppDbContext context) : base(context)
{
}
}
...
public static void RegisterTypes(IUnityContainer container)
{
...
_container.RegisterType<AppDbContext, AppDbContext>(new InjectionConstructor());
_container.RegisterType<IUserStore<User>, AppUserStore>();
}
shouldn't there be UserStore somewhere? I mean looks like you're using Unity for DI which I've never used so I don't know how it works but normally to instantiate UserManager you do something like this:
var manager = new UserManager<User>(new UserStore<User>(new AppDbContext()));
The same thing happened to me and it sucks. You need to enable-immigration and update your database from the Nuget manager console.
Check out this blog. Link is here
This just happened to me also when upgrading from Identity 1.0 to 2.0. The identity code scaffold by Visual Studio (such as the AccountController, ApplicationOAuthProvider, Startup.Auth.cs, etc) was generated when I created the project in Asp.Net Identity 1.0. It seems that Visual Studio had an update and now generates slightly different code for Identity 2.0.
The solution for me was to generate a new web app and copy over all the (new) generated code into the project I just upgraded, replacing the old generated code.

Add role in ASP.NET Identity

How can I add a Role in the new ASP.NET Identity system (1.0)?
There is a UserStore class but no RoleStore class.
I can't find any documentation on this issue.
RoleManager = new RoleManager<IdentityRole>(
new RoleStore<IdentityRole>(new MyDbContext()));
var roleresult = RoleManager.Create(new IdentityRole(roleName));
Starting with the .NET Framework 4.5, Windows Identity Foundation (WIF) has been fully integrated into the .NET Framework.
I would advice to examine the possibility, in my opinion the preferred, to implement Authorization through Claims (Expressing Roles as Claims).
When the IsInRole() method is called, there is a check made to see if the current user has that role.
In claims-aware applications, the role is expressed by a role claim type that should be available in the token.
The role claim type is expressed using the following URI:
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
So from the UserManager you can do something like this (without the RoleManager):
var um = new UserManager();
um.AddClaimAsync(1, new Claim(ClaimTypes.Role, "administrator"));
Claims can simplify and increase the performance of authentication and authorization processes.
You can use the roles stored as claims to eliminate back-end queries every time authorization takes place.
Using Claims you will not need the RoleStore anymore (at least for the equivalent authorization purposes...)
I used below snippets in one sample asp.net web page page_load for starting to grasp the way ASP Identity works
UserManager userManager = new UserManager();
var roleStore = new RoleStore<IdentityRole>(new ApplicationDbContext());
var roleManager = new RoleManager<IdentityRole>(roleStore);
var applicationRoleAdministrator = new IdentityRole("superadmin");
if (!roleManager.RoleExists(applicationRoleAdministrator.Name))
{
roleManager.Create(applicationRoleAdministrator);
}
ApplicationUser applicationUserAdministrator = userManager.FindByName(User.Identity.Name);
if (!userManager.GetRoles(applicationUserAdministrator.Id).Contains("superadmin"))
{
Response.Redirect("~/account/login.aspx?ReturnUrl=" + Request.Url.AbsolutePath);
}
Of course below ApplicationDbContext is automatically generated with ASP.NET 4.5+ templates like below
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
}
Also Create application Role Manager class too
public class ApplicationRoleManager : RoleManager<IdentityRole>
{
public ApplicationRoleManager(IRoleStore<IdentityRole, string> roleStore)
: base(roleStore)
{
}
public static ApplicationRoleManager Create(IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context)
{
//return new ApplicationRoleManager(new RoleStore<IdentityRole>(context.Get<ApplicationDbContext>()));
return new ApplicationRoleManager(new RoleStore<IdentityRole>(new ApplicationDbContext()));
}
}
also add below line in your startup.Auth.cs => ConfigureAuth(IAppBuilder app) method
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
And then in your controller:
private ApplicationRoleManager _roleManager;
public ApplicationRoleManager RoleManager
{
get
{
return _roleManager ?? HttpContext.GetOwinContext().Get<ApplicationRoleManager>();
}
private set
{
_roleManager = value;
}
}
I am new to this Identity Stuff and I am not sure if it is necessary or I am doing it clean and right, but these steps worked for me
ASP.NET identity is claims aware with respect to roles. That really confused me because in the previous system you configured membership and role providers in web.config.
The issue for me is that I have code like this:
HttpContext.Current.User.IsInRole("some role")
Fortunately, this logic still works. You can see the logic in the CreateAsync function in ClaimsIdentityFactory.cs which is in Microsoft.AspNet.Identity.Core. One of the arguments is UserManager. It asks it if it SupportsUserRole and if so then it calls GetRolesAsync and adds each role as a claim to the ClaimIdentity. There is no need to do this yourself.
IsInRole uses claims as described here:
http://msdn.microsoft.com/en-us/library/hh545448.aspx

Mocking User.Identity in ASP.NET MVC

I need to create Unit Tests for an ASP.NET MVC 2.0 web site. The site uses Windows Authentication.
I've been reading up on the necessity to mock the HTTP context for code that deals with the HttpContext. I feel like I'm starting to get a handle on the DI pattern as well. (Give the class an attribute of type IRepository and then pass in a Repository object when you instantiate the controller.)
What I don't understand, however, is the proper way to Mock the Windows Principal object available through User.Identity. Is this part of the HttpContext?
Does any body have a link to an article that demonstrates this (or a recommendation for a book)?
Thanks,
Trey Carroll
I've used IoC to abstract this away with some success. I first defined a class to represent the currently logged in user:
public class CurrentUser
{
public CurrentUser(IIdentity identity)
{
IsAuthenticated = identity.IsAuthenticated;
DisplayName = identity.Name;
var formsIdentity = identity as FormsIdentity;
if (formsIdentity != null)
{
UserID = int.Parse(formsIdentity.Ticket.UserData);
}
}
public string DisplayName { get; private set; }
public bool IsAuthenticated { get; private set; }
public int UserID { get; private set; }
}
It takes an IIdentity in the constructor to set its values. For unit tests, you could add another constructor to allow you bypass the IIdentity dependency.
And then I use Ninject (pick your favorite IoC container, doesn't matter), and created a binding for IIdentity as such:
Bind<IIdentity>().ToMethod(c => HttpContext.Current.User.Identity);
Then, inside of my controller I declare the dependency in the constructor:
CurrentUser _currentUser;
public HomeController(CurrentUser currentUser)
{
_currentUser = currentUser;
}
The IoC container sees that HomeController takes a CurrentUser object, and the CurrentUser constructor takes an IIdentity. It will resolve the dependencies automatically, and voila! Your controller can know who the currently logged on user is. It seems to work pretty well for me with FormsAuthentication. You might be able to adapt this example to Windows Authentication.
I don't know for MVC 2.0, but in newer versions you can mock the ControllerContext:
// create mock principal
var mocks = new MockRepository(MockBehavior.Default);
Mock<IPrincipal> mockPrincipal = mocks.Create<IPrincipal>();
mockPrincipal.SetupGet(p => p.Identity.Name).Returns(userName);
mockPrincipal.Setup(p => p.IsInRole("User")).Returns(true);
// create mock controller context
var mockContext = new Mock<ControllerContext>();
mockContext.SetupGet(p => p.HttpContext.User).Returns(mockPrincipal.Object);
mockContext.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);
// create controller
var controller = new MvcController() { ControllerContext = mock.Object };
see also How to unit-test an MVC controller action which depends on authentification in c#?
Scott Hanselman shows in his blog how to use IPrincipal and ModelBinder to make easier to test the controller by mocking IPrincipal.
Example for mocking username and SID on MVC4.
The username and SID (Windows Authentication) in the following action should be tested:
[Authorize]
public class UserController : Controller
{
public ActionResult Index()
{
// get Username
ViewBag.Username = User.Identity.Name;
// get SID
var lIdentity = HttpContext.User.Identity as WindowsIdentity;
ViewBag.Sid = lIdentity.User.ToString();
return View();
}
}
I use Moq and Visual Studio Test Tools. The test is implemented as follows:
[TestMethod]
public void IndexTest()
{
// Arrange
var myController = new UserController();
var contextMock = new Mock<ControllerContext>();
var httpContextMock = new Mock<HttpContextBase>();
var lWindowsIdentity = new WindowsIdentity("Administrator");
httpContextMock.Setup(x => x.User).Returns(new WindowsPrincipal(lWindowsIdentity));
contextMock.Setup(ctx => ctx.HttpContext).Returns(httpContextMock.Object);
myController.ControllerContext = contextMock.Object;
// Act
var lResult = myController.Index() as ViewResult;
// Assert
Assert.IsTrue(lResult.ViewBag.Username == "Administrator");
Assert.IsTrue(lResult.ViewBag.Sid == "Any SID Pattern");
}
I've changed dev environment global.asax and Web.Config for use FormsAuth for force a specific user. The username uses the same WindowsAuth format. See:
public override void Init()
{
base.Init();
this.PostAuthenticateRequest +=
new EventHandler(MvcApplication_PostAuthenticateRequest);
}
void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
FormsAuthentication.SetAuthCookie("Domain\\login", true);
}
The Windows or Forms Auth shares the same login patterns.
The application will work with both Windows authentication and Form authentication.
To mock WindowsIdentity you can do the following:
var mockedPrincipal = new Mock<WindowsPrincipal>(WindowsIdentity.GetCurrent());
mockedPrincipal.SetupGet(x => x.Identity.IsAuthenticated).Returns(true);
mockedPrincipal.SetupGet(x => x.Identity.Name).Returns("Domain\\User1");
mockedPrincipal.Setup(x => x.IsInRole("Domain\\Group1")).Returns(true);
mockedPrincipal.Setup(x => x.IsInRole("Domain\\Group2")).Returns(false);
then use mockedPrincipal.Object to get the actual WindowsIdentity

Resources