WebFileServer20

Доработка Identity и ApplicationDbContext: в User и Role

11/24/2020 9:40:25 PM

Details

diff --git a/Src/Tools.Identity.EF/BaseIdentityContext.cs b/Src/Tools.Identity.EF/BaseIdentityContext.cs
new file mode 100644
index 0000000..42f0747
--- /dev/null
+++ b/Src/Tools.Identity.EF/BaseIdentityContext.cs
@@ -0,0 +1,112 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Options;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
+
+using IdentityServer4.EntityFramework.Entities;
+using IdentityServer4.EntityFramework.Extensions;
+using IdentityServer4.EntityFramework.Interfaces;
+using IdentityServer4.EntityFramework.Options;
+
+
+using Tools.Identity.EF.Entities;
+
+namespace Tools.Identity.EF
+{
+
+#warning Подходит только для Identity Server 3.*
+
+    /// <summary>
+    /// Database abstraction for a combined <see cref="DbContext"/> using ASP.NET Identity and Identity Server.
+    /// Данный контекст используется для работы Identity и IndetityServer
+    /// </summary>
+    /// <typeparam name="TUser"></typeparam>
+    /// <typeparam name="TRole"></typeparam>
+    /// <typeparam name="TKey">Key of the IdentityUser entity</typeparam>
+    public class BaseIdentityContext<TKey, TUser, TRole, TUserRole>
+        :
+        IdentityDbContext<
+            TUser, 
+            TRole, 
+            TKey,
+            IdentityUserClaim<TKey>, 
+            BaseUserRole<TKey, TUser, TRole>, 
+            IdentityUserLogin<TKey>,        
+            IdentityRoleClaim<TKey>,       
+            IdentityUserToken<TKey>
+            >,
+        IPersistedGrantDbContext
+
+        where TKey : IEquatable<TKey>
+        where TUser : BaseUser<TKey, TUser, TRole>
+        where TRole : BaseRole<TKey, TUser, TRole>
+        where TUserRole : BaseUserRole<TKey, TUser, TRole>
+
+    {
+        private readonly IOptions<OperationalStoreOptions> _operationalStoreOptions;
+
+
+        /// <summary>
+        /// Initializes a new instance of <see cref="ApiAuthorizationDbContext{TUser, TRole, TKey}"/>.
+        /// </summary>
+        /// <param name="options">The <see cref="DbContextOptions"/>.</param>
+        /// <param name="operationalStoreOptions">The <see cref="IOptions{OperationalStoreOptions}"/>.</param>
+        public BaseIdentityContext
+        (
+            DbContextOptions options,
+            IOptions<OperationalStoreOptions> operationalStoreOptions
+        )
+            : base(options)
+        {
+            _operationalStoreOptions = operationalStoreOptions;
+        }
+
+
+        #region IPersistedGrantDbContext
+
+        /// <summary>
+        /// Gets or sets the <see cref="DbSet{PersistedGrant}"/>.
+        /// </summary>
+        public DbSet<PersistedGrant> PersistedGrants { get; set; }
+
+        /// <summary>
+        /// Gets or sets the <see cref="DbSet{DeviceFlowCodes}"/>.
+        /// </summary>
+        public DbSet<DeviceFlowCodes> DeviceFlowCodes { get; set; }
+
+        Task<int> IPersistedGrantDbContext.SaveChangesAsync() 
+            => base.SaveChangesAsync();
+
+        #endregion
+
+        /// <inheritdoc />
+        protected override void OnModelCreating(ModelBuilder builder)
+        {
+            base.OnModelCreating(builder);
+
+            builder
+                .ConfigurePersistedGrantContext(_operationalStoreOptions.Value);
+
+            //builder.Entity<UserRole<TKey, TUser, TRole>>()
+            //    .HasKey(t => new { t.UserId, t.RoleId });
+
+            builder.Entity<BaseUserRole<TKey, TUser, TRole>>()
+                .HasOne(sc => sc.User)
+                .WithMany(s => s.Roles)
+                .HasForeignKey(sc => sc.UserId)
+                .IsRequired();
+
+            builder.Entity<BaseUserRole<TKey, TUser, TRole>>()
+                .HasOne(sc => sc.Role)
+                .WithMany(c => c.Users)
+                .HasForeignKey(sc => sc.RoleId)
+                .IsRequired();
+        }
+    }
+
+}
diff --git a/Src/Tools.Identity.EF/Entities/BaseRole.cs b/Src/Tools.Identity.EF/Entities/BaseRole.cs
new file mode 100644
index 0000000..0b78536
--- /dev/null
+++ b/Src/Tools.Identity.EF/Entities/BaseRole.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Linq;
+using System.ComponentModel.DataAnnotations.Schema;
+
+using Microsoft.AspNetCore.Identity;
+
+namespace Tools.Identity.EF.Entities
+{
+    public class BaseRole<TKey, TUser, TRole>
+        : IdentityRole<TKey>
+
+        where TKey : IEquatable<TKey>
+        where TUser : BaseUser<TKey, TUser, TRole>
+        where TRole : BaseRole<TKey, TUser, TRole>
+    {
+        public virtual List<BaseUserRole<TKey, TUser, TRole>> Users { set; get; }
+
+
+        [NotMapped]
+        public IReadOnlyDictionary<TKey, TUser> UsersDict
+        {
+            get => Users
+                .ToDictionary(e => e.UserId, e => e.User);
+            set => Users = value
+                .Select(
+                    e => new BaseUserRole<TKey, TUser, TRole>()
+                    {
+                        UserId = e.Key,
+                        User = e.Value,
+
+                        RoleId = Id,
+                        Role = (TRole)this
+                    }
+                )
+                .ToList();
+        }
+
+
+
+        public BaseRole() { }
+        public BaseRole(string roleName) 
+            : base(roleName) { }
+    }
+
+}
diff --git a/Src/Tools.Identity.EF/Entities/BaseUser.cs b/Src/Tools.Identity.EF/Entities/BaseUser.cs
new file mode 100644
index 0000000..38048a4
--- /dev/null
+++ b/Src/Tools.Identity.EF/Entities/BaseUser.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text;
+
+using System.ComponentModel.DataAnnotations.Schema;
+
+using Microsoft.AspNetCore.Identity;
+
+namespace Tools.Identity.EF.Entities
+{
+    public class BaseUser<TKey, TUser, TRole>
+        : IdentityUser<TKey>
+
+        where TKey : IEquatable<TKey>
+        where TUser : BaseUser<TKey, TUser, TRole>
+        where TRole : BaseRole<TKey, TUser, TRole>
+    {
+        public virtual List<BaseUserRole<TKey, TUser, TRole>> Roles { set; get; }
+
+
+        [NotMapped]
+        public IReadOnlyDictionary<TKey, TRole> RolesDict
+        {
+            get => Roles
+                .ToDictionary(e => e.RoleId, e => e.Role);
+            set => Roles = value
+                .Select(
+                    e => new BaseUserRole<TKey, TUser, TRole>()
+                    {
+                        UserId = Id,
+                        User = (TUser)this,
+
+                        RoleId = e.Key,
+                        Role = e.Value
+                    }
+                )
+                .ToList();
+        }
+
+
+        public BaseUser() { }
+    }    
+
+}
diff --git a/Src/Tools.Identity.EF/Entities/BaseUserRole.cs b/Src/Tools.Identity.EF/Entities/BaseUserRole.cs
new file mode 100644
index 0000000..6d69bb5
--- /dev/null
+++ b/Src/Tools.Identity.EF/Entities/BaseUserRole.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Microsoft.AspNetCore.Identity;
+
+namespace Tools.Identity.EF.Entities
+{
+    public class BaseUserRole<TKey, TUser, TRole>
+            : IdentityUserRole<TKey>
+
+        where TKey : IEquatable<TKey>
+        where TUser : BaseUser<TKey, TUser, TRole>
+        where TRole : BaseRole<TKey, TUser, TRole>
+    {
+        public virtual TUser User { get; set; }
+
+        public virtual TRole Role { get; set; }
+    }
+
+}
diff --git a/Src/Tools.Identity.EF/Tools.Identity.EF.csproj b/Src/Tools.Identity.EF/Tools.Identity.EF.csproj
new file mode 100644
index 0000000..f6f12c2
--- /dev/null
+++ b/Src/Tools.Identity.EF/Tools.Identity.EF.csproj
@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="IdentityServer4" Version="3.1.4" />
+    <PackageReference Include="IdentityServer4.EntityFramework" Version="3.1.4" />
+    <PackageReference Include="Microsoft.AspNetCore.ApiAuthorization.IdentityServer" Version="3.1.9" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.9" />
+  </ItemGroup>
+
+</Project>
diff --git a/Src/WebFileServ.Model.DAL/DataBase/EF/ApplicationDbContext.cs b/Src/WebFileServ.Model.DAL/DataBase/EF/ApplicationDbContext.cs
index 78d2098..20b9958 100644
--- a/Src/WebFileServ.Model.DAL/DataBase/EF/ApplicationDbContext.cs
+++ b/Src/WebFileServ.Model.DAL/DataBase/EF/ApplicationDbContext.cs
@@ -5,19 +5,24 @@ using System.Threading.Tasks;
 
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Options;
+//using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
+//using Microsoft.AspNetCore.ApiAuthorization.IdentityServer;
 
 using IdentityServer4.EntityFramework.Options;
 
-using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
-
-using Microsoft.AspNetCore.ApiAuthorization.IdentityServer;
+using Tools.Identity.EF;
 
 using WebFileServ.Model.Entities.Identity;
 
 namespace WebFileServ.Model.DAL.DataBase.EF
 {
     public class ApplicationDbContext
-        : ApiAuthorizationDbContext<ApplicationUser>
+        : BaseIdentityContext<
+            Guid, 
+            ApplicationUser, 
+            ApplicationRole, 
+            ApplicationUserRole
+            >
     {
 
         public ApplicationDbContext(
@@ -38,6 +43,7 @@ namespace WebFileServ.Model.DAL.DataBase.EF
                 //Microsoft.EntityFrameworkCore.Proxies
                 .UseLazyLoadingProxies();
 
+
             base.OnConfiguring(optionsBuilder);
         }
         
diff --git a/Src/WebFileServ.Model.DAL/DataInit/DataInitializer.cs b/Src/WebFileServ.Model.DAL/DataInit/DataInitializer.cs
index 1090cb3..df782ca 100644
--- a/Src/WebFileServ.Model.DAL/DataInit/DataInitializer.cs
+++ b/Src/WebFileServ.Model.DAL/DataInit/DataInitializer.cs
@@ -24,7 +24,7 @@ namespace WebFileServ.Model.DAL.DataInit
         private readonly ApplicationDbContext Context;
 
         private readonly UserManager<ApplicationUser> UserManager;
-        private readonly RoleManager<IdentityRole> RoleManager;
+        private readonly RoleManager<ApplicationRole> RoleManager;
 
 
 
@@ -32,14 +32,17 @@ namespace WebFileServ.Model.DAL.DataInit
         public DataInitializer
             (
             DataInitializerConfig dataInitializerConfig,
+
             ApplicationDbContext context,
+
             UserManager<ApplicationUser> userManager,
-            RoleManager<IdentityRole> roleManager
+            RoleManager<ApplicationRole> roleManager
             )
         {
             DataInitializerConfig = dataInitializerConfig;
 
             Context = context;
+
             UserManager = userManager;
             RoleManager = roleManager;
         }
@@ -102,7 +105,7 @@ namespace WebFileServ.Model.DAL.DataInit
             if (adminRole == null)
             {
                 var createRoleResult = await RoleManager.CreateAsync(
-                    new IdentityRole(roleName)
+                    new ApplicationRole(roleName)
                     );
 
                 if (!createRoleResult.Succeeded)
@@ -136,7 +139,6 @@ namespace WebFileServ.Model.DAL.DataInit
                         );
                 }
 
-
                 adminUser = await UserManager
                     .FindByNameAsync(email);
 
@@ -149,6 +151,9 @@ namespace WebFileServ.Model.DAL.DataInit
                         addToRoleResult.Errors.FirstOrDefault()?.Description
                         );
                 }
+
+                var adminRoles = adminUser
+                    .RolesDict;
             }
         }
 
diff --git a/Src/WebFileServ.Model.DAL/Migrations/ApplicationDbContextModelSnapshot.cs b/Src/WebFileServ.Model.DAL/Migrations/ApplicationDbContextModelSnapshot.cs
index 287ecc0..89353d1 100644
--- a/Src/WebFileServ.Model.DAL/Migrations/ApplicationDbContextModelSnapshot.cs
+++ b/Src/WebFileServ.Model.DAL/Migrations/ApplicationDbContextModelSnapshot.cs
@@ -101,34 +101,7 @@ namespace WebFileServ.Model.DAL.Migrations
                     b.ToTable("PersistedGrants");
                 });
 
-            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
-                {
-                    b.Property<string>("Id")
-                        .HasColumnType("nvarchar(450)");
-
-                    b.Property<string>("ConcurrencyStamp")
-                        .IsConcurrencyToken()
-                        .HasColumnType("nvarchar(max)");
-
-                    b.Property<string>("Name")
-                        .HasColumnType("nvarchar(256)")
-                        .HasMaxLength(256);
-
-                    b.Property<string>("NormalizedName")
-                        .HasColumnType("nvarchar(256)")
-                        .HasMaxLength(256);
-
-                    b.HasKey("Id");
-
-                    b.HasIndex("NormalizedName")
-                        .IsUnique()
-                        .HasName("RoleNameIndex")
-                        .HasFilter("[NormalizedName] IS NOT NULL");
-
-                    b.ToTable("AspNetRoles");
-                });
-
-            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
+            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
                 {
                     b.Property<int>("Id")
                         .ValueGeneratedOnAdd()
@@ -141,9 +114,8 @@ namespace WebFileServ.Model.DAL.Migrations
                     b.Property<string>("ClaimValue")
                         .HasColumnType("nvarchar(max)");
 
-                    b.Property<string>("RoleId")
-                        .IsRequired()
-                        .HasColumnType("nvarchar(450)");
+                    b.Property<Guid>("RoleId")
+                        .HasColumnType("uniqueidentifier");
 
                     b.HasKey("Id");
 
@@ -152,7 +124,7 @@ namespace WebFileServ.Model.DAL.Migrations
                     b.ToTable("AspNetRoleClaims");
                 });
 
-            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
+            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
                 {
                     b.Property<int>("Id")
                         .ValueGeneratedOnAdd()
@@ -165,9 +137,8 @@ namespace WebFileServ.Model.DAL.Migrations
                     b.Property<string>("ClaimValue")
                         .HasColumnType("nvarchar(max)");
 
-                    b.Property<string>("UserId")
-                        .IsRequired()
-                        .HasColumnType("nvarchar(450)");
+                    b.Property<Guid>("UserId")
+                        .HasColumnType("uniqueidentifier");
 
                     b.HasKey("Id");
 
@@ -176,7 +147,7 @@ namespace WebFileServ.Model.DAL.Migrations
                     b.ToTable("AspNetUserClaims");
                 });
 
-            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
+            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
                 {
                     b.Property<string>("LoginProvider")
                         .HasColumnType("nvarchar(128)")
@@ -189,9 +160,8 @@ namespace WebFileServ.Model.DAL.Migrations
                     b.Property<string>("ProviderDisplayName")
                         .HasColumnType("nvarchar(max)");
 
-                    b.Property<string>("UserId")
-                        .IsRequired()
-                        .HasColumnType("nvarchar(450)");
+                    b.Property<Guid>("UserId")
+                        .HasColumnType("uniqueidentifier");
 
                     b.HasKey("LoginProvider", "ProviderKey");
 
@@ -200,13 +170,34 @@ namespace WebFileServ.Model.DAL.Migrations
                     b.ToTable("AspNetUserLogins");
                 });
 
-            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
+            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
+                {
+                    b.Property<Guid>("UserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("LoginProvider")
+                        .HasColumnType("nvarchar(128)")
+                        .HasMaxLength(128);
+
+                    b.Property<string>("Name")
+                        .HasColumnType("nvarchar(128)")
+                        .HasMaxLength(128);
+
+                    b.Property<string>("Value")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.HasKey("UserId", "LoginProvider", "Name");
+
+                    b.ToTable("AspNetUserTokens");
+                });
+
+            modelBuilder.Entity("Tools.Identity.EF.Entities.BaseUserRole<System.Guid, WebFileServ.Model.Entities.Identity.ApplicationUser, WebFileServ.Model.Entities.Identity.ApplicationRole>", b =>
                 {
-                    b.Property<string>("UserId")
-                        .HasColumnType("nvarchar(450)");
+                    b.Property<Guid>("UserId")
+                        .HasColumnType("uniqueidentifier");
 
-                    b.Property<string>("RoleId")
-                        .HasColumnType("nvarchar(450)");
+                    b.Property<Guid>("RoleId")
+                        .HasColumnType("uniqueidentifier");
 
                     b.HasKey("UserId", "RoleId");
 
@@ -215,31 +206,39 @@ namespace WebFileServ.Model.DAL.Migrations
                     b.ToTable("AspNetUserRoles");
                 });
 
-            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
+            modelBuilder.Entity("WebFileServ.Model.Entities.Identity.ApplicationRole", b =>
                 {
-                    b.Property<string>("UserId")
-                        .HasColumnType("nvarchar(450)");
+                    b.Property<Guid>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uniqueidentifier");
 
-                    b.Property<string>("LoginProvider")
-                        .HasColumnType("nvarchar(128)")
-                        .HasMaxLength(128);
+                    b.Property<string>("ConcurrencyStamp")
+                        .IsConcurrencyToken()
+                        .HasColumnType("nvarchar(max)");
 
                     b.Property<string>("Name")
-                        .HasColumnType("nvarchar(128)")
-                        .HasMaxLength(128);
+                        .HasColumnType("nvarchar(256)")
+                        .HasMaxLength(256);
 
-                    b.Property<string>("Value")
-                        .HasColumnType("nvarchar(max)");
+                    b.Property<string>("NormalizedName")
+                        .HasColumnType("nvarchar(256)")
+                        .HasMaxLength(256);
 
-                    b.HasKey("UserId", "LoginProvider", "Name");
+                    b.HasKey("Id");
 
-                    b.ToTable("AspNetUserTokens");
+                    b.HasIndex("NormalizedName")
+                        .IsUnique()
+                        .HasName("RoleNameIndex")
+                        .HasFilter("[NormalizedName] IS NOT NULL");
+
+                    b.ToTable("AspNetRoles");
                 });
 
             modelBuilder.Entity("WebFileServ.Model.Entities.Identity.ApplicationUser", b =>
                 {
-                    b.Property<string>("Id")
-                        .HasColumnType("nvarchar(450)");
+                    b.Property<Guid>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uniqueidentifier");
 
                     b.Property<int>("AccessFailedCount")
                         .HasColumnType("int");
@@ -301,16 +300,16 @@ namespace WebFileServ.Model.DAL.Migrations
                     b.ToTable("AspNetUsers");
                 });
 
-            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
+            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
                 {
-                    b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
+                    b.HasOne("WebFileServ.Model.Entities.Identity.ApplicationRole", null)
                         .WithMany()
                         .HasForeignKey("RoleId")
                         .OnDelete(DeleteBehavior.Cascade)
                         .IsRequired();
                 });
 
-            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
+            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
                 {
                     b.HasOne("WebFileServ.Model.Entities.Identity.ApplicationUser", null)
                         .WithMany()
@@ -319,7 +318,7 @@ namespace WebFileServ.Model.DAL.Migrations
                         .IsRequired();
                 });
 
-            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
+            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
                 {
                     b.HasOne("WebFileServ.Model.Entities.Identity.ApplicationUser", null)
                         .WithMany()
@@ -328,14 +327,8 @@ namespace WebFileServ.Model.DAL.Migrations
                         .IsRequired();
                 });
 
-            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
+            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
                 {
-                    b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
-                        .WithMany()
-                        .HasForeignKey("RoleId")
-                        .OnDelete(DeleteBehavior.Cascade)
-                        .IsRequired();
-
                     b.HasOne("WebFileServ.Model.Entities.Identity.ApplicationUser", null)
                         .WithMany()
                         .HasForeignKey("UserId")
@@ -343,10 +336,16 @@ namespace WebFileServ.Model.DAL.Migrations
                         .IsRequired();
                 });
 
-            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
+            modelBuilder.Entity("Tools.Identity.EF.Entities.BaseUserRole<System.Guid, WebFileServ.Model.Entities.Identity.ApplicationUser, WebFileServ.Model.Entities.Identity.ApplicationRole>", b =>
                 {
-                    b.HasOne("WebFileServ.Model.Entities.Identity.ApplicationUser", null)
-                        .WithMany()
+                    b.HasOne("WebFileServ.Model.Entities.Identity.ApplicationRole", "Role")
+                        .WithMany("Users")
+                        .HasForeignKey("RoleId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("WebFileServ.Model.Entities.Identity.ApplicationUser", "User")
+                        .WithMany("Roles")
                         .HasForeignKey("UserId")
                         .OnDelete(DeleteBehavior.Cascade)
                         .IsRequired();
diff --git a/Src/WebFileServ.Model.DAL/WebFileServ.Model.DAL.csproj b/Src/WebFileServ.Model.DAL/WebFileServ.Model.DAL.csproj
index e34105d..7e248e8 100644
--- a/Src/WebFileServ.Model.DAL/WebFileServ.Model.DAL.csproj
+++ b/Src/WebFileServ.Model.DAL/WebFileServ.Model.DAL.csproj
@@ -1,11 +1,10 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <TargetFramework>netcoreapp3.1</TargetFramework>
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.AspNetCore.ApiAuthorization.IdentityServer" Version="3.1.9" />
     <PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
     <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.9" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="3.1.9" />
@@ -17,7 +16,12 @@
   </ItemGroup>
 
   <ItemGroup>
+    <ProjectReference Include="..\Tools.Identity.EF\Tools.Identity.EF.csproj" />
     <ProjectReference Include="..\WebFileServ.Model.Entities\WebFileServ.Model.Entities.csproj" />
   </ItemGroup>
 
+  <ItemGroup>
+    <Folder Include="Migrations\" />
+  </ItemGroup>
+
 </Project>
diff --git a/Src/WebFileServ.Model.DI/DIManager.cs b/Src/WebFileServ.Model.DI/DIManager.cs
index 65eb889..55a6e74 100644
--- a/Src/WebFileServ.Model.DI/DIManager.cs
+++ b/Src/WebFileServ.Model.DI/DIManager.cs
@@ -50,7 +50,7 @@ namespace WebFileServ.Model.DI
 
             RegistryDAL(services, configuration);
 
-            RegustryIdentity(services, configuration);
+            RegistryIdentity(services, configuration);
 
             RegistryBLL(services, configuration);
         }
@@ -61,31 +61,21 @@ namespace WebFileServ.Model.DI
         {
             services
                 .AddSingleton(configuration);
-            services
-                .AddSingleton<DataInitializerConfig>();
-
 
 
-            //if (Enviroment == EnumEnviroment.WebApp)
-            //{
             services.AddDbContext<ApplicationDbContext>(options =>
                         options.UseSqlServer(
                             configuration.GetConnectionString("DefaultConnection")
                         )
                     );
-            //}
-            //else 
-            //{
-            //    services.AddDbContext<ApplicationDbContext>(options =>
-            //            options.UseInMemoryDatabase("TestDb")
-            //        );
-            //}
+
 
             services
+                .AddSingleton<DataInitializerConfig>()
                 .AddScoped<DataInitializer>();
         }
 
-        private void RegustryIdentity(IServiceCollection services, IConfiguration configuration) 
+        private void RegistryIdentity(IServiceCollection services, IConfiguration configuration) 
         {
             if (Enviroment != AppEnviroment.UnitTests)
             {
@@ -105,12 +95,14 @@ namespace WebFileServ.Model.DI
                                 RequireNonAlphanumeric = false
                             };
                         }
-                    )
-                    .AddRoles<IdentityRole>()
+                    )                    
+                    .AddRoles<ApplicationRole>()
+                    .AddRoleManager<RoleManager<ApplicationRole>>()
                     .AddEntityFrameworkStores<ApplicationDbContext>();
 
-                services.AddIdentityServer()
-                .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
+                services
+                    .AddIdentityServer()                
+                    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
             }            
         }
 
diff --git a/Src/WebFileServ.Model.Entities/Identity/ApplicationRole.cs b/Src/WebFileServ.Model.Entities/Identity/ApplicationRole.cs
new file mode 100644
index 0000000..0bcf276
--- /dev/null
+++ b/Src/WebFileServ.Model.Entities/Identity/ApplicationRole.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+using Tools.Identity.EF.Entities;
+
+namespace WebFileServ.Model.Entities.Identity
+{
+    public class ApplicationRole
+        : BaseRole<
+            Guid, 
+            ApplicationUser, 
+            ApplicationRole
+            >
+    {
+
+        public ApplicationRole() 
+        { }
+        public ApplicationRole(string roleName)
+            : base(roleName)
+        { }
+    }
+
+}
diff --git a/Src/WebFileServ.Model.Entities/Identity/ApplicationUser.cs b/Src/WebFileServ.Model.Entities/Identity/ApplicationUser.cs
index 8dfc2f6..719216f 100644
--- a/Src/WebFileServ.Model.Entities/Identity/ApplicationUser.cs
+++ b/Src/WebFileServ.Model.Entities/Identity/ApplicationUser.cs
@@ -3,14 +3,17 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
 
-using Microsoft.AspNetCore.Identity;
+using Tools.Identity.EF.Entities;
 
 namespace WebFileServ.Model.Entities.Identity
 {
-    public class ApplicationUser
-        : IdentityUser
-    {
-        
+    public class ApplicationUser 
+        : BaseUser<
+            Guid, 
+            ApplicationUser, 
+            ApplicationRole
+            >
+    {        
     }
 
 }
diff --git a/Src/WebFileServ.Model.Entities/Identity/ApplicationUserRole.cs b/Src/WebFileServ.Model.Entities/Identity/ApplicationUserRole.cs
new file mode 100644
index 0000000..7f2cd63
--- /dev/null
+++ b/Src/WebFileServ.Model.Entities/Identity/ApplicationUserRole.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+using Tools.Identity.EF.Entities;
+
+namespace WebFileServ.Model.Entities.Identity
+{
+    public class ApplicationUserRole     
+        : BaseUserRole<
+            Guid, 
+            ApplicationUser, 
+            ApplicationRole
+            >
+    {        
+    }
+
+}
diff --git a/Src/WebFileServ.Model.Entities/WebFileServ.Model.Entities.csproj b/Src/WebFileServ.Model.Entities/WebFileServ.Model.Entities.csproj
index 7d17a3f..b75481f 100644
--- a/Src/WebFileServ.Model.Entities/WebFileServ.Model.Entities.csproj
+++ b/Src/WebFileServ.Model.Entities/WebFileServ.Model.Entities.csproj
@@ -9,4 +9,8 @@
     <PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="3.1.9" />
   </ItemGroup>
 
+  <ItemGroup>
+    <ProjectReference Include="..\Tools.Identity.EF\Tools.Identity.EF.csproj" />
+  </ItemGroup>
+
 </Project>

Src/WebFileServ.sln 21(+14 -7)

diff --git a/Src/WebFileServ.sln b/Src/WebFileServ.sln
index 6dcc67a..8ac57cd 100644
--- a/Src/WebFileServ.sln
+++ b/Src/WebFileServ.sln
@@ -9,21 +9,23 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Model", "Model", "{883FD108
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{B9C7B869-94B9-445C-80FD-17E0A601A303}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebFileServ.Model.Entities", "WebFileServ.Model.Entities\WebFileServ.Model.Entities.csproj", "{905AAC41-34C7-43DB-8AB4-58970EFEC5ED}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebFileServ.Model.Entities", "WebFileServ.Model.Entities\WebFileServ.Model.Entities.csproj", "{905AAC41-34C7-43DB-8AB4-58970EFEC5ED}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebFileServ.Model.DAL", "WebFileServ.Model.DAL\WebFileServ.Model.DAL.csproj", "{B701142C-9BF3-4F15-96D9-50017F0A36BC}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebFileServ.Model.DAL", "WebFileServ.Model.DAL\WebFileServ.Model.DAL.csproj", "{B701142C-9BF3-4F15-96D9-50017F0A36BC}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebFileServ.WebApp", "WebFileServ.WebApp\WebFileServ.WebApp.csproj", "{E34E770C-A485-4EE1-AD18-1A5665C368D9}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebFileServ.WebApp", "WebFileServ.WebApp\WebFileServ.WebApp.csproj", "{E34E770C-A485-4EE1-AD18-1A5665C368D9}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebFileServ.Test", "WebFileServ.Test", "{FDB98DA0-4EFD-4DDC-A01D-6A9A2C21A5F9}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebFileServ.Model.DI", "WebFileServ.Model.DI\WebFileServ.Model.DI.csproj", "{D2B916A2-2F5D-4D83-8259-09561FA8A22B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebFileServ.Model.DI", "WebFileServ.Model.DI\WebFileServ.Model.DI.csproj", "{D2B916A2-2F5D-4D83-8259-09561FA8A22B}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebFileServ.Test.ConsoleTest", "WebFileServ.Test.ConsoleTest\WebFileServ.Test.ConsoleTest.csproj", "{D7E4DD6B-17F1-488B-B9F6-533BB31B2DD7}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebFileServ.Test.ConsoleTest", "WebFileServ.Test.ConsoleTest\WebFileServ.Test.ConsoleTest.csproj", "{D7E4DD6B-17F1-488B-B9F6-533BB31B2DD7}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebFileServ.Model.BLL", "WebFileServ.Model.BLL\WebFileServ.Model.BLL.csproj", "{7F419249-7F5C-4D09-926E-8867BF52C5D2}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebFileServ.Model.BLL", "WebFileServ.Model.BLL\WebFileServ.Model.BLL.csproj", "{7F419249-7F5C-4D09-926E-8867BF52C5D2}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebFileServ.Test.UnitTests", "WebFileServ.Test.UnitTests\WebFileServ.Test.UnitTests.csproj", "{2A8D425B-0426-4C6F-AA33-59C36F543B10}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebFileServ.Test.UnitTests", "WebFileServ.Test.UnitTests\WebFileServ.Test.UnitTests.csproj", "{2A8D425B-0426-4C6F-AA33-59C36F543B10}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tools.Identity.EF", "Tools.Identity.EF\Tools.Identity.EF.csproj", "{7D3E7E6A-D523-4F52-9460-6C82F19A6E5C}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -59,6 +61,10 @@ Global
 		{2A8D425B-0426-4C6F-AA33-59C36F543B10}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{2A8D425B-0426-4C6F-AA33-59C36F543B10}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{2A8D425B-0426-4C6F-AA33-59C36F543B10}.Release|Any CPU.Build.0 = Release|Any CPU
+		{7D3E7E6A-D523-4F52-9460-6C82F19A6E5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7D3E7E6A-D523-4F52-9460-6C82F19A6E5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7D3E7E6A-D523-4F52-9460-6C82F19A6E5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7D3E7E6A-D523-4F52-9460-6C82F19A6E5C}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -72,6 +78,7 @@ Global
 		{D7E4DD6B-17F1-488B-B9F6-533BB31B2DD7} = {FDB98DA0-4EFD-4DDC-A01D-6A9A2C21A5F9}
 		{7F419249-7F5C-4D09-926E-8867BF52C5D2} = {883FD108-87F4-4E87-A222-780B037244AD}
 		{2A8D425B-0426-4C6F-AA33-59C36F543B10} = {FDB98DA0-4EFD-4DDC-A01D-6A9A2C21A5F9}
+		{7D3E7E6A-D523-4F52-9460-6C82F19A6E5C} = {B9C7B869-94B9-445C-80FD-17E0A601A303}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {540CF309-C759-4844-9BEA-596512586550}
diff --git a/Src/WebFileServ.Test.UnitTests/DITest.cs b/Src/WebFileServ.Test.UnitTests/DITest.cs
index 479c817..61ae5bb 100644
--- a/Src/WebFileServ.Test.UnitTests/DITest.cs
+++ b/Src/WebFileServ.Test.UnitTests/DITest.cs
@@ -7,6 +7,8 @@ using Microsoft.Extensions.Configuration;
 
 using WebFileServ.Model.DI;
 
+using WebFileServ.Model.DAL.DataInit;
+
 using WebFileServ.Model.BLL;
 
 namespace WebFileServ.Test.UnitTests
@@ -49,5 +51,6 @@ namespace WebFileServ.Test.UnitTests
                 throw new Exception();
             }
         }
+
     }
 }
diff --git a/Src/WebFileServ.WebApp/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs b/Src/WebFileServ.WebApp/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs
index bdf8298..35b4eb5 100644
--- a/Src/WebFileServ.WebApp/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs
+++ b/Src/WebFileServ.WebApp/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs
@@ -87,7 +87,7 @@ namespace WebFileServ.WebApp.Areas.Identity.Pages.Account.Manage
                 return NotFound($"Unable to load user with ID 'user.Id'.");
             }
 
-            var info = await _signInManager.GetExternalLoginInfoAsync(user.Id);
+            var info = await _signInManager.GetExternalLoginInfoAsync(user.Id.ToString());
             if (info == null)
             {
                 throw new InvalidOperationException($"Unexpected error occurred loading external login info for user with ID '{user.Id}'.");
diff --git a/Src/WebFileServ.WebApp/Areas/Identity/Pages/Account/Manage/Index.cshtml b/Src/WebFileServ.WebApp/Areas/Identity/Pages/Account/Manage/Index.cshtml
index e018437..7dc76a0 100644
--- a/Src/WebFileServ.WebApp/Areas/Identity/Pages/Account/Manage/Index.cshtml
+++ b/Src/WebFileServ.WebApp/Areas/Identity/Pages/Account/Manage/Index.cshtml
@@ -20,6 +20,13 @@
                 <input asp-for="Input.PhoneNumber" class="form-control" />
                 <span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
             </div>
+            <div class="form-group">
+                <label asp-for="UserRoles"></label>
+                @foreach (var item in Model.UserRoles)
+                {
+                    <input asp-for=@item class="form-control" disabled/>
+                }
+            </div>
             <button id="update-profile-button" type="submit" class="btn btn-primary">Save</button>
         </form>
     </div>
diff --git a/Src/WebFileServ.WebApp/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs b/Src/WebFileServ.WebApp/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs
index fac82d5..d27ce9d 100644
--- a/Src/WebFileServ.WebApp/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs
+++ b/Src/WebFileServ.WebApp/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs
@@ -12,44 +12,40 @@ namespace WebFileServ.WebApp.Areas.Identity.Pages.Account.Manage
 {
     public partial class IndexModel : PageModel
     {
+        public class InputModel
+        {
+            [Phone]
+            [Display(Name = "Phone number")]
+            public string PhoneNumber { get; set; }
+        }
+
+
         private readonly UserManager<ApplicationUser> _userManager;
         private readonly SignInManager<ApplicationUser> _signInManager;
 
-        public IndexModel(
-            UserManager<ApplicationUser> userManager,
-            SignInManager<ApplicationUser> signInManager)
-        {
-            _userManager = userManager;
-            _signInManager = signInManager;
-        }
 
         public string Username { get; set; }
 
+        public string[] UserRoles { get; set; }
+
+
         [TempData]
         public string StatusMessage { get; set; }
 
         [BindProperty]
         public InputModel Input { get; set; }
 
-        public class InputModel
-        {
-            [Phone]
-            [Display(Name = "Phone number")]
-            public string PhoneNumber { get; set; }
-        }
 
-        private async Task LoadAsync(ApplicationUser user)
+        public IndexModel(
+            UserManager<ApplicationUser> userManager,
+            SignInManager<ApplicationUser> signInManager)
         {
-            var userName = await _userManager.GetUserNameAsync(user);
-            var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
+            _userManager = userManager;
+            _signInManager = signInManager;
+        }
 
-            Username = userName;
 
-            Input = new InputModel
-            {
-                PhoneNumber = phoneNumber
-            };
-        }
+        #region public 
 
         public async Task<IActionResult> OnGetAsync()
         {
@@ -92,5 +88,28 @@ namespace WebFileServ.WebApp.Areas.Identity.Pages.Account.Manage
             StatusMessage = "Your profile has been updated";
             return RedirectToPage();
         }
+
+        #endregion
+
+
+        private async Task LoadAsync(ApplicationUser user)
+        {
+            var userName = await _userManager.GetUserNameAsync(user);
+            var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
+            var userRoles = user
+                .Roles
+                .Select(e => e.Role.Name)
+                .ToArray();
+
+
+            Username = userName;
+            UserRoles = userRoles;
+
+            Input = new InputModel
+            {
+                PhoneNumber = phoneNumber
+            };
+        }
+        
     }
 }
diff --git a/Src/WebFileServ.WebApp/WebFileServ.WebApp.csproj b/Src/WebFileServ.WebApp/WebFileServ.WebApp.csproj
index 7341d7b..ff476d3 100644
--- a/Src/WebFileServ.WebApp/WebFileServ.WebApp.csproj
+++ b/Src/WebFileServ.WebApp/WebFileServ.WebApp.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk.Web">
+<Project Sdk="Microsoft.NET.Sdk.Web">
 
   <PropertyGroup>
     <TargetFramework>netcoreapp3.1</TargetFramework>
@@ -17,10 +17,11 @@
     <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.9" />
     <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="3.1.9" />
     <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.9" />
-    <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.9" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.9" />
-    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.9" />
-    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.9" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.9">
+      <PrivateAssets>all</PrivateAssets>
+      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+    </PackageReference>
     <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.4" />
     <PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
   </ItemGroup>