diff --git a/RentACar/Data/Data/RentACarDbContext.cs b/RentACar/Data/Data/RentACarDbContext.cs index af66b2e..c6fec30 100644 --- a/RentACar/Data/Data/RentACarDbContext.cs +++ b/RentACar/Data/Data/RentACarDbContext.cs @@ -6,21 +6,14 @@ using Microsoft.EntityFrameworkCore; namespace Data { - public class RentACarDbContext : IdentityDbContext + public class RentACarDbContext : IdentityDbContext { public virtual DbSet Cars { get; set; } public virtual DbSet Rents { get; set; } - private UserManager userManager { get; set; } - private RoleManager roleManager { get; set; } - - public RentACarDbContext() - { - //TODO: initialize UserManager and RoleManager - } public RentACarDbContext(DbContextOptions dbContextOptions) : base(dbContextOptions) { - //TODO: initialize UserManager and RoleManager + } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) @@ -33,57 +26,48 @@ namespace Data protected override async void OnModelCreating(ModelBuilder modelBuilder) { + + base.OnModelCreating(modelBuilder); + + this.Database.EnsureCreated(); string[] roles = { "Admin", "Employee" }; foreach (string role in roles) { - if (!await roleManager.RoleExistsAsync(role)) + IdentityRole roleToCheck = await this.Roles.FirstOrDefaultAsync(roleToCheck => roleToCheck.Name == role); + if (roleToCheck == null) { - await roleManager.CreateAsync(new IdentityRole(role)); + //this.Roles.Add(new IdentityRole(role)); + modelBuilder.Entity().HasData(new IdentityRole(role)); } } - User initialUser = new User + PasswordHasher passwordHasher = new PasswordHasher(); + + User initialUser = new User(); + initialUser.Id = Guid.NewGuid().ToString(); + initialUser.UserName = "admin"; + initialUser.PasswordHash = passwordHasher.HashPassword(initialUser, "admin"); + + + if (this.Users.FirstOrDefaultAsync() != null) { - UserName = "admin", - PasswordHash = "admin", - }; - modelBuilder.Entity().HasData(initialUser); - await userManager.AddToRoleAsync(initialUser, roles[0]); + modelBuilder.Entity().HasData(initialUser); + IdentityRole adminRole = await this.Roles.FirstOrDefaultAsync(role => role.Name == "Admin"); + modelBuilder.Entity>().HasData(new IdentityUserRole {RoleId = adminRole.Id, UserId = initialUser.Id}); + } - //modelBuilder.Entity().HasData( - // new User - // { - // Username = "user", - // Password = "user", - // FirstName = "User", - // LastName = "User", - // PersonalNumber = "0987654321", - // PhoneNumber = "0882750588", - // Email = "user@gmail.org", - // Role = User.RoleEnum.User - // } - //); - //modelBuilder.Entity().HasData( - // new User - // { - // Username = "manager", - // Password = "manager", - // FirstName = "Manager", - // LastName = "Manager", - // PersonalNumber = "0987654321", - // PhoneNumber = "0882750588", - // Email = "manager@gmail.org", - // Role = User.RoleEnum.Manager - // } - //); - base.OnModelCreating(modelBuilder); - modelBuilder.Entity() - .HasIndex(user => new { user.UserName }) - .IsUnique(true); modelBuilder.Entity().HasOne(rents => rents.User); modelBuilder.Entity().HasOne(rents => rents.Car); + + modelBuilder.Entity().HasData(new Car() + { + Id = 1, + Brand = "Trabant" + }) ; + + this.SaveChanges(); } } diff --git a/RentACar/Data/Migrations/20220401141638_InitialMigration.Designer.cs b/RentACar/Data/Migrations/20220404172708_Changes.Designer.cs similarity index 83% rename from RentACar/Data/Migrations/20220401141638_InitialMigration.Designer.cs rename to RentACar/Data/Migrations/20220404172708_Changes.Designer.cs index 97721c1..6f6cc10 100644 --- a/RentACar/Data/Migrations/20220401141638_InitialMigration.Designer.cs +++ b/RentACar/Data/Migrations/20220404172708_Changes.Designer.cs @@ -10,15 +10,15 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Data.Migrations { [DbContext(typeof(RentACarDbContext))] - [Migration("20220401141638_InitialMigration")] - partial class InitialMigration + [Migration("20220404172708_Changes")] + partial class Changes { protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.13") + .HasAnnotation("ProductVersion", "5.0.15") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("Data.Entities.Car", b => @@ -58,7 +58,7 @@ namespace Data.Migrations .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - b.Property("CarId") + b.Property("CarId") .HasColumnType("int"); b.Property("EndDate") @@ -88,10 +88,12 @@ namespace Data.Migrations .HasColumnType("int"); b.Property("ConcurrencyStamp") + .IsConcurrencyToken() .HasColumnType("nvarchar(max)"); b.Property("Email") - .HasColumnType("nvarchar(max)"); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("EmailConfirmed") .HasColumnType("bit"); @@ -109,10 +111,12 @@ namespace Data.Migrations .HasColumnType("datetimeoffset"); b.Property("NormalizedEmail") - .HasColumnType("nvarchar(max)"); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("NormalizedUserName") - .HasColumnType("nvarchar(max)"); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("PasswordHash") .HasColumnType("nvarchar(max)"); @@ -133,15 +137,20 @@ namespace Data.Migrations .HasColumnType("bit"); b.Property("UserName") - .HasColumnType("nvarchar(450)"); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.HasKey("Id"); - b.HasIndex("UserName") - .IsUnique() - .HasFilter("[UserName] IS NOT NULL"); + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); - b.ToTable("User"); + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => @@ -195,71 +204,6 @@ namespace Data.Migrations b.ToTable("AspNetRoleClaims"); }); - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.ToTable("AspNetUsers"); - }); - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => { b.Property("Id") @@ -344,7 +288,9 @@ namespace Data.Migrations { b.HasOne("Data.Entities.Car", "Car") .WithMany() - .HasForeignKey("CarId"); + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); b.HasOne("Data.Entities.User", "User") .WithMany() @@ -366,7 +312,7 @@ namespace Data.Migrations modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + b.HasOne("Data.Entities.User", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -375,7 +321,7 @@ namespace Data.Migrations modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + b.HasOne("Data.Entities.User", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -390,7 +336,7 @@ namespace Data.Migrations .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + b.HasOne("Data.Entities.User", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -399,7 +345,7 @@ namespace Data.Migrations modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + b.HasOne("Data.Entities.User", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) diff --git a/RentACar/Data/Migrations/20220401141638_InitialMigration.cs b/RentACar/Data/Migrations/20220404172708_Changes.cs similarity index 85% rename from RentACar/Data/Migrations/20220401141638_InitialMigration.cs rename to RentACar/Data/Migrations/20220404172708_Changes.cs index 27fc668..5097a69 100644 --- a/RentACar/Data/Migrations/20220401141638_InitialMigration.cs +++ b/RentACar/Data/Migrations/20220404172708_Changes.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations; namespace Data.Migrations { - public partial class InitialMigration : Migration + public partial class Changes : Migration { protected override void Up(MigrationBuilder migrationBuilder) { @@ -26,6 +26,9 @@ namespace Data.Migrations columns: table => new { Id = table.Column(type: "nvarchar(450)", nullable: false), + FirstName = table.Column(type: "nvarchar(max)", nullable: true), + LastName = table.Column(type: "nvarchar(max)", nullable: true), + PersonalNumber = table.Column(type: "nvarchar(max)", nullable: true), UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), @@ -64,34 +67,6 @@ namespace Data.Migrations table.PrimaryKey("PK_Cars", x => x.Id); }); - migrationBuilder.CreateTable( - name: "User", - columns: table => new - { - Id = table.Column(type: "nvarchar(450)", nullable: false), - FirstName = table.Column(type: "nvarchar(max)", nullable: true), - LastName = table.Column(type: "nvarchar(max)", nullable: true), - PersonalNumber = table.Column(type: "nvarchar(max)", nullable: true), - UserName = table.Column(type: "nvarchar(450)", nullable: true), - NormalizedUserName = table.Column(type: "nvarchar(max)", nullable: true), - Email = table.Column(type: "nvarchar(max)", nullable: true), - NormalizedEmail = table.Column(type: "nvarchar(max)", nullable: true), - EmailConfirmed = table.Column(type: "bit", nullable: false), - PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), - SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), - PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), - PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), - TwoFactorEnabled = table.Column(type: "bit", nullable: false), - LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), - LockoutEnabled = table.Column(type: "bit", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_User", x => x.Id); - }); - migrationBuilder.CreateTable( name: "AspNetRoleClaims", columns: table => new @@ -204,7 +179,7 @@ namespace Data.Migrations { Id = table.Column(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), - CarId = table.Column(type: "int", nullable: true), + CarId = table.Column(type: "int", nullable: false), StartDate = table.Column(type: "datetime2", nullable: false), EndDate = table.Column(type: "datetime2", nullable: false), UserId = table.Column(type: "nvarchar(450)", nullable: true) @@ -212,18 +187,18 @@ namespace Data.Migrations constraints: table => { table.PrimaryKey("PK_Rents", x => x.Id); + table.ForeignKey( + name: "FK_Rents_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); table.ForeignKey( name: "FK_Rents_Cars_CarId", column: x => x.CarId, principalTable: "Cars", principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Rents_User_UserId", - column: x => x.UserId, - principalTable: "User", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); + onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateIndex( @@ -274,13 +249,6 @@ namespace Data.Migrations name: "IX_Rents_UserId", table: "Rents", column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_User_UserName", - table: "User", - column: "UserName", - unique: true, - filter: "[UserName] IS NOT NULL"); } protected override void Down(MigrationBuilder migrationBuilder) @@ -311,9 +279,6 @@ namespace Data.Migrations migrationBuilder.DropTable( name: "Cars"); - - migrationBuilder.DropTable( - name: "User"); } } } diff --git a/RentACar/Data/Migrations/RentACarDbContextModelSnapshot.cs b/RentACar/Data/Migrations/RentACarDbContextModelSnapshot.cs index 4bf9e27..9414400 100644 --- a/RentACar/Data/Migrations/RentACarDbContextModelSnapshot.cs +++ b/RentACar/Data/Migrations/RentACarDbContextModelSnapshot.cs @@ -16,7 +16,7 @@ namespace Data.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.13") + .HasAnnotation("ProductVersion", "5.0.15") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("Data.Entities.Car", b => @@ -56,7 +56,7 @@ namespace Data.Migrations .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - b.Property("CarId") + b.Property("CarId") .HasColumnType("int"); b.Property("EndDate") @@ -86,10 +86,12 @@ namespace Data.Migrations .HasColumnType("int"); b.Property("ConcurrencyStamp") + .IsConcurrencyToken() .HasColumnType("nvarchar(max)"); b.Property("Email") - .HasColumnType("nvarchar(max)"); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("EmailConfirmed") .HasColumnType("bit"); @@ -107,10 +109,12 @@ namespace Data.Migrations .HasColumnType("datetimeoffset"); b.Property("NormalizedEmail") - .HasColumnType("nvarchar(max)"); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("NormalizedUserName") - .HasColumnType("nvarchar(max)"); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("PasswordHash") .HasColumnType("nvarchar(max)"); @@ -131,15 +135,20 @@ namespace Data.Migrations .HasColumnType("bit"); b.Property("UserName") - .HasColumnType("nvarchar(450)"); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.HasKey("Id"); - b.HasIndex("UserName") - .IsUnique() - .HasFilter("[UserName] IS NOT NULL"); + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); - b.ToTable("User"); + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => @@ -193,71 +202,6 @@ namespace Data.Migrations b.ToTable("AspNetRoleClaims"); }); - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.ToTable("AspNetUsers"); - }); - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => { b.Property("Id") @@ -342,7 +286,9 @@ namespace Data.Migrations { b.HasOne("Data.Entities.Car", "Car") .WithMany() - .HasForeignKey("CarId"); + .HasForeignKey("CarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); b.HasOne("Data.Entities.User", "User") .WithMany() @@ -364,7 +310,7 @@ namespace Data.Migrations modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + b.HasOne("Data.Entities.User", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -373,7 +319,7 @@ namespace Data.Migrations modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + b.HasOne("Data.Entities.User", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -388,7 +334,7 @@ namespace Data.Migrations .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + b.HasOne("Data.Entities.User", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) @@ -397,7 +343,7 @@ namespace Data.Migrations modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + b.HasOne("Data.Entities.User", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) diff --git a/RentACar/Data/Models/Rents.cs b/RentACar/Data/Models/Rents.cs index 5f33094..77a541a 100644 --- a/RentACar/Data/Models/Rents.cs +++ b/RentACar/Data/Models/Rents.cs @@ -1,5 +1,7 @@ -using System; +using Microsoft.AspNetCore.Identity; +using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -9,13 +11,13 @@ namespace Data.Entities public class Rents { public int Id { get; set; } - + [ForeignKey("Car")] + public int CarId { get; set; } public virtual Car Car { get; set; } - public DateTime StartDate { get; set; } - public DateTime EndDate { get; set; } - + [ForeignKey("User")] + public string UserId { get; set; } public virtual User User { get; set; } } } diff --git a/RentACar/WebApp/Areas/Identity/IdentityHostingStartup.cs b/RentACar/WebApp/Areas/Identity/IdentityHostingStartup.cs index bb403e8..a7f5b0d 100644 --- a/RentACar/WebApp/Areas/Identity/IdentityHostingStartup.cs +++ b/RentACar/WebApp/Areas/Identity/IdentityHostingStartup.cs @@ -1,5 +1,6 @@ using System; using Data; +using Data.Entities; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.UI; diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/AccessDenied.cshtml b/RentACar/WebApp/Areas/Identity/Pages/Account/AccessDenied.cshtml new file mode 100644 index 0000000..017f6ff --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/AccessDenied.cshtml @@ -0,0 +1,10 @@ +@page +@model AccessDeniedModel +@{ + ViewData["Title"] = "Access denied"; +} + +
+

@ViewData["Title"]

+

You do not have access to this resource.

+
diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs b/RentACar/WebApp/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs new file mode 100644 index 0000000..0533326 --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace WebApp.Areas.Identity.Pages.Account +{ + public class AccessDeniedModel : PageModel + { + public void OnGet() + { + + } + } +} + diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Login.cshtml b/RentACar/WebApp/Areas/Identity/Pages/Account/Login.cshtml index 857833a..8e4d327 100644 --- a/RentACar/WebApp/Areas/Identity/Pages/Account/Login.cshtml +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Login.cshtml @@ -14,9 +14,9 @@
- - - + + +
diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Login.cshtml.cs b/RentACar/WebApp/Areas/Identity/Pages/Account/Login.cshtml.cs index c5acdcc..0cd002d 100644 --- a/RentACar/WebApp/Areas/Identity/Pages/Account/Login.cshtml.cs +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Login.cshtml.cs @@ -5,13 +5,13 @@ using System.Linq; using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; +using Data.Entities; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Logging; -using Data.Entities; namespace WebApp.Areas.Identity.Pages.Account { @@ -44,7 +44,8 @@ namespace WebApp.Areas.Identity.Pages.Account public class InputModel { [Required] - public string Username { get; set; } + [EmailAddress] + public string Email { get; set; } [Required] [DataType(DataType.Password)] @@ -81,7 +82,7 @@ namespace WebApp.Areas.Identity.Pages.Account { // This doesn't count login failures towards account lockout // To enable password failures to trigger account lockout, set lockoutOnFailure: true - var result = await _signInManager.PasswordSignInAsync(Input.Username, Input.Password, Input.RememberMe, lockoutOnFailure: false); + var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false); if (result.Succeeded) { _logger.LogInformation("User logged in."); diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Logout.cshtml.cs b/RentACar/WebApp/Areas/Identity/Pages/Account/Logout.cshtml.cs index 5755a27..bf0f8c6 100644 --- a/RentACar/WebApp/Areas/Identity/Pages/Account/Logout.cshtml.cs +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Logout.cshtml.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Data.Entities; using Microsoft.AspNetCore.Authorization; +using Data.Entities; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml new file mode 100644 index 0000000..31a2ea5 --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml @@ -0,0 +1,36 @@ +@page +@model ChangePasswordModel +@{ + ViewData["Title"] = "Change password"; + ViewData["ActivePage"] = ManageNavPages.ChangePassword; +} + +

@ViewData["Title"]

+ +
+
+
+
+
+ + + +
+
+ + + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} \ No newline at end of file diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs new file mode 100644 index 0000000..7a57349 --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Data.Entities; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +namespace WebApp.Areas.Identity.Pages.Account.Manage +{ + public class ChangePasswordModel : PageModel + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public ChangePasswordModel( + UserManager userManager, + SignInManager signInManager, + ILogger logger) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + } + + [BindProperty] + public InputModel Input { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public class InputModel + { + [Required] + [DataType(DataType.Password)] + [Display(Name = "Current password")] + public string OldPassword { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } + + public async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var hasPassword = await _userManager.HasPasswordAsync(user); + if (!hasPassword) + { + return RedirectToPage("./SetPassword"); + } + + return Page(); + } + + public async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var changePasswordResult = await _userManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword); + if (!changePasswordResult.Succeeded) + { + foreach (var error in changePasswordResult.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + return Page(); + } + + await _signInManager.RefreshSignInAsync(user); + _logger.LogInformation("User changed their password successfully."); + StatusMessage = "Your password has been changed."; + + return RedirectToPage(); + } + } +} diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml new file mode 100644 index 0000000..c95ab92 --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml @@ -0,0 +1,33 @@ +@page +@model DeletePersonalDataModel +@{ + ViewData["Title"] = "Delete Personal Data"; + ViewData["ActivePage"] = ManageNavPages.PersonalData; +} + +

@ViewData["Title"]

+ + + +
+
+
+ @if (Model.RequirePassword) + { +
+ + + +
+ } + +
+
+ +@section Scripts { + +} \ No newline at end of file diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs new file mode 100644 index 0000000..c6bca09 --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs @@ -0,0 +1,84 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Data.Entities; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace WebApp.Areas.Identity.Pages.Account.Manage +{ + public class DeletePersonalDataModel : PageModel + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public DeletePersonalDataModel( + UserManager userManager, + SignInManager signInManager, + ILogger logger) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + } + + public bool RequirePassword { get; set; } + + public async Task OnGet() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + RequirePassword = await _userManager.HasPasswordAsync(user); + return Page(); + } + + public async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + RequirePassword = await _userManager.HasPasswordAsync(user); + if (RequirePassword) + { + if (!await _userManager.CheckPasswordAsync(user, Input.Password)) + { + ModelState.AddModelError(string.Empty, "Incorrect password."); + return Page(); + } + } + + var result = await _userManager.DeleteAsync(user); + var userId = await _userManager.GetUserIdAsync(user); + if (!result.Succeeded) + { + throw new InvalidOperationException($"Unexpected error occurred deleting user with ID '{userId}'."); + } + + await _signInManager.SignOutAsync(); + + _logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId); + + return Redirect("~/"); + } + } +} diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml new file mode 100644 index 0000000..87470c2 --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml @@ -0,0 +1,12 @@ +@page +@model DownloadPersonalDataModel +@{ + ViewData["Title"] = "Download Your Data"; + ViewData["ActivePage"] = ManageNavPages.PersonalData; +} + +

@ViewData["Title"]

+ +@section Scripts { + +} \ No newline at end of file diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs new file mode 100644 index 0000000..5729bf2 --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Data.Entities; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace WebApp.Areas.Identity.Pages.Account.Manage +{ + public class DownloadPersonalDataModel : PageModel + { + private readonly UserManager _userManager; + private readonly ILogger _logger; + + public DownloadPersonalDataModel( + UserManager userManager, + ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + public async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + _logger.LogInformation("User with ID '{UserId}' asked for their personal data.", _userManager.GetUserId(User)); + + // Only include personal data for download + var personalData = new Dictionary(); + var personalDataProps = typeof(User).GetProperties().Where( + prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute))); + foreach (var p in personalDataProps) + { + personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null"); + } + + var logins = await _userManager.GetLoginsAsync(user); + foreach (var l in logins) + { + personalData.Add($"{l.LoginProvider} external login provider key", l.ProviderKey); + } + + Response.Headers.Add("Content-Disposition", "attachment; filename=PersonalData.json"); + return new FileContentResult(JsonSerializer.SerializeToUtf8Bytes(personalData), "application/json"); + } + } +} diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/Email.cshtml b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/Email.cshtml new file mode 100644 index 0000000..2a599ee --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/Email.cshtml @@ -0,0 +1,43 @@ +@page +@model EmailModel +@{ + ViewData["Title"] = "Manage Email"; + ViewData["ActivePage"] = ManageNavPages.Email; +} + +

@ViewData["Title"]

+ +
+
+
+
+
+ + @if (Model.IsEmailConfirmed) + { +
+ +
+ +
+
+ } + else + { + + + } +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/Email.cshtml.cs b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/Email.cshtml.cs new file mode 100644 index 0000000..f421033 --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/Email.cshtml.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; +using System.Text.Encodings.Web; +using System.Linq; +using System.Threading.Tasks; +using Data.Entities; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.WebUtilities; + +namespace WebApp.Areas.Identity.Pages.Account.Manage +{ + public partial class EmailModel : PageModel + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly IEmailSender _emailSender; + + public EmailModel( + UserManager userManager, + SignInManager signInManager, + IEmailSender emailSender) + { + _userManager = userManager; + _signInManager = signInManager; + _emailSender = emailSender; + } + + public string Username { get; set; } + + public string Email { get; set; } + + public bool IsEmailConfirmed { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + [Display(Name = "New email")] + public string NewEmail { get; set; } + } + + private async Task LoadAsync(User user) + { + var email = await _userManager.GetEmailAsync(user); + Email = email; + + Input = new InputModel + { + NewEmail = email, + }; + + IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user); + } + + public async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + await LoadAsync(user); + return Page(); + } + + public async Task OnPostChangeEmailAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + if (!ModelState.IsValid) + { + await LoadAsync(user); + return Page(); + } + + var email = await _userManager.GetEmailAsync(user); + if (Input.NewEmail != email) + { + var userId = await _userManager.GetUserIdAsync(user); + var code = await _userManager.GenerateChangeEmailTokenAsync(user, Input.NewEmail); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + var callbackUrl = Url.Page( + "/Account/ConfirmEmailChange", + pageHandler: null, + values: new { userId = userId, email = Input.NewEmail, code = code }, + protocol: Request.Scheme); + await _emailSender.SendEmailAsync( + Input.NewEmail, + "Confirm your email", + $"Please confirm your account by clicking here."); + + StatusMessage = "Confirmation link to change email sent. Please check your email."; + return RedirectToPage(); + } + + StatusMessage = "Your email is unchanged."; + return RedirectToPage(); + } + + public async Task OnPostSendVerificationEmailAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + if (!ModelState.IsValid) + { + await LoadAsync(user); + return Page(); + } + + var userId = await _userManager.GetUserIdAsync(user); + var email = await _userManager.GetEmailAsync(user); + var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + var callbackUrl = Url.Page( + "/Account/ConfirmEmail", + pageHandler: null, + values: new { area = "Identity", userId = userId, code = code }, + protocol: Request.Scheme); + await _emailSender.SendEmailAsync( + email, + "Confirm your email", + $"Please confirm your account by clicking here."); + + StatusMessage = "Verification email sent. Please check your email."; + return RedirectToPage(); + } + } +} diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/Index.cshtml b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/Index.cshtml new file mode 100644 index 0000000..e018437 --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/Index.cshtml @@ -0,0 +1,30 @@ +@page +@model IndexModel +@{ + ViewData["Title"] = "Profile"; + ViewData["ActivePage"] = ManageNavPages.Index; +} + +

@ViewData["Title"]

+ +
+
+
+
+
+ + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} \ No newline at end of file diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs new file mode 100644 index 0000000..cbffed3 --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Data.Entities; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace WebApp.Areas.Identity.Pages.Account.Manage +{ + public partial class IndexModel : PageModel + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + + public IndexModel( + UserManager userManager, + SignInManager signInManager) + { + _userManager = userManager; + _signInManager = signInManager; + } + + public string Username { 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(User user) + { + var userName = await _userManager.GetUserNameAsync(user); + var phoneNumber = await _userManager.GetPhoneNumberAsync(user); + + Username = userName; + + Input = new InputModel + { + PhoneNumber = phoneNumber + }; + } + + public async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + await LoadAsync(user); + return Page(); + } + + public async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + if (!ModelState.IsValid) + { + await LoadAsync(user); + return Page(); + } + + var phoneNumber = await _userManager.GetPhoneNumberAsync(user); + if (Input.PhoneNumber != phoneNumber) + { + var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber); + if (!setPhoneResult.Succeeded) + { + StatusMessage = "Unexpected error when trying to set phone number."; + return RedirectToPage(); + } + } + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = "Your profile has been updated"; + return RedirectToPage(); + } + } +} diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs new file mode 100644 index 0000000..f4e5adf --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Rendering; + +namespace WebApp.Areas.Identity.Pages.Account.Manage +{ + public static class ManageNavPages + { + public static string Index => "Index"; + + public static string Email => "Email"; + + public static string ChangePassword => "ChangePassword"; + + public static string DownloadPersonalData => "DownloadPersonalData"; + + public static string DeletePersonalData => "DeletePersonalData"; + + public static string ExternalLogins => "ExternalLogins"; + + public static string PersonalData => "PersonalData"; + + public static string TwoFactorAuthentication => "TwoFactorAuthentication"; + + public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index); + + public static string EmailNavClass(ViewContext viewContext) => PageNavClass(viewContext, Email); + + public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword); + + public static string DownloadPersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DownloadPersonalData); + + public static string DeletePersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DeletePersonalData); + + public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins); + + public static string PersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, PersonalData); + + public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication); + + private static string PageNavClass(ViewContext viewContext, string page) + { + var activePage = viewContext.ViewData["ActivePage"] as string + ?? System.IO.Path.GetFileNameWithoutExtension(viewContext.ActionDescriptor.DisplayName); + return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null; + } + } +} diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml new file mode 100644 index 0000000..d64bd82 --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml @@ -0,0 +1,27 @@ +@page +@model PersonalDataModel +@{ + ViewData["Title"] = "Personal Data"; + ViewData["ActivePage"] = ManageNavPages.PersonalData; +} + +

@ViewData["Title"]

+ +
+
+

Your account contains personal data that you have given us. This page allows you to download or delete that data.

+

+ Deleting this data will permanently remove your account, and this cannot be recovered. +

+
+ +
+

+ Delete +

+
+
+ +@section Scripts { + +} diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs new file mode 100644 index 0000000..d86ad57 --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs @@ -0,0 +1,34 @@ +using System.Threading.Tasks; +using Data.Entities; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace WebApp.Areas.Identity.Pages.Account.Manage +{ + public class PersonalDataModel : PageModel + { + private readonly UserManager _userManager; + private readonly ILogger _logger; + + public PersonalDataModel( + UserManager userManager, + ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + public async Task OnGet() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + return Page(); + } + } +} \ No newline at end of file diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml new file mode 100644 index 0000000..f1817aa --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml @@ -0,0 +1,35 @@ +@page +@model SetPasswordModel +@{ + ViewData["Title"] = "Set password"; + ViewData["ActivePage"] = ManageNavPages.ChangePassword; +} + +

Set your password

+ +

+ You do not have a local username/password for this site. Add a local + account so you can log in without an external login. +

+
+
+
+
+
+ + + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} \ No newline at end of file diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs new file mode 100644 index 0000000..e8b93e7 --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Data.Entities; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace WebApp.Areas.Identity.Pages.Account.Manage +{ + public class SetPasswordModel : PageModel + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + + public SetPasswordModel( + UserManager userManager, + SignInManager signInManager) + { + _userManager = userManager; + _signInManager = signInManager; + } + + [BindProperty] + public InputModel Input { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public class InputModel + { + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } + + public async Task OnGetAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var hasPassword = await _userManager.HasPasswordAsync(user); + + if (hasPassword) + { + return RedirectToPage("./ChangePassword"); + } + + return Page(); + } + + public async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var addPasswordResult = await _userManager.AddPasswordAsync(user, Input.NewPassword); + if (!addPasswordResult.Succeeded) + { + foreach (var error in addPasswordResult.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + return Page(); + } + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = "Your password has been set."; + + return RedirectToPage(); + } + } +} diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/_Layout.cshtml b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/_Layout.cshtml new file mode 100644 index 0000000..3d882cc --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/_Layout.cshtml @@ -0,0 +1,29 @@ +@{ + if (ViewData.TryGetValue("ParentLayout", out var parentLayout)) + { + Layout = (string)parentLayout; + } + else + { + Layout = "/Areas/Identity/Pages/_Layout.cshtml"; + } +} + +

Manage your account

+ +
+

Change your account settings

+
+
+
+ +
+
+ @RenderBody() +
+
+
+ +@section Scripts { + @RenderSection("Scripts", required: false) +} diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml new file mode 100644 index 0000000..6d2863c --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml @@ -0,0 +1,15 @@ +@inject SignInManager SignInManager +@{ + var hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any(); +} + diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml new file mode 100644 index 0000000..208a424 --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml @@ -0,0 +1,10 @@ +@model string + +@if (!String.IsNullOrEmpty(Model)) +{ + var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success"; + +} \ No newline at end of file diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml new file mode 100644 index 0000000..cdc74bb --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml @@ -0,0 +1 @@ +@using WebApp.Areas.Identity.Pages.Account.Manage diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Register.cshtml b/RentACar/WebApp/Areas/Identity/Pages/Account/Register.cshtml index 7326a17..d9b50ab 100644 --- a/RentACar/WebApp/Areas/Identity/Pages/Account/Register.cshtml +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Register.cshtml @@ -17,36 +17,6 @@
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/Register.cshtml.cs b/RentACar/WebApp/Areas/Identity/Pages/Account/Register.cshtml.cs index 1bb8d65..8f92fe7 100644 --- a/RentACar/WebApp/Areas/Identity/Pages/Account/Register.cshtml.cs +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/Register.cshtml.cs @@ -5,10 +5,9 @@ using System.Linq; using System.Text; using System.Text.Encodings.Web; using System.Threading.Tasks; -using Data; -using Data.Entities; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; +using Data.Entities; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.Mvc; @@ -24,19 +23,19 @@ namespace WebApp.Areas.Identity.Pages.Account private readonly SignInManager _signInManager; private readonly UserManager _userManager; private readonly ILogger _logger; - private readonly RentACarDbContext _rentACartDbContext; + private readonly IEmailSender _emailSender; public RegisterModel( UserManager userManager, SignInManager signInManager, ILogger logger, - RentACarDbContext rentACarDbContext) - { + IEmailSender emailSender) + { _userManager = userManager; _signInManager = signInManager; _logger = logger; - _rentACartDbContext = rentACarDbContext; - } + _emailSender = emailSender; + } [BindProperty] public InputModel Input { get; set; } @@ -52,25 +51,6 @@ namespace WebApp.Areas.Identity.Pages.Account [Display(Name = "Email")] public string Email { get; set; } - [Required] - public string UserName { get; set; } - - [Required] - public string FirstName { get; set; } - - [Required] - public string LastName { get; set; } - public bool IsAdmin { get; set; } - - [Required] - [StringLength(10, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 10)] - public string EGN { get; set; } - - [Required] - [DataType(DataType.PhoneNumber)] - [StringLength(10, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 10)] - public string PhoneNumber { get; set; } - [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] [DataType(DataType.Password)] @@ -95,23 +75,33 @@ namespace WebApp.Areas.Identity.Pages.Account ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); if (ModelState.IsValid) { - var user = new User { Id = Guid.NewGuid().ToString(), UserName = Input.UserName, Email = Input.Email, FirstName = Input.FirstName, LastName = Input.LastName, PhoneNumber = Input.PhoneNumber, PersonalNumber = Input.EGN}; + var user = new User { UserName = Input.Email, Email = Input.Email }; var result = await _userManager.CreateAsync(user, Input.Password); if (result.Succeeded) { - _logger.LogInformation("User created a new account with password."); - await _signInManager.SignInAsync(user, isPersistent: false); - if (Input.IsAdmin == true) + _logger.LogInformation("User created a new account with password."); + + var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + var callbackUrl = Url.Page( + "/Account/ConfirmEmail", + pageHandler: null, + values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl }, + protocol: Request.Scheme); + + await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", + $"Please confirm your account by clicking here."); + + if (_userManager.Options.SignIn.RequireConfirmedAccount) { - await _userManager.AddToRoleAsync(user, "Admin"); + return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl }); } else { - await _userManager.AddToRoleAsync(user, "User"); + await _signInManager.SignInAsync(user, isPersistent: false); + return LocalRedirect(returnUrl); } - return LocalRedirect(returnUrl); } - foreach (var error in result.Errors) { ModelState.AddModelError(string.Empty, error.Description); diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/ResetPassword.cshtml b/RentACar/WebApp/Areas/Identity/Pages/Account/ResetPassword.cshtml new file mode 100644 index 0000000..27bc951 --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/ResetPassword.cshtml @@ -0,0 +1,37 @@ +@page +@model ResetPasswordModel +@{ + ViewData["Title"] = "Reset password"; +} + +

@ViewData["Title"]

+

Reset your password.

+
+
+
+
+
+ +
+ + + +
+
+ + + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs b/RentACar/WebApp/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs new file mode 100644 index 0000000..b42f7c6 --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Data.Entities; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.WebUtilities; + +namespace WebApp.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class ResetPasswordModel : PageModel + { + private readonly UserManager _userManager; + + public ResetPasswordModel(UserManager userManager) + { + _userManager = userManager; + } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + + public string Code { get; set; } + } + + public IActionResult OnGet(string code = null) + { + if (code == null) + { + return BadRequest("A code must be supplied for password reset."); + } + else + { + Input = new InputModel + { + Code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)) + }; + return Page(); + } + } + + public async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _userManager.FindByEmailAsync(Input.Email); + if (user == null) + { + // Don't reveal that the user does not exist + return RedirectToPage("./ResetPasswordConfirmation"); + } + + var result = await _userManager.ResetPasswordAsync(user, Input.Code, Input.Password); + if (result.Succeeded) + { + return RedirectToPage("./ResetPasswordConfirmation"); + } + + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + return Page(); + } + } +} diff --git a/RentACar/WebApp/Areas/Identity/Pages/Account/_StatusMessage.cshtml b/RentACar/WebApp/Areas/Identity/Pages/Account/_StatusMessage.cshtml new file mode 100644 index 0000000..e996841 --- /dev/null +++ b/RentACar/WebApp/Areas/Identity/Pages/Account/_StatusMessage.cshtml @@ -0,0 +1,10 @@ +@model string + +@if (!String.IsNullOrEmpty(Model)) +{ + var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success"; + +} diff --git a/RentACar/WebApp/Areas/Identity/Pages/_ViewImports.cshtml b/RentACar/WebApp/Areas/Identity/Pages/_ViewImports.cshtml index af3a34a..54ec829 100644 --- a/RentACar/WebApp/Areas/Identity/Pages/_ViewImports.cshtml +++ b/RentACar/WebApp/Areas/Identity/Pages/_ViewImports.cshtml @@ -2,3 +2,4 @@ @using WebApp.Areas.Identity @using WebApp.Areas.Identity.Pages @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@using Data.Entities diff --git a/RentACar/WebApp/Areas/Identity/Pages/_ViewStart.cshtml b/RentACar/WebApp/Areas/Identity/Pages/_ViewStart.cshtml index c4284f6..94fd419 100644 --- a/RentACar/WebApp/Areas/Identity/Pages/_ViewStart.cshtml +++ b/RentACar/WebApp/Areas/Identity/Pages/_ViewStart.cshtml @@ -1,3 +1,4 @@ -@{ + +@{ Layout = "/Views/Shared/_Layout.cshtml"; } diff --git a/RentACar/WebApp/Controllers/CarsController.cs b/RentACar/WebApp/Controllers/CarsController.cs index 823c87e..ca51976 100644 --- a/RentACar/WebApp/Controllers/CarsController.cs +++ b/RentACar/WebApp/Controllers/CarsController.cs @@ -7,6 +7,8 @@ using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.EntityFrameworkCore; using Data; using Data.Entities; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Authorization; namespace WebApp.Controllers { @@ -44,6 +46,7 @@ namespace WebApp.Controllers } // GET: Cars/Create + [Authorize(Roles = "Admin")] public IActionResult Create() { return View(); diff --git a/RentACar/WebApp/Startup.cs b/RentACar/WebApp/Startup.cs index 2ba6b7c..416ff74 100644 --- a/RentACar/WebApp/Startup.cs +++ b/RentACar/WebApp/Startup.cs @@ -29,8 +29,7 @@ namespace API public void ConfigureServices(IServiceCollection services) { services.AddDbContext(options => - options.UseSqlServer( - Configuration.GetConnectionString("DefaultConnection"))); + options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddDatabaseDeveloperPageExceptionFilter(); services.AddIdentity(options => { @@ -42,7 +41,10 @@ namespace API options.User.RequireUniqueEmail = false; }) + .AddRoles() + .AddDefaultUI() .AddEntityFrameworkStores(); + services.AddControllersWithViews(); services.AddRazorPages(); } diff --git a/RentACar/WebApp/WebApp.csproj b/RentACar/WebApp/WebApp.csproj index 8dd026f..880cf2b 100644 --- a/RentACar/WebApp/WebApp.csproj +++ b/RentACar/WebApp/WebApp.csproj @@ -7,10 +7,10 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Cryptography.Internal.dll b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Cryptography.Internal.dll index 9882aa4..468e801 100644 Binary files a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Cryptography.Internal.dll and b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Cryptography.Internal.dll differ diff --git a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Cryptography.KeyDerivation.dll b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Cryptography.KeyDerivation.dll index 073a44e..46a9ed6 100644 Binary files a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Cryptography.KeyDerivation.dll and b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Cryptography.KeyDerivation.dll differ diff --git a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Identity.EntityFrameworkCore.dll b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Identity.EntityFrameworkCore.dll index 62437b8..166b3c1 100644 Binary files a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Identity.EntityFrameworkCore.dll and b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Identity.EntityFrameworkCore.dll differ diff --git a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Identity.UI.Views.V4.dll b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Identity.UI.Views.V4.dll index 5c312ea..f26f714 100644 Binary files a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Identity.UI.Views.V4.dll and b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Identity.UI.Views.V4.dll differ diff --git a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Identity.UI.dll b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Identity.UI.dll index df04632..b523dca 100644 Binary files a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Identity.UI.dll and b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.AspNetCore.Identity.UI.dll differ diff --git a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.Abstractions.dll b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.Abstractions.dll index ab5a277..e7af5ca 100644 Binary files a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.Abstractions.dll and b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.Abstractions.dll differ diff --git a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.Design.dll b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.Design.dll index 80ce204..671d92e 100644 Binary files a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.Design.dll and b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.Design.dll differ diff --git a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.Relational.dll b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.Relational.dll index 537d75d..46382b5 100644 Binary files a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.Relational.dll and b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.Relational.dll differ diff --git a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.SqlServer.dll b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.SqlServer.dll index ead5ec3..9580953 100644 Binary files a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.SqlServer.dll and b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.SqlServer.dll differ diff --git a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.dll b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.dll index c26b871..3a48e92 100644 Binary files a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.dll and b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.EntityFrameworkCore.dll differ diff --git a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.Extensions.FileProviders.Embedded.dll b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.Extensions.FileProviders.Embedded.dll index ffa5133..c683d73 100644 Binary files a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.Extensions.FileProviders.Embedded.dll and b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.Extensions.FileProviders.Embedded.dll differ diff --git a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.Extensions.Identity.Core.dll b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.Extensions.Identity.Core.dll index 029060f..3fda6b4 100644 Binary files a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.Extensions.Identity.Core.dll and b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.Extensions.Identity.Core.dll differ diff --git a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.Extensions.Identity.Stores.dll b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.Extensions.Identity.Stores.dll index 55b944c..813204a 100644 Binary files a/RentACar/WebApp/bin/Debug/net5.0/Microsoft.Extensions.Identity.Stores.dll and b/RentACar/WebApp/bin/Debug/net5.0/Microsoft.Extensions.Identity.Stores.dll differ