ASP.Net Core 3 MVC web application with Identity
My DB context is
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{}
etc...
So Users, from IdentityUserContext is a public virtual DbSet, and is instantiated as
DbSet<Microsoft.AspNetCore.Identity.IdentityUser> Users
However, I have defined ApplicationUser : IdentityUser to define my own profile data.
public class ApplicationUser : IdentityUser
{
[Display(Name = "Is Manager")]
public bool IsSupervisor { get; set; }
etc....
So, I have configured services in startup.cs as:
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
I have set up my UserManager as
private readonly UserManager<ApplicationUser> _userManager
But when I try to use LINQ to access and filter User records based on my custom profile information (in the record 'Detail' call to the controler), I'm told that those custom attributes are not available:
var managers = new List<SelectListItem>();
managers.AddRange(_context.Users.Where(x => x.IsSupervisor == true).Select(manager => new SelectListItem
{
Text = manager.DisplayName,
Value = manager.Id.ToString()
}).ToList());
ViewBag.ManagersList = managers;
/Users/robert/Projects/mvc/Vacate/Controllers/EmployeesController.cs(59,59): Error CS1061: 'IdentityUser' does not contain a definition for 'IsSupervisor' and no accessible extension method 'IsSupervisor' accepting a first argument of type 'IdentityUser' could be found (are you missing a using directive or an assembly reference?) (CS1061) (Vacate)
So, without using a separate table to store profile information, is there a way to use LINQ against Users accessing your custom profile members? Sort of 'cast' Users to be the correct ApplicationUser type in the LINQ call? Or did I miss something when setting up the ApplicationUser class for use in Identity?
You should make your default ApplicationDbContext use the new ApplicationUser :
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
The update your database : Add-migration , Update-Database .
Related
This is how I add new column to Identity role table
public class ApplicationRole : IdentityRole
{
public ApplicationRole() : base() { }
public ApplicationRole(string name, long customId) : base(name)
{
this.CustomId= customId;
}
public virtual long CustomId{ get; set; }
}
In dbContext , I added
modelBuilder.Entity<ApplicationRole>().ToTable("AspNetRoles");
using this , new column CustomId is added successfully to AspNetRoles table using migration .
But when I have to call role table like
private readonly IdentityDbContext db_Identity;
..
..
db_Identity.Roles.Select(x=>x.CustomId)
I can't find newly added column CustomId here .
Is there any step I missed ?
Update
With #Chirs's answer , I can get CoustomId , but when I run , I got this error
Cannot create a DbSet for 'IdentityRole' because this type is not included in the model for the context
If you are using custom Identity models, you also need to inherit from one of the generic IdentityDbContext classes and specify your custom type in the appropriate type param. In this case, you just need something like:
public class MyDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
Then use your custom context class:
private readonly MyDbContext db_Identity;
I'm new on the asp net boilerplate framework, and i created a new mvc project multipage web application, without module zero.
I would like to use the AbpSession class that from what I understand has inside the user id that is taken over Thread.CurrentPrincipal.
However, I do not understand how to do after login, to save the user id in the Thread.CurrentPrincipal.
I've searched in the network and found several solutions, but in the AbpSession class the user id is always null.
The most optimal solution I found was this:
IList<Claim> claimCollection = new List<Claim>
{
new Claim(AbpClaimTypes.UserId, "5")
};
ClaimsIdentity claimsIdentity = new ClaimsIdentity(claimCollection);
var principal = new ClaimsPrincipal(claimsIdentity);
Thread.CurrentPrincipal = principal;
It's the first time I use principal and identity and despite being documented I did not quite understand how to use them with asp net boilerplate, and I did not find sample codes.
Do you know how to tell me the right way or tell me where to find some functional codes?
Thanks
Start expanding AbpSession
The last section has cleared the way of thinking. Let's roll up our sleeves and expand in this section.
AbpSession attributes have been injected into three base classes: Application Service, AbpController and ABP ApiController.
So we need to extend AbpSession at the domain level, which is the project at the end of. Core.
Now suppose we need to extend an Email attribute.
Extending IAbpSession
Locate the project at the end of. Core, add the Extensions folder, and then add the IAbpSession Extension interface inherited from IAbpSession:
namespace LearningMpaAbp.Extensions
{
public interface IAbpSessionExtension : IAbpSession
{
string Email { get; }
}
}
Implementing IAbpSession Extension
Add the AbpSession Extension class, which is based on Claims AbpSession and implements the IAbpSession Extension interface.
namespace LearningMpaAbp.Extensions
{
public class AbpSessionExtension : ClaimsAbpSession, IAbpSessionExtension, ITransientDependency
{
public AbpSessionExtension(
IPrincipalAccessor principalAccessor,
IMultiTenancyConfig multiTenancy,
ITenantResolver tenantResolver,
IAmbientScopeProvider<SessionOverride> sessionOverrideScopeProvider) :
base(principalAccessor, multiTenancy, tenantResolver, sessionOverrideScopeProvider)
{}
public string Email => GetClaimValue(ClaimTypes.Email);
private string GetClaimValue(string claimType)
{
var claimsPrincipal = PrincipalAccessor.Principal;
var claim = claimsPrincipal?.Claims.FirstOrDefault(c => c.Type == claimType);
if (string.IsNullOrEmpty(claim?.Value))
return null;
return claim.Value;
}
}
}
UserClaimsPrincipalFactory.cs
//Override CreateAsync method to add your custom claim
public override async Task<ClaimsPrincipal> CreateAsync(User user)
{
var claim = await base.CreateAsync(user);
claim.Identities.First().AddClaim(new Claim(ClaimTypes.Email, user.EmailAddress));
return claim;
}
Replace the injected AbbSession attribute
First replace the injected ABP Session in AbpController
Locate. ApplicationxxxControllerBase:AbpController. CS and inject IAbpSession Extension with attributes. Add the following code:
//AbpSession Hiding Parent Class
public new IAbpSessionExtension AbpSession { get; set; }
Replace the injected ABP Session in Application Service
Locate. ApplicationxxxAppServiceBase.cs. Introduce IAbpSession Extension with attributes, and add the following code as well:
//AbpSession Hiding Parent Class
public new IAbpSessionExtension AbpSession { get; set; }
Chaneg the injected ABP Session in Views AbpRazorPage
Locate. ApplicationxxxRazorPage.cs. Introduce IAbpSession Extension with attributes, and add the following code as well:
[RazorInject]
public IAbpSessionExtension AbpSession { get; set; }
Altough the question is very general, i would like to share you some code about how to add custom field to AbpSession in ASP.NET Core.
MyAppSession.cs
//Define your own session and add your custom field to it
//Then, you can inject MyAppSession and use it's new property in your project.
public class MyAppSession : ClaimsAbpSession, ITransientDependency
{
public MyAppSession(
IPrincipalAccessor principalAccessor,
IMultiTenancyConfig multiTenancy,
ITenantResolver tenantResolver,
IAmbientScopeProvider<SessionOverride> sessionOverrideScopeProvider) :
base(principalAccessor, multiTenancy, tenantResolver, sessionOverrideScopeProvider)
{
}
public string UserEmail
{
get
{
var userEmailClaim = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == "Application_UserEmail");
if (string.IsNullOrEmpty(userEmailClaim?.Value))
{
return null;
}
return userEmailClaim.Value;
}
}
}
UserClaimsPrincipalFactory.cs
//Override CreateAsync method to add your custom claim
public override async Task<ClaimsPrincipal> CreateAsync(User user)
{
var claim = await base.CreateAsync(user);
claim.Identities.First().AddClaim(new Claim("Application_UserEmail", user.EmailAddress));
return claim;
}
I'm using the SimpleMembership.MVC3 package with my MVC3 application and I want to be able to access users from the table through Entity Framework
In examples for doing this with MVC4, you can simply create a POCO to mirror the User table that's been generated, add your DbSet in your DbContext implementation and then query the DbSet like you normally would, ie: context.Users.
This collection is always returning 0 items for me even though there are rows in the table. What am I doing wrong? Here's what I got so far:
[Table("User")]
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class TestContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
}
}
In my service:
model.Accounts = context.Users.ToList();
Thanks!
You do not create your a POCO that mirrors the User table in order to access it directly from EF. There is already a POCO created by the Internet template when you created the project, which you can customize as described here. This same article also shows how you can access the user information by accessing EF directly. You do not create your own context, there is one already in place that you use. Here is a code snippet from that article.
var context = new UsersContext();
var username = User.Identity.Name;
var user = context.UserProfiles.SingleOrDefault(u => u.UserName == username);
var email = user.Email;
The article also has links to download the source code that demonstrates the details on how to do this.
I circumvented the membership classes entirely and implemented a pure EF membership system. I leveraged the System.Web.Helpers Crypto helpers to handle password hashing and just create the AuthCookie when needed.
I'm trying to make EntityFramework work with ASP .NET MVC3 using this tutorial:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application
Ok, I have my database, my .edmx model, model classes but one first thing I don't get is:
How does my DbContext derived class even know my .emdx model ? I don't fine where the "link" is created in this tutorial (maybe having several thing with the same name "SchoolContext", for the context as for the connexionstring is confusing ...)
When I run what I got for now with the code:
MMContext context = new MMContext();
List<EntityUser> testList = (from u in context.Users
select u).ToList();
I get:
System.Data.Edm.EdmEntityType: : EntityType 'EntityUser' has no key defined. Define the key for this EntityType.
System.Data.Edm.EdmEntitySet: EntityType: EntitySet �Users� is based on type �EntityUser� that has no keys defined.
Thank you for your help.
Assuming you are using the Code-First approach, you have to define a Key in your Users class:
public class User
{
public int Id { get; set; }
// ...
}
As mentioned from Kyle, if your ID field is not named "Id" you have to add the [Key] attribute:
using System.ComponentModel.DataAnnotations;
public class User
{
[Key]
public int u_Id { get; set; }
// ...
}
I've hit a bit of a wall and was hoping someone could point out where I'm going wrong.
I've been using Ninject to Inject into custom ActionFilterAttributes and this is working fine:
kernel.BindFilter<CriticalErrorAttribute>(FilterScope.Last, 1);
I'm now trying to inject into a custom AuthorizeAttribute. I've got the syntax correct so that I'm inserting the Role and custom attributes:
kernel.BindFilter<Authorisation>(FilterScope.Action, 0)
.WhenActionMethodHas<Authorisation>()
.WithPropertyValueFromActionAttribute<Authorisation>("Roles", n => n.Roles)
.WithPropertyValueFromActionAttribute<Authorisation>("Years", n => n.Years);
The attribute is being executed correctly and the roles and years are being inserted fine, my issue is that a service I'm trying to inject in is always null:
[Inject]
public IUserServices userService { get; set; }
In normal ActionFilterAttributes the service is injected fine, but here it isn't.
Any help would be appreciated
Instead of deriving from an attribute you should implement the corresponding interface e.g. IAuthorizationFilter or IActionFilter and use a different normal attribut to mark the controllers or actions for which you want to apply that filter
public class AuthorisationFilter : IAuthorizationFilter ....
public class Authorization : Attribute ....
kernel.BindFilter<AuthorisationFilter>(FilterScope.Action, 0)
.WhenActionMethodHas<Authorisation>()
.WithPropertyValueFromActionAttribute<Authorisation>("Roles", n => n.Roles)
.WithPropertyValueFromActionAttribute<Authorisation>("Years", n => n.Years);
I have very issue similar to the already asked question, but I can't solve it.
I am adding my custom authorization filters, which inside have access to the db context. The dbcontext is defined to be used InRequestScope (Ninject Binding). When I hit the point where the filter is executing I am getting an error that dbcontext is already been disposed.
void IAuthorizationFilter.OnAuthorization(AuthorizationContext filterContext)
{
....
var customerPermissions = _authorizationService.GetCustomersListForPermission(_userProvider.CurrentUser.Username,
this.PermissionEnums);
..
}
The aurhorization service asks the db for permissions for the current user - this means that Ninject should create a new dbcontext instance, but this dosn't happen...
I read that "The MVC framework itsself caches filters. " https://github.com/ninject/Ninject.Web.Mvc/wiki/Filters-and-Scoped
But can't get how to implement my filter bindings.
Currently these are the bindings that I have:
kernel.Bind<TDBContext>().ToSelf().InRequestScope();
kernel.Bind<IUnitOfWork>().To<UnitOfWork>();//.InThreadScope();
kernel.Bind<IUnitOfWorkManager>().To<UnitOfWorkManager>().InRequestScope();
#region UserAllCSPermissionBasedAuthFilter
kernel.BindFilter<UserAllCSPermissionBasedAuthFilter>(FilterScope.Action, 0)
.WhenActionMethodHas<UserAllCSPermissionBasedAuthFilter>()
.WithConstructorArgumentFromActionAttribute<UserAllCSPermissionBasedAuthFilter>("permissionEnums", att => att.PermissionEnums);
kernel.BindFilter<UserAllCSPermissionBasedAuthFilter>(FilterScope.Controller, 0)
.WhenActionMethodHas<UserAllCSPermissionBasedAuthFilter>()
.WithConstructorArgumentFromControllerAttribute<UserAllCSPermissionBasedAuthFilter>("permissionEnums", att => att.PermissionEnums);
#endregion
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class UserAllCSPermissionBasedAuthFilter : FilterAttribute, IAuthorizationFilter
{
#region Private Fields
private static readonly ObjectCache _permissionCache = MemoryCache.Default;
private static readonly ILog _log = LogManager.GetLogger(typeof(UserAllCSPermissionBasedAuthFilter));
[Inject]
public IAuthorizationService _authorizationService { get; set; }/// DependencyResolver.Current.GetService<IAuthorizationService>();