From b526da60321ce3f64ac139ba2d3322795c10b7a0 Mon Sep 17 00:00:00 2001 From: Vladymor Date: Mon, 17 Mar 2025 18:32:47 +0200 Subject: [PATCH] Added User/Admin Policy Added Jwt-secret key, decoding jwt-key --- Tracker/Controllers/CategoryController.cs | 11 +- .../20250317163008_Init.Designer.cs | 167 ++++++++++++++++++ Tracker/Migrations/20250317163008_Init.cs | 109 ++++++++++++ .../TrackerDbContextModelSnapshot.cs | 164 +++++++++++++++++ Tracker/Program.cs | 33 +++- Tracker/Tracker.csproj | 4 +- Tracker/appsettings.json | 5 +- 7 files changed, 489 insertions(+), 4 deletions(-) create mode 100644 Tracker/Migrations/20250317163008_Init.Designer.cs create mode 100644 Tracker/Migrations/20250317163008_Init.cs create mode 100644 Tracker/Migrations/TrackerDbContextModelSnapshot.cs diff --git a/Tracker/Controllers/CategoryController.cs b/Tracker/Controllers/CategoryController.cs index 40fc52b..c481a84 100644 --- a/Tracker/Controllers/CategoryController.cs +++ b/Tracker/Controllers/CategoryController.cs @@ -1,5 +1,7 @@ -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using System.Security.Claims; using Tracker.Entitites.Enums; using Tracker.Entitites.Filters; using Tracker.Interfaces.ServiceInterfaces; @@ -26,10 +28,13 @@ public CategoryController(ICategoryService categoryService) // _validationService = validationService; //} + [Authorize(Roles = "Admin")] [ServiceFilter(typeof(ValidateCategoryNameFilter))] [HttpPost("CreateCategory")] public async Task CreateCategoryAsync(string name) { + var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (!string.IsNullOrEmpty(name)) { var createdCategory = await _categoryService.CreateCategoryAsync(name); @@ -42,9 +47,12 @@ public async Task CreateCategoryAsync(string name) } } + [Authorize(Policy = "AdminPolicy")] [HttpGet("GetAllCategories")] public async Task GetAllCategoriesAsync() { + var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + var categories = await _categoryService.GetAllCategoriesAsync(); return Ok(categories); @@ -63,6 +71,7 @@ public async Task RenameCategoryAsync(string newName, int id) return Ok(renamedCategory); } + [Authorize(Policy = "UserPolicy")] [HttpDelete("DeleteCategory")] public async Task DeleteCategoryAsync(int id) { diff --git a/Tracker/Migrations/20250317163008_Init.Designer.cs b/Tracker/Migrations/20250317163008_Init.Designer.cs new file mode 100644 index 0000000..885a413 --- /dev/null +++ b/Tracker/Migrations/20250317163008_Init.Designer.cs @@ -0,0 +1,167 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Tracker.Repositories; + +#nullable disable + +namespace Tracker.Migrations +{ + [DbContext(typeof(TrackerDbContext))] + [Migration("20250317163008_Init")] + partial class Init + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Tracker.Entitites.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(true) + .HasColumnType("nvarchar(50)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Categories", (string)null); + + b.HasData( + new + { + Id = 1, + Name = "Programming", + UserId = 0 + }, + new + { + Id = 2, + Name = "English", + UserId = 0 + }); + }); + + modelBuilder.Entity("Tracker.Entitites.Goal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .IsRequired() + .HasPrecision(0) + .HasColumnType("datetime2(0)"); + + b.Property("DailyLimit") + .HasColumnType("time"); + + b.Property("DeadLine") + .IsRequired() + .HasPrecision(0) + .HasColumnType("datetime2(0)"); + + b.Property("FinishedAt") + .HasPrecision(0) + .HasColumnType("datetime2(0)"); + + b.Property("IsFinished") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .IsUnicode(true) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.ToTable("Goals", (string)null); + }); + + modelBuilder.Entity("Tracker.Entitites.Session", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("EndSession") + .HasPrecision(0) + .HasColumnType("datetime2(0)"); + + b.Property("StartSession") + .HasPrecision(0) + .HasColumnType("datetime2(0)"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.ToTable("Sessions", (string)null); + }); + + modelBuilder.Entity("Tracker.Entitites.Goal", b => + { + b.HasOne("Tracker.Entitites.Category", "Category") + .WithMany("Goals") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Tracker.Entitites.Session", b => + { + b.HasOne("Tracker.Entitites.Category", "Category") + .WithMany("Sessions") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Tracker.Entitites.Category", b => + { + b.Navigation("Goals"); + + b.Navigation("Sessions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Tracker/Migrations/20250317163008_Init.cs b/Tracker/Migrations/20250317163008_Init.cs new file mode 100644 index 0000000..a2be349 --- /dev/null +++ b/Tracker/Migrations/20250317163008_Init.cs @@ -0,0 +1,109 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace Tracker.Migrations +{ + /// + public partial class Init : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Categories", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: false), + UserId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Categories", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Goals", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + DeadLine = table.Column(type: "datetime2(0)", precision: 0, nullable: false), + DailyLimit = table.Column(type: "time", nullable: false), + IsFinished = table.Column(type: "bit", nullable: false, defaultValue: false), + CreatedAt = table.Column(type: "datetime2(0)", precision: 0, nullable: false), + FinishedAt = table.Column(type: "datetime2(0)", precision: 0, nullable: true), + CategoryId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Goals", x => x.Id); + table.ForeignKey( + name: "FK_Goals_Categories_CategoryId", + column: x => x.CategoryId, + principalTable: "Categories", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Sessions", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + StartSession = table.Column(type: "datetime2(0)", precision: 0, nullable: false), + EndSession = table.Column(type: "datetime2(0)", precision: 0, nullable: true), + CategoryId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Sessions", x => x.Id); + table.ForeignKey( + name: "FK_Sessions_Categories_CategoryId", + column: x => x.CategoryId, + principalTable: "Categories", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.InsertData( + table: "Categories", + columns: new[] { "Id", "Name", "UserId" }, + values: new object[,] + { + { 1, "Programming", 0 }, + { 2, "English", 0 } + }); + + migrationBuilder.CreateIndex( + name: "IX_Goals_CategoryId", + table: "Goals", + column: "CategoryId"); + + migrationBuilder.CreateIndex( + name: "IX_Sessions_CategoryId", + table: "Sessions", + column: "CategoryId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Goals"); + + migrationBuilder.DropTable( + name: "Sessions"); + + migrationBuilder.DropTable( + name: "Categories"); + } + } +} diff --git a/Tracker/Migrations/TrackerDbContextModelSnapshot.cs b/Tracker/Migrations/TrackerDbContextModelSnapshot.cs new file mode 100644 index 0000000..4dc6cf6 --- /dev/null +++ b/Tracker/Migrations/TrackerDbContextModelSnapshot.cs @@ -0,0 +1,164 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Tracker.Repositories; + +#nullable disable + +namespace Tracker.Migrations +{ + [DbContext(typeof(TrackerDbContext))] + partial class TrackerDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Tracker.Entitites.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(true) + .HasColumnType("nvarchar(50)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Categories", (string)null); + + b.HasData( + new + { + Id = 1, + Name = "Programming", + UserId = 0 + }, + new + { + Id = 2, + Name = "English", + UserId = 0 + }); + }); + + modelBuilder.Entity("Tracker.Entitites.Goal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .IsRequired() + .HasPrecision(0) + .HasColumnType("datetime2(0)"); + + b.Property("DailyLimit") + .HasColumnType("time"); + + b.Property("DeadLine") + .IsRequired() + .HasPrecision(0) + .HasColumnType("datetime2(0)"); + + b.Property("FinishedAt") + .HasPrecision(0) + .HasColumnType("datetime2(0)"); + + b.Property("IsFinished") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .IsUnicode(true) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.ToTable("Goals", (string)null); + }); + + modelBuilder.Entity("Tracker.Entitites.Session", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("EndSession") + .HasPrecision(0) + .HasColumnType("datetime2(0)"); + + b.Property("StartSession") + .HasPrecision(0) + .HasColumnType("datetime2(0)"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.ToTable("Sessions", (string)null); + }); + + modelBuilder.Entity("Tracker.Entitites.Goal", b => + { + b.HasOne("Tracker.Entitites.Category", "Category") + .WithMany("Goals") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Tracker.Entitites.Session", b => + { + b.HasOne("Tracker.Entitites.Category", "Category") + .WithMany("Sessions") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Tracker.Entitites.Category", b => + { + b.Navigation("Goals"); + + b.Navigation("Sessions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Tracker/Program.cs b/Tracker/Program.cs index bc3b32b..fe66b9b 100644 --- a/Tracker/Program.cs +++ b/Tracker/Program.cs @@ -1,5 +1,7 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; +using Microsoft.IdentityModel.Tokens; +using System.Text; using Tracker.Controllers.AutoMappers; using Tracker.Controllers.Validation; using Tracker.DatabaseCatalog.Repositories; @@ -19,6 +21,12 @@ builder.Services.AddScoped(); builder.Services.AddSingleton(); +builder.Services.AddAuthorization(options => +{ + options.AddPolicy("AdminPolicy", policy => policy.RequireRole("Admin")); + options.AddPolicy("UserPolicy", policy => policy.RequireRole("User")); +}); + builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); @@ -44,6 +52,27 @@ options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")); }); +builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + options.Authority = "http://localhost:5085"; + options.Audience = "timetracker_api"; + options.RequireHttpsMetadata = false; + + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = true, + ValidIssuer = "http://localhost:5085", + ValidAudience = "timetracker_api", + + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JWT:Key"])), + + //RoleClaimType = "role", // Ñêàæè äå øóêàòè ðîëü + //NameClaimType = "nameid" // Çà ïîòðåáè äëÿ NameIdentifier + }; + }); + var app = builder.Build(); @@ -55,8 +84,10 @@ app.UseHttpsRedirection(); +app.UseAuthentication(); app.UseAuthorization(); + app.MapControllers(); app.Run(); diff --git a/Tracker/Tracker.csproj b/Tracker/Tracker.csproj index 74b791c..7a223a4 100644 --- a/Tracker/Tracker.csproj +++ b/Tracker/Tracker.csproj @@ -1,4 +1,4 @@ - + net7.0 @@ -8,6 +8,8 @@ + + diff --git a/Tracker/appsettings.json b/Tracker/appsettings.json index b0630e1..085b60d 100644 --- a/Tracker/appsettings.json +++ b/Tracker/appsettings.json @@ -13,5 +13,8 @@ "SessionController_CreateSessionAsync_categoryName": "^[\\p{L}0-9][\\p{L}0-9 '\\-]{1,48}[\\p{L}0-9]$", "CategoryController_RenameCategoryAsync_newName": "^[\\p{L}0-9][\\p{L}0-9 '\\-]{1,48}[\\p{L}0-9]$", "CategoryController_RenameCategoryAsync_id": "^[1-9][0-9]{0,1}$" + }, + "JWT": { + "Key": "BB86C512D863772FD45ABAF5F1C55ATSVHH12AT5VKAEPTY2051AWRH3772FD45ABAF5" + } } -}