asp.net Identity Roles organisation unit structure creation - c#

Is it possible to put users created from ASP.NET Identity Roles into an organisational unit as you can in Active Directory? I want to be able to allow users to be put into departments where managers of that department can access other users' data only within their own departments. For example, an HR manager should be able to access all of the HR employees' data but not operations or IT employees. I then want to allow managers higher up access to everyone's data. Does ASP.NET's roles and identity allow for this?
Should I use the claims to put users into departments and use policy to add people to management?
It should act as a tree structure, similar to what you can get in Active Directory. However, I can not find an option in asp.net identity that allows the option of organisations.

Yes, it is possible. Asp.NET identity allow you to use classes inherited from IdentityUser or IdentityRole
For example you can add support for User Hierarchy by creating a class that inherits from IdentityUser
public class AppUser : IdentityUser
{
public string ManagerId { get; set; }
public AppUser Manager { get; set; }
public ICollection<AppUser> DirectSubordinates;
}
Then change your ApplicationDbContext to inherit from IdentityDbContext<AppUser> instead of from default IdentityDbContext
public class ApplicationDbContext : IdentityDbContext<AppUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
After changing the hierarchy use dotnet-ef tool to add migration then update your database.
You can also uses a class inherited from IdentityRole as follows:
public class ApplicationDbContext : IdentityDbContext<AppUser, AppRole, string>
where AppRole is a class inheriting from IdentityRole

Related

How to add tables and relations to generated Asp.Net Core MVC Identity database?

I have a project where I have to make an ASP.NET Core MVC Web Application in which I should include user sign up, login, and an SQL database with basic CRUD operations and corresponding views. For login and signup I decided to use Asp.Net Core Identity following this tutorial: https://youtu.be/CzRM-hOe35o. Everything works and looks fine for signup and login but I can't figure out how to use my database with the database generated by Identity.
My idea for a project database was to have a table for a User and Article (there are more tables but I'm going to keep it simple to explain my problem) with one to many relation. Now that I have a generated database for all things about Users(from Identity tutorial) how can I include this Article table in the databse and make a one to many relation between AspNetUsers and Article?
Or can I use two databases in my project, one with Users and the other one with other project tables? But then how to make relations between tables from different databases, and is it even possible?
First of all, you should know that you can use two databases but never on this case.
To do what you want, follow those steps:
Step 1 - Create a class called "User" in project folder called "Data". Class will be like this:
public class User : IdentityUser
{
}
Step 2- Create another class called "Article" in the same folder called "Data".
public class Article
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public virtual User User { get; set; }
}
Step 3 - Go to "User" class that you created in the step one and edit it to look like this
public class User : IdentityUser
{
public virtual ICollection<Article> Articles { get; set; }
}
Step 4 - In the same folder where you added those two classes you have another class called "ApplicationDbContext". Open it and register User and Article classes.
public class ApplicationDbContext : IdentityDbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Article> Articles { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
Step 5 - delete in your local database the existing db generated from your project before.
After that "Add-migration" and "Update-database"
For more information about how Entity Framework Core works see the link : https://www.learnentityframeworkcore.com/conventions/one-to-many-relationship#:~:text=The%20easiest%20way%20to%20configure,public%20class%20Author

Asp.net core role based access identity / roles make user table self referencing

Hi I am trying to model an access control system where access is granted by role to certain actions. im trying to use asp.net core with identity and roles. it seems the default identity comes with its own tables for users. The users table i have need to be self referencing as a users will have managers who are also users.
is it possible to make the default table that comes with identity do that?
If you want to add additional properties to the already existing IdentityUser, all you have to do is inherit the IdentityUser class and then add your own properties then you go to the startup file and your dbcontext and do the following
public class InheritedUser : IdentityUser
{
public bool HasTwoHeads { get; set;}
}
your startup
services.AddDefaultIdentity<InheritedUser>()
.AddEntityFrameworkStores<CustomDbContext>() .AddDefaultTokenProviders()
.AddDefaultUI(Microsoft.AspNetCore.Identity.UI.UIFramework.Bootstrap4);
Then your Inherited DbContext
public class InheritedDbContext : IdentityDbContex<InheritedUser>
{
}

Add relationships to the ApplicationUser class in ASP.NET Identity (Database First)

I'm using ASP.NET Identity (Database First) in my ASP.NET MVC application. I followed the instructions here, to set up the ASP.NET Identity with database first approach.
My AspNetUsers table has a relationship with the Employee table (The Employee table has a UserId foreign key, and the AspNetUsers entity has an ICollection<Employee> property).
I would like to add the ICollection<Employee> property to the ApplicationUser, like below:
public class ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public ICollection<Employee> Employees { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, int> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
But, when I do that, I get the following error message:
EntityType 'AspNetUserLogins' has no key defined. Define the key for
this EntityType. AspNetUserLogins: EntityType: EntitySet
'AspNetUserLogins' is based on type 'AspNetUserLogins' that has no
keys defined.
Why am I getting this error message? How can I fix that?
I cannot reproduce the issue, even when I create the tables in another database without keys and relations. So I'm sure that there is a problem with your model. Unfortunately you didn't add code which I can compare, so I can't tell what is different and answer the question directly. The only thing I can do is to show what works for me. However, first I have some remarks.
I think you shouldn't follow the article. As there is no reason to add the context to an existing database.
Like Ivan Stoev mentioned you are not supposed to mix contexts. The Identity context is meant to authenticate the user. It stores the credentials, the roles of the user and claims. Where claims are meant to add identity information about the user.
In fact, the default Hometown field of the ApplicationUser template can be removed, as it is an identity claim which should be stored in the AspNetUserClaims table. Not something you need to extend the ApplicationUser for. Actually I can't think of any reason to extend the ApplicationUser.
About the roles, these are not really claims, as they tell nothing about the identity but rather are used for authorization. That's why it's fine that they are stored in the AspNetUserRoles table. Unfortunately roles are added to the identity as role claims, which makes things confusing.
Please note that the Identity information is present in the claims. This means that the application doesn't have to call the Identity context. E.g. User.IsInRole checks the role claims of the current identity, not the roles stored in the table.
About the different contexts, the other context (which I usually call the business model) has nothing in common with the Identity context. Email and other fields are not part, nor have meaning to the business model. You may think that those fields are redundant, but in fact they are not. I could login using a google account, but for the business use my work email address.
There are several reasons to keep the context seperated.
Seperation of concerns. Suppose you want to exchange the authentication framework in the future with another one. Like implement IdentityServer in case you want to support single sign-on (SSO).
You can't move the users table to another database if another application needs the same logins. So you'll end up adding other contexts as well to the database.
Trouble with migrations. If you mix the contexts then migrations will fail.
It'll makes things far more easier. This is the first problem you've encountered, not the last.
As also mentioned in the article:
At this point if you need to add any relationships (E.g. foreign keys)
from your own tables to these tables you are welcome to do so but do
not modify any of the Entity Framework 2.0 tables directly or later on
any of their POCO classes. Doing so will result in errors based upon
feedback I’ve received.
So how to manage the information if you shouldn't access the identity context from your application?
For the current user you don't need to access the users table. All the information is present in the identity claims. The only reason to access the identity context is to allow a user to login. Besides user management.
You can suffice by adding a reference to the user (the userid). If you need to show information of other users (like name) in a report, then create a user table in your business context to store the information. You can add relations to this table, as it is part of the same context.
Please let me know if you have questions about this approach.
Now the code that works for me. Like others have mentioned, it is not likely that adding the line:
public ICollection<Employee> Employees { get; set; }
is the cause. Without the virtual keyword I think it is even ignored (remains null).
When I follow the steps of the article then I end up with the following model:
public class ApplicationUser : IdentityUser
{
public string Hometown { get; set; }
//public virtual ICollection<Employee> Employees { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
// Disable migrations
//Database.SetInitializer<ApplicationDbContext>(null);
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
And then I add the Employee class and uncomment the line in the ApplicationUser class above:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
//public virtual ApplicationUser ApplicationUser { get; set; }
public string ApplicationUserId { get; set; }
}
In the database I added the table:
CREATE TABLE [dbo].[Employees](
[Id] [int] NOT NULL,
[Name] [varchar](50) NOT NULL,
[ApplicationUserId] [nvarchar](128) NOT NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
You can use the [ForeignKey] attribute to use a different field name.
You can try this or choose to keep both contexts seperated instead.
Concern: I know exactly what your burden is here. Yes, Microsoft, an esoteric cult, did very poor job in providing the information to this matter that is creating a relationship with the Identity (Entity Framework).
Contribution: Ruard van Elburg post on Aug 24 at 16:31 gives good insight on this matter; however, there was one key component that I noticed was missing in his code which was DbSet that needed to be placed in the DBContext of IdentityModels.
Tech Stack:
I provide my tech stack so that if this does not work with older versions of software, you will know what I used to get this issue resolved.
Visual Studio 2017 MVC 5. FYI, MVC 5 is built in into most recent VS.
SQL Server 17
MS SQL Management Studio 17
Solution:
Disclaimer!!! I understand that the concern is for database first; however, this solution is only for code first approach. But hey, it works!
Here I provide a walk through on how to do this. Please make sure you have all the dependencies in top margin of your code.
Step 1: Add
public virtual DbSet<ModelNameOfInterest> ModelNameOfInterest { get; set; } to public class ApplicationDbContext : IdentityDbContext<ApplicationUser>{} as seen in code below.
using System.Data.Entity;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System.ComponentModel.DataAnnotations.Schema;
namespace AwesomeCode.Models
{
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
//A virtul DbSet in order to interact with the autogenerated code the identity framewrok produces.
public virtual DbSet<ModelNameOfInterest> ModelNameOfInterest { get; set; }
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
}
Step 2: Add public virtual ApplicationUser ApplicationUser { get; set; } to your model that you want to create a relationship with as seen code below.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Web;
namespace AwesomeCode.Models
{
public class WorkExp
{
[Key]
public int Id { get; set; }
public string JobTitle { get; set; }
//Create foreign key with reference to ApplicationUser_Id that was auto-generated by entity framework.
public virtual ApplicationUser ApplicationUser { get; set; }
}
}
Step 3: Given that you set up your connection string for your database, you need to produce a migration. Path to Package Manager Console: Tools->NuGet Packer Manager->Package Manager Console
Enable a migration if migration folder is absent in root: After PM>, type Enable-Migrations you should see a migration folder with two files.
Once migration is enabled: After PM>, type Update-Database You should see tables in your database now.
To add another migration: After PM>, type Add-Migration After Name:, type InitialCreate or Your model of interest You should see tables in your database now. You should see tables in your database now.
Step 4: Double check that the model of interest's foreign key is properly referenced to the AspNetUser table. In MS Management Studio, you can create a relational diagram to show the references. You can find how to do that on google.
Step 5: As always stay cool, calm, and collected.

User.Identity.Name or GetUserName not working on MVC 5

I'm working on an MVC system where I have achieved to put the log in view as the initial page to show (like a splash screen) and once the users have successfully log in redirect them to the main view.
What I need to do next is:
One the users are in the main view, there is a section where I try to show the user name. For example: John Smith. To achieve this I'm using the following:
User.Identity.Name
or
User.Identity.GetUserName
But none of them seems to work. The name is not displayed.
Looking into the tables that were generated when I created my MVC project from VS, AspNetUser table doesn't have any column for the Name, last name or full name. Instead it has some columns and one of them is UserName which store the data like this: john.smith.
So, in order to display the Name and last name of the user should I created extra column on that table and update the register view to enter those kind of data?
Thanks in advance
First of all if you have properly set up ASP .NET Identity or if you are using project starter template which comes with ASP .NET Identity then following should work in the View:
#User.Identity.Name
#User.Identity.GetUserName()
Also If you want to add custom properties to your user then you will have to add those properties to ApplicationUser class or any other class inherited by IdentityUser and then use that class in IdentityDbContext...
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
}
Check this

Can I Use two ApplicationUser contexts for same application log in?

My question involves both a technical and a modeling issue.
I have a MCV 5 system where there will be a web front-end and also a mobile app(Android).
At some point the web user will be able to see a queue of mobile signed-on users.
Mobile accounts wont access web front-end neither web accounts the mobile app.
I have chosen Azure to provide all my system needs.
Modeling wise, should I create separate login systems(one more backend system for mobile login)?
If not, is the Asp.net Identity system capable of using two different logins, as I tought roughly something like this:
namespace Application.Models {
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
public virtual ICollection<ApplicationMobileClient> Clients { get; set; }
}
public class ApplicationMobileClient : IdentityUser
{
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
}
}
The web user model would have a collection of mobile clients model. Just like an photo/comments system. But I dont know(well I asume not) if the Identity can do something like this, handle two logins on same application. I could Maybe im just larning too much technologies/frameworks/metodologies/patters at the same time and Im just dont know what to do lol.
I can't see why you should need two different ApplicationUser. Because it's the same user regardless if he is on your website or signed in through your Android app. If you want to keep track of the users mobile sign ins then you can add a property to the ApplicationUser.
public class ApplicationUser : IdentityUser
{
public virtual ICollection<MobileSignIn> MobileSignIns { get; set; }
}
public class MobileSignIn
{
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{ }
}

Categories

Resources