Multitier architecture and Identity. Why to use UserManager and RoleManager? - c#

Microsoft Identity introduces UserManager<T> and RoleManager<T> classes to interact with AspNetUsers, AspNetRoles and the other database tables. But why we should use them? Maybe it is a better solution to scaffold the database and work with its tables directly using the Entity Framework and forget about classes UserManager<T> and RoleManager<T>?
Why am I asking?
I my application I want to follow the multitier architecture.
I started with creating the DTO-class for the user:
public class UserDto
{
public string Id { get; set; }
public string UserName { get; set; }
...
public List<RoleDto> Roles { get; set; }
}
public class RoleDto
{
public string Id { get; set; }
public string Name { get; set; }
...
}
Type IdentityUser I want to map into UserDto, type IdentityRole - into RoleDto.
As you can see, in UserDto I want to store roles of a user.
Now let's have a look at the IdentityUser type: link.
It contains a lot of properties, but it doesn't have roles of the user.
So, when mapping IdentityUser into the UserDto, I need to work with RoleManager<T> to get roles of the user. I think, it's a dirty solution.
My idea
We can forget about UserManager<T> and RoleManager<T> types. We can simply scaffold the database and work with it using the Entity Framework.
After scaffolding of database I have the following:
public partial class AspNetUser
{
public string Id { get; set; }
public string UserName { get; set; }
public string NormalizedUserName { get; set; }
public string Email { get; set; }
public string NormalizedEmail { get; set; }
public bool EmailConfirmed { get; set; }
public string PasswordHash { get; set; }
public string SecurityStamp { get; set; }
public string ConcurrencyStamp { get; set; }
public string PhoneNumber { get; set; }
public bool PhoneNumberConfirmed { get; set; }
public bool TwoFactorEnabled { get; set; }
public DateTimeOffset? LockoutEnd { get; set; }
public bool LockoutEnabled { get; set; }
public int AccessFailedCount { get; set; }
public virtual ICollection<AspNetUserClaim> AspNetUserClaims { get; set; }
public virtual ICollection<AspNetUserLogin> AspNetUserLogins { get; set; }
public virtual ICollection<AspNetUserRole> AspNetUserRoles { get; set; }
public virtual ICollection<AspNetUserToken> AspNetUserTokens { get; set; }
}
// AspNetUserClaim, AspNetUserLogin, AspNetUserRole and AspNetUserToken
I introduced only AspNetUser class to be short. As you can see, it has a property AspNetUserRoles - roles of the user. It's great, because now it's really simple to map this class into the UserDto.
Question
Is it a good idea to scaffold the database and don't work with UserManager and RoleManager classes?
Maybe you can introduce a better solution?

Why do you think that this is a good idea? What would you gain by re-writing something like identity?
From the Introduction to Identity on ASP.NET Core
ASP.NET Core Identity:
Is an API that supports user interface (UI) login functionality.
Manages users, passwords, profile data, roles, claims, tokens, email confirmation, and more.
It's not just database access. It is also code that manages login functionality, secure token creation, secure password management and much more.
You need to take all of the above into consideration if you create a custom system, have an external auditor to pen-test your solution (even though this is a good idea whatever choice you make), unit test, performance test etc.
All the above is already done. You can easily customize the identity with various hook points too.
BTW, identity uses ef to access the datastore already by default.
Do structure your multilayer application, but leave identity out of it. It is a horizontal concern and it's presence is there to simplify your development and let you worry about your business needs only.

Related

ASP.Net MVC application with multiple users

I am creating an ASP.Net MVC application related to design, buildings and etc. I need to have two types of users - Normal People and Companies. Here I read how I can do it. I have created basic class ApplicationUser. The classes PersonUser and CompanyUser inherit this class and have some more properties. I am using MSSQL Database. I have thee following questions:
(My main question) Should I create DbSet<PersonUser> and DbSet<CompanyUser> or I should create DbSet<ApplicationUser>?
When I create database connections should the foreign key point to PersonUser and CompanyUser or to ApplicationUser?
In controllers when I user UserManager<TUser> or SignInManager<TUser> depending on the user I need to use different properties. Do I have to have two UserManagers or I should parse like that every time var user = (PersonUser) await this.userManager.GetUserAsync(this.User);?
Here are my classes:
public class ApplicationUser : IdentityUser, IAuditInfo, IDeletableEntity
{
public ApplicationUser()
{
this.Id = Guid.NewGuid().ToString();
this.Roles = new HashSet<IdentityUserRole<string>>();
this.Claims = new HashSet<IdentityUserClaim<string>>();
this.Logins = new HashSet<IdentityUserLogin<string>>();
this.Notifications = new HashSet<Notification>();
}
// Audit info
public DateTime CreatedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
// Deletable entity
public bool IsDeleted { get; set; }
public DateTime? DeletedOn { get; set; }
public virtual ICollection<IdentityUserRole<string>> Roles { get; set; }
public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
public virtual ICollection<IdentityUserLogin<string>> Logins { get; set; }
[Required]
public string Password { get; set; }
public UserType UserType { get; set; }
public virtual ICollection<Notification> Notifications { get; set; }
}
public class PersonUser : ApplicationUser
{
public PersonUser() : base()
{
this.Bids = new HashSet<Bid>();
this.Buildings = new HashSet<Building>();
}
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public DesignerType DesignerType { get; set; }
public virtual ICollection<Bid> Bids { get; set; }
public virtual ICollection<Building> Buildings { get; set; }
}
public class CompanyUser : ApplicationUser
{
public CompanyUser() : base()
{
}
[Required]
public string CompanyName { get; set; }
// That is like Id of the company
[Required]
public string Bulstat { get; set; }
}
There's two different scopes of problems related to having different "Types" of users in your application.
Permissions / Authorization
This is the scope for what different types of Users are allowed to do. What endpoints are they allowed to call? What are they allowed to acces?
For this problem scope you want to use IdentityRoles in order to distinguish said users
Database Relationships
This is the scope when the different types of Users will have genuinely distinct links in the database to different tables. For example for your SchoolDatabase, you may have TeacherUsers which have One to Many relationship with StudentUsers, and vise versa, but both types of users can login.
In this case you actually want to have different classes to distinguish them that inherit from a base User class.
Table links both types of users share, for example perhaps both a Teacher and a Student have a Classroom they belong to, you would put on the base class. Also chances are things like Password, Login, UserName, Email, etc would all go on the BaseUser class since everyone has such things.
Table links that are *uniqueto one type of user would go on that users table. So if Teachers have aCurriculum` then you would add a link between Teacher<->Curriculum, not BaseUser<->Curriculum, and if Students have Homework (but Teachers dont), then that link would be to the Student table specifically.

How Do I Determine if a Child or GrandChild Class Needs a TenantId?

Let's say you have a Tenant that has many Users that have many Accounts that have many transactions.
How far down the tree do you add a TenantId property?
How far down do the tree do you add a UserId?
Or do you only ever need to have the parent id?
Assume that the user will never intentionally access a child entity without first accessing it's parent. In a slug, it would be something like:
baseurl.com/accounts/{accountId/transactions/{transactionId}
public class Tenant
{
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<Account> Accounts { get; set; }
public virtual ICollection<Transaction> Transactions { get; set; }
}
public class User
{
public long Id { get; set; }
public long TenantId { get; set; }
public string Name { get; set; }
public virtual ICollection<Account> Accounts { get; set; }
}
public class Account
{
public long Id { get; set; }
public long TenantId { get; set; }
public string UserId { get; set; }
public virtual ICollection<Transaction> Transactions { get; set; }
}
public class Transaction
{
public long Id { get; set; }
public long TenantId { get; set; }
public string UserId { get; set; }
public string AccountId { get; set; }
}
I tend to see examples using TenantId on everything that falls under a tenant. I imagine this is for security, but my natural assumption would be that UserId would suffice. For exampple, even though Transaction is two levels below User, I don't believe I should allow anyone who know the Transaction ID to access a transaction without also being the user that owns the account.
The norm that we have followed for adding the TenantId is to all the main entities. for example, Users and Accounts should have the TenantId. Since the transaction is a dependent entity on both the User and Accounts and as such since it cannot be fetched with out referring to the base (Account / User), there is no mandate for a TenantId.
The rule of thumb is to classify the main business entity to be grouped / categorized by a tenant. (User, Accounts etc) so that when the data is being retrieved or updated in the database, the proper tenant filtering happens prior to any operation being performed on the mapping or child tables. Hence, based on the domain objects, please include the TenantId column wisely.

Using ASP.Net Identity with model classes

I'm new to ASP.Net Identity, and I'm looking for a good tutorial for using Identity in conjunction with other classes in my model.
As an example I have a basic Ratings class that looks like this (from a project not using Identity)
public class Rating
{
public int Id { get; set; }
public Product Product { get; set; }
public User User { get; set; }
public int Stars { get; set; }
public string Comment { get; set; }
public bool Active { get; set; }
}
And a User Class that looks a bit like this
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public ICollection<Rating> Ratings { get; set; }
}
Looking for a way to achieve the same with Identity. My test project is set up with MVC5, and code first
The recommended solution is to add the properties you need to the ApplicationUser class
You can also use your own "User table", in your case that would be the User class. You'd have to inherit from IdentityUser.
This article has examples of how to do both.
I agree with Rui.
Here is a site that will teach you How to Extend Identity Accounts and also Implement Based Authentication. When I was starting with Identity, that site taught me a lot.
As a related hint: Watch out when you implement a Unit of Work pattern in your project. ASP.NET identities datacontext needs to be the same as the Uow datacontext, otherwise the whole think will crash.
A good starting point may be this: ASP.NET Identity with Repository and Unit of Work

MVC Code First: One-to-many relationship between own model and SimpleMembership user

I am currently working on an event calendar website and I am still a novice when it comes to ASP.NET. I am using the MVC4 Framework, as well as the EntityFramework (CodeFirst) and the SimpleMembershipProvider, which comes with the MVC4 template. I am also using Migrations - If that is from any interest.
What I've got so far
I do currently have two models, with a one-to-many relationship, which work just fine:
_Events.cs (had to name it that way, because event is reserved)
public class _Event
{
public int _EventId { get; set; }
public string Name { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public virtual List<Comment> comments { get; set; }
}
Comment.cs
public class Comment
{
public int CommentId { get; set; }
public string Text { get; set; }
public int _EventId { get; set; }
public virtual _Event _Event { get; set; }
}
The problem
Now, I would like to add another one-to-many relationship between Comment and the User from the Membership model, but can't seem to figure out how to do so.
The goal I would like to archieve is, that I can have a list of commments for each event and print out the user information for each comment. I tried several things, but could not get it to work yet. My last attempt looks like this:
public class Comment
{
// snip - see above
public virtual UserProfile User { get; set; }
}
I would like to thank you very much for any help in advance.
You need to have UserId in your Comments as so.
public class Comment
{
// snip - see above
public int UserId {get; set;}
public virtual UserProfile User { get; set; }
}
You will also need to set the relationship between your Comment and your User so in your account controller have something like this.
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
In an ASP.net MVC project if you want to use default Microsoft Membership Provider, note that you need to implement membership system like role provider to have relationships and navigation and more functionalists between your created models and membership system. Microsoft Stores User related info at the separate place (like databases in App_Data folder) in your project.
So you need to store other models to Microsoft storage place, work with Microsoft functions directly and set connection string for this purpose OR implement Microsoft Membership to store User Info at relevant database like this NUGET Package that implements codefirst membership provider in C#. You can install this package and learn to write your own membership provider. More helps will be found by searching 'custom membership provider for MVC or aspnet membership provider'.

I need to create a complex relationship between two code-first entities but I'm having a hard time wrapping my brain around it

I have two entities and these two entities have two different relationships between one another. The first relationship is a one to many relationship and the second relationship is a many to many relationship. See the entities below.
public class User
{
public int Id { get; set; }
public string Password { get; set; }
public string Name { get; set; }
// Collection that represent this user's attached social providers.
public virtual ICollection<SocialProvider> SocialProviders { get; set; }
// Collection representing this user's friends from her attached social providers.
public virtual ICollection<SocialProvider> SocialContacts { get; set; }
}
public enum Provider
{
Google,
Facebook,
Twitter
}
public class SocialProvider
{
public int Id { get; set; }
public Provider Provider { get; set; }
// Granted access token for talking to the provider API on user's behalf.
public string AccessToken { get; set; }
// User's unique ID at the social provider.
public string ProviderId { get; set; }
// The user that this SocialProvider belongs to.
public virtual User User { get; set; }
// The users that have this social provider as a contact.
public virtual ICollection<User> Contacts { get; set; }
}
The workflow is as follows:
I sign up within the application. This creates a User.
I attach social providers (Google, Facebook, etc). This creates one or more SocialProvider and adds it to user.SocialProviders.
I import my contacts/friends from my attached social providers. This pulls down a list of social contacts and creates a SocialProvider for each one if it does not already exist and adds them to user.SocialContacts. In this case the SocialProvider will not have a parent user that "owns" it, nor will it have an AccessToken until a user signs up with the application and adds a social provider that matches it, in which case those things will be populated.
Will this work? I have a feeling EF is about to get very confused. I'm not very familiar with the fluent API or even most of the data annotations. Any suggestions for what I can do to make this work, or should I just make SocialContacts be a new entity called SocialContact that mostly looks like SocialProvider just to avoid any confusing relationship stuff?
Try this:
User
public class User
{
public int Id { get; set; }
ublic string Password { get; set; }
public string Name { get; set; }
// Collection that represent this user's attached social providers.
public virtual ICollection<SocialProvider> SocialProviders { get; set; }
// Collection representing this user's friends from her attached social providers.
public virtual ICollection<SocialProvider> SocialContacts { get; set; }
}
SocialProvider
public class SocialProvider
{
public int Id { get; set; }
public string AccessToken { get; set; }
[ForeignKey("Provider"), DatabaseGenerated(DatabaseGeneratedOption.None)]
public string ProviderId { get; set; }
public Provider Provider { get; set; }
[ForeignKey("User"), DatabaseGenerated(DatabaseGeneratedOption.None)]
public string UserId { get; set; }
// The user that this SocialProvider belongs to.
public virtual User User { get; set; }
// The users that have this social provider as a contact.
public virtual ICollection<User> Contacts { get; set; }
}
Then in your DbContext class
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasMany<SocialProvider>(r => r.SocialContacts)
.WithMany(u => u.Contacts)
.Map(m =>
{
m.ToTable("UsersInSocialContacts");
m.MapLeftKey("UserId");
m.MapRightKey("ProviderId");//If not ProviderId try Id
});
}
I can't and haven't test this, so let me know the results so i can improve it. Ideally, you may have to pick this design apart and restructure it into one or more entities.

Categories