using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Security.Claims; using JetBrains.Annotations; using Microsoft.AspNetCore.Identity; using Volo.Abp.Domain.Entities; using Volo.Abp.Guids; using Volo.ExtensionMethods.Collections.Generic; namespace Volo.Abp.Identity { //TODO: Properties should not be public! //TODO: Add Name/Surname/FullName? public class IdentityUser : AggregateRoot, IHasConcurrencyStamp { public const int MaxUserNameLength = 256; public const int MaxNormalizedUserNameLength = MaxUserNameLength; public const int MaxEmailLength = 256; public const int MaxNormalizedEmailLength = MaxEmailLength; /// /// Gets or sets the user name for this user. /// public virtual string UserName { get; set; } /// /// Gets or sets the normalized user name for this user. /// public virtual string NormalizedUserName { get; set; } /// /// Gets or sets the email address for this user. /// public virtual string Email { get; set; } /// /// Gets or sets the normalized email address for this user. /// public virtual string NormalizedEmail { get; set; } /// /// Gets or sets a flag indicating if a user has confirmed their email address. /// /// True if the email address has been confirmed, otherwise false. public virtual bool EmailConfirmed { get; set; } /// /// Gets or sets a salted and hashed representation of the password for this user. /// public virtual string PasswordHash { get; set; } /// /// A random value that must change whenever a users credentials change (password changed, login removed) /// public virtual string SecurityStamp { get; set; } /// /// A random value that must change whenever a user is persisted to the store /// public virtual string ConcurrencyStamp { get; set; } /// /// Gets or sets a telephone number for the user. /// public virtual string PhoneNumber { get; set; } /// /// Gets or sets a flag indicating if a user has confirmed their telephone address. /// /// True if the telephone number has been confirmed, otherwise false. public virtual bool PhoneNumberConfirmed { get; set; } /// /// Gets or sets a flag indicating if two factor authentication is enabled for this user. /// /// True if 2fa is enabled, otherwise false. public virtual bool TwoFactorEnabled { get; set; } /// /// Gets or sets the date and time, in UTC, when any user lockout ends. /// /// /// A value in the past means the user is not locked out. /// public virtual DateTimeOffset? LockoutEnd { get; set; } /// /// Gets or sets a flag indicating if the user could be locked out. /// /// True if the user could be locked out, otherwise false. public virtual bool LockoutEnabled { get; set; } /// /// Gets or sets the number of failed login attempts for the current user. /// public virtual int AccessFailedCount { get; set; } //TODO: Can we make collections readonly collection, which will provide encapsulation but can work for all ORMs? /// /// Navigation property for the roles this user belongs to. /// public virtual ICollection Roles { get; } = new Collection(); /// /// Navigation property for the claims this user possesses. /// public virtual ICollection Claims { get; } = new Collection(); /// /// Navigation property for this users login accounts. /// public virtual ICollection Logins { get; } = new Collection(); /// /// Navigation property for this users tokens. /// public virtual ICollection Tokens { get; } = new Collection(); protected IdentityUser() { } public IdentityUser(Guid id, [NotNull] string userName) { Check.NotNull(userName, nameof(userName)); Id = id; UserName = userName; ConcurrencyStamp = Guid.NewGuid().ToString(); } public void AddRole(IGuidGenerator guidGenerator, Guid roleId) { Check.NotNull(roleId, nameof(roleId)); if (Roles.Any(r => r.RoleId == roleId)) { return; } Roles.Add(new IdentityUserRole(guidGenerator.Create(), Id, roleId)); } public void RemoveRole(Guid roleId) { Check.NotNull(roleId, nameof(roleId)); if (!IsInRole(roleId)) { return; } Roles.RemoveAll(r => r.RoleId == roleId); } public bool IsInRole(Guid roleId) { Check.NotNull(roleId, nameof(roleId)); return Roles.Any(r => r.RoleId == roleId); } public void AddClaim([NotNull] IGuidGenerator guidGenerator, [NotNull] Claim claim) { Check.NotNull(guidGenerator, nameof(guidGenerator)); Check.NotNull(claim, nameof(claim)); Claims.Add(new IdentityUserClaim(guidGenerator.Create(), Id, claim)); } public void AddClaims([NotNull] IGuidGenerator guidGenerator, [NotNull] IEnumerable claims) { Check.NotNull(guidGenerator, nameof(guidGenerator)); Check.NotNull(claims, nameof(claims)); foreach (var claim in claims) { AddClaim(guidGenerator, claim); } } public void ReplaceClaim([NotNull] Claim claim, [NotNull] Claim newClaim) { Check.NotNull(claim, nameof(claim)); Check.NotNull(newClaim, nameof(newClaim)); var userClaims = Claims.Where(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type); foreach (var userClaim in userClaims) { userClaim.SetClaim(newClaim); } } public void RemoveClaims([NotNull] IEnumerable claims) { Check.NotNull(claims, nameof(claims)); foreach (var claim in claims) { RemoveClaim(claim); } } public void RemoveClaim([NotNull] Claim claim) { Check.NotNull(claim, nameof(claim)); Claims.RemoveAll(c => c.ClaimValue == claim.Value && c.ClaimType == claim.Type); } public void AddLogin([NotNull] IGuidGenerator guidGenerator, [NotNull] UserLoginInfo login) { Check.NotNull(guidGenerator, nameof(guidGenerator)); Check.NotNull(login, nameof(login)); Logins.Add(new IdentityUserLogin(guidGenerator.Create(), Id, login)); } public void RemoveLogin([NotNull] string loginProvider, [NotNull] string providerKey) { Check.NotNull(loginProvider, nameof(loginProvider)); Check.NotNull(providerKey, nameof(providerKey)); Logins.RemoveAll(userLogin => userLogin.LoginProvider == loginProvider && userLogin.ProviderKey == providerKey); } [CanBeNull] public IdentityUserToken FindToken(string loginProvider, string name) { return Tokens.FirstOrDefault(t => t.LoginProvider == loginProvider && t.Name == name); } public void SetToken(IGuidGenerator guidGenerator, string loginProvider, string name, string value) { var token = FindToken(loginProvider, name); if (token == null) { Tokens.Add(new IdentityUserToken(guidGenerator.Create(), Id, loginProvider, name, value)); } else { token.Value = value; } } public void RemoveToken(string loginProvider, string name) { Tokens.RemoveAll(t => t.LoginProvider == loginProvider && t.Name == name); } /// /// Returns the username for this user. /// public override string ToString() { return $"{base.ToString()} UserName = {UserName}"; } } }