From 29e6efc9e04f9b66c2334c9c4a2e76be52d09e49 Mon Sep 17 00:00:00 2001 From: ivanszabo Date: Wed, 12 Mar 2025 09:48:57 +0100 Subject: [PATCH] JwtToken 1.0 --- src/WorkFlowCheck.API/appsettings.json | 6 + .../Mappings/MapperProfile.cs | 3 +- .../Services/Interfaces/IUserService.cs | 1 + src/WorkFlowCheck.BL/Services/UserService.cs | 71 +- src/WorkFlowCheck.Common/DTO/RoleDTO.cs | 8 + src/WorkFlowCheck.Common/DTO/UserDTO.cs | 3 +- src/WorkFlowCheck.DL/AppDbContext.cs | 10 +- src/WorkFlowCheck.DL/Entities/Role.cs | 3 + src/WorkFlowCheck.DL/Entities/User.cs | 16 +- src/WorkFlowCheck.DL/Entities/UserRole.cs | 2 +- ...250312080859_UserRoleExtension.Designer.cs | 624 ++++++++++++++++++ .../20250312080859_UserRoleExtension.cs | 99 +++ .../Migrations/AppDbContextModelSnapshot.cs | 41 +- .../Middleware/JwtService.cs | 11 +- src/WorkFlowCheck.Web/appsettings.json | 4 +- 15 files changed, 867 insertions(+), 35 deletions(-) create mode 100644 src/WorkFlowCheck.Common/DTO/RoleDTO.cs create mode 100644 src/WorkFlowCheck.DL/Migrations/20250312080859_UserRoleExtension.Designer.cs create mode 100644 src/WorkFlowCheck.DL/Migrations/20250312080859_UserRoleExtension.cs diff --git a/src/WorkFlowCheck.API/appsettings.json b/src/WorkFlowCheck.API/appsettings.json index 7f24213..9dda47b 100644 --- a/src/WorkFlowCheck.API/appsettings.json +++ b/src/WorkFlowCheck.API/appsettings.json @@ -3,6 +3,12 @@ "DefaultConnection": "Data Source=WS2016DC\\SQLEXPRESS;Initial Catalog=WFCUAT;Integrated Security=True;Connect Timeout=30;Encrypt=False;Trust Server Certificate=False;Application Intent=ReadWrite;Multi Subnet Failover=False" }, "ApiKey": "RUJeLSpSMzVASUdaRCEzUyYxRSE0VyFISFRSJC0zRzhLM1hCSDU=", + "Jwt": { + "SecretKey": "9aLxV2t7RpM8BfKqZwXy3GehYJDUNTCrHkVZ", + "Issuer": "https://wfcapi.nuvolar.hu/", + "Audience": "wfc-application", + "TokenLifetimeMinutes": 30 + }, "Logging": { "LogLevel": { "Default": "Information", diff --git a/src/WorkFlowCheck.BL/Mappings/MapperProfile.cs b/src/WorkFlowCheck.BL/Mappings/MapperProfile.cs index 1f00a2e..a76caca 100644 --- a/src/WorkFlowCheck.BL/Mappings/MapperProfile.cs +++ b/src/WorkFlowCheck.BL/Mappings/MapperProfile.cs @@ -14,7 +14,8 @@ namespace WorkFlowCheck.BL.Mappings { public MapperProfile() { - CreateMap(); + CreateMap().ForMember(dest => dest.RoleDTO, opt => opt.MapFrom(src => src.UserRoles.Select(ur => ur.Role))); + CreateMap(); CreateMap() .ForMember(dest => dest.PasswordHash, opt => opt.MapFrom()); diff --git a/src/WorkFlowCheck.BL/Services/Interfaces/IUserService.cs b/src/WorkFlowCheck.BL/Services/Interfaces/IUserService.cs index 59945d3..32132b9 100644 --- a/src/WorkFlowCheck.BL/Services/Interfaces/IUserService.cs +++ b/src/WorkFlowCheck.BL/Services/Interfaces/IUserService.cs @@ -11,5 +11,6 @@ namespace WorkFlowCheck.BL.Services.Interfaces { Task GetUser(int Id); Task Authenticate(string UserName, string Password); + string GenerateJwtToken(UserDTO userDTO); } } diff --git a/src/WorkFlowCheck.BL/Services/UserService.cs b/src/WorkFlowCheck.BL/Services/UserService.cs index 0fdb2ae..ab60dbf 100644 --- a/src/WorkFlowCheck.BL/Services/UserService.cs +++ b/src/WorkFlowCheck.BL/Services/UserService.cs @@ -10,6 +10,11 @@ using WorkFlowCheck.Common.DTO; using WorkFlowCheck.BL.Services.Interfaces; using WorkFlowCheck.DL; using WorkFlowCheck.Common.Security; +using Microsoft.IdentityModel.Tokens; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using WorkFlowCheck.DL.Entities; +using Microsoft.Extensions.Configuration; namespace WorkFlowCheck.BL.Services { @@ -17,14 +22,17 @@ namespace WorkFlowCheck.BL.Services { private AppDbContext _dbContext; private IMapper _mapper; - public UserService(AppDbContext dbContext, IMapper mapper) + private IConfiguration _configuration; + + public UserService(AppDbContext dbContext, IMapper mapper, IConfiguration configuration) { _dbContext = dbContext; _mapper = mapper; + _configuration = configuration; } public async Task GetUser(int Id) { - var retVal = new UserDTO(); + var retVal = new UserDTO() { RoleDTO = new List() }; try { @@ -41,15 +49,37 @@ namespace WorkFlowCheck.BL.Services return retVal; } - public async Task Authenticate(string userName, string password) + public async Task Authenticate(string userName, string password) { - var retVal = new UserDTO(); + var retVal = new UserDTO() { RoleDTO = new List() }; try { - var user = await _dbContext.Users.Where(w => w.UserName == userName).FirstOrDefaultAsync(); + var user = await _dbContext.Users + .Include(i => i.UserRoles) + .ThenInclude(i => i.Role) + .Where(w => w.UserName == userName) + .FirstOrDefaultAsync(); if (user != null && PasswordHasher.VerifyPassword(user.PasswordHash, password)) { + + var userDTO = new UserDTO() + { + Id = user.Id, + UserName = user.UserName, + RoleDTO = new List(), + }; + foreach (var item in user.UserRoles) + { + userDTO.RoleDTO.Add(new RoleDTO() + { + Id = item.Role.Id, + RoleName = item.Role.RoleName, + }); + } + user.JwtToken = GenerateJwtToken(userDTO); + await _dbContext.SaveChangesAsync(); + retVal = _mapper.Map(user); } } @@ -59,5 +89,36 @@ namespace WorkFlowCheck.BL.Services } return retVal; } + + public string GenerateJwtToken(UserDTO userDTO) + { + var jwtSettings = _configuration.GetSection("Jwt"); + var secretKey = jwtSettings["SecretKey"]; + var issuer = jwtSettings["Issuer"]; + var audience = jwtSettings["Audience"]; + var tokenLifetime = int.Parse(jwtSettings["TokenLifetimeMinutes"]); + + var claims = new List + { + new Claim(ClaimTypes.Name, userDTO.UserName), + new Claim(ClaimTypes.NameIdentifier, userDTO.Id.ToString()) + }; + + foreach (var role in userDTO.RoleDTO) + { + claims.Add(new Claim(ClaimTypes.Role, role.RoleName)); + } + + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey)); + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + var token = new JwtSecurityToken( + issuer: issuer, + audience: audience, + claims: claims, + expires: DateTime.UtcNow.AddMinutes(tokenLifetime), + signingCredentials: creds); + + return new JwtSecurityTokenHandler().WriteToken(token); + } } } diff --git a/src/WorkFlowCheck.Common/DTO/RoleDTO.cs b/src/WorkFlowCheck.Common/DTO/RoleDTO.cs new file mode 100644 index 0000000..06d88b5 --- /dev/null +++ b/src/WorkFlowCheck.Common/DTO/RoleDTO.cs @@ -0,0 +1,8 @@ +namespace WorkFlowCheck.Common.DTO +{ + public class RoleDTO + { + public int Id { get; set; } + public string RoleName { get; set; } = null!; + } +} diff --git a/src/WorkFlowCheck.Common/DTO/UserDTO.cs b/src/WorkFlowCheck.Common/DTO/UserDTO.cs index 5a39d65..36584b7 100644 --- a/src/WorkFlowCheck.Common/DTO/UserDTO.cs +++ b/src/WorkFlowCheck.Common/DTO/UserDTO.cs @@ -8,7 +8,8 @@ public string LastName { get; set; } = null!; public string UserName { get;set; } = null!; public string Password { get; set; } - public string JwtToken { get; set; } = null!; + public string JwtToken { get; set; } = null!; + public required ICollection RoleDTO { get; set; } = new List(); } } diff --git a/src/WorkFlowCheck.DL/AppDbContext.cs b/src/WorkFlowCheck.DL/AppDbContext.cs index 162926a..c02e6d5 100644 --- a/src/WorkFlowCheck.DL/AppDbContext.cs +++ b/src/WorkFlowCheck.DL/AppDbContext.cs @@ -16,20 +16,22 @@ namespace WorkFlowCheck.DL { public class AppDbContext : DbContext { - private readonly IRequestContext _requestContext; + #if !DEBUG - public AppDbContext(DbContextOptions options, IRequestContext requestContext) +private readonly IRequestContext _requestContext; +public AppDbContext(DbContextOptions options, IRequestContext requestContext) : base(options) { _requestContext = requestContext; } #endif #if DEBUG + private dynamic _requestContext; public AppDbContext(DbContextOptions options) : base(options) { - var _requestContext = new { CurrentUserEmail = "", CurrentUsername = "", TraceId = 0 }; + _requestContext = new { CurrentUserEmail = "", CurrentUsername = "admin", TraceId = 0 }; } #endif #region Entities DbSet @@ -137,6 +139,7 @@ namespace WorkFlowCheck.DL CreatedBy = "System", LastModAt = DateTime.UtcNow, LastModBy = "System", + JwtToken = "" }, new User { @@ -151,6 +154,7 @@ namespace WorkFlowCheck.DL CreatedBy = "System", LastModAt = DateTime.UtcNow, LastModBy = "System", + JwtToken = "" } ); } diff --git a/src/WorkFlowCheck.DL/Entities/Role.cs b/src/WorkFlowCheck.DL/Entities/Role.cs index f190af3..a5eb213 100644 --- a/src/WorkFlowCheck.DL/Entities/Role.cs +++ b/src/WorkFlowCheck.DL/Entities/Role.cs @@ -12,6 +12,9 @@ namespace WorkFlowCheck.DL.Entities { public int Id { get; set; } public string RoleName { get; set; } = null!; + + public virtual ICollection UserRoles { get; set; } = new List(); + public bool IsDeleted { get ; set ; } public DateTime LastModAt { get ; set ; } public string LastModBy { get ; set ; } = null!; diff --git a/src/WorkFlowCheck.DL/Entities/User.cs b/src/WorkFlowCheck.DL/Entities/User.cs index 2555c17..656872c 100644 --- a/src/WorkFlowCheck.DL/Entities/User.cs +++ b/src/WorkFlowCheck.DL/Entities/User.cs @@ -3,7 +3,7 @@ using WorkFlowCheck.DL.Interfaces; namespace WorkFlowCheck.DL.Entities { - public class User:IAuditableEntity,ISoftDeletableEntity + public class User : IAuditableEntity, ISoftDeletableEntity { public int Id { get; set; } public string Email { get; set; } = null!; @@ -11,10 +11,14 @@ namespace WorkFlowCheck.DL.Entities public string LastName { get; set; } = null!; public string UserName { get; set; } = null!; public string? PasswordHash { get; set; } = null; - public DateTime LastModAt { get ; set ; } - public string LastModBy { get ; set ; } = null!; - public DateTime CreatedAt { get ; set ; } - public string CreatedBy { get ; set ; } = null!; - public bool IsDeleted { get ; set ; } + public string JwtToken { get; set; } = null!; + + public virtual ICollection UserRoles { get; set; } = new List(); + + public DateTime LastModAt { get; set; } + public string LastModBy { get; set; } = null!; + public DateTime CreatedAt { get; set; } + public string CreatedBy { get; set; } = null!; + public bool IsDeleted { get; set; } } } diff --git a/src/WorkFlowCheck.DL/Entities/UserRole.cs b/src/WorkFlowCheck.DL/Entities/UserRole.cs index 0eebcc8..90753a2 100644 --- a/src/WorkFlowCheck.DL/Entities/UserRole.cs +++ b/src/WorkFlowCheck.DL/Entities/UserRole.cs @@ -9,7 +9,7 @@ namespace WorkFlowCheck.DL.Entities public class UserRole { public int Id { get; set; } - public int RoleId { get; set; } = 0; + public int? RoleId { get; set; } = 0; public virtual Role Role { get; set; } = null!; public int UserId { get; set; } public virtual User User { get; set; } = null!; diff --git a/src/WorkFlowCheck.DL/Migrations/20250312080859_UserRoleExtension.Designer.cs b/src/WorkFlowCheck.DL/Migrations/20250312080859_UserRoleExtension.Designer.cs new file mode 100644 index 0000000..67cbb26 --- /dev/null +++ b/src/WorkFlowCheck.DL/Migrations/20250312080859_UserRoleExtension.Designer.cs @@ -0,0 +1,624 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using WorkFlowCheck.DL; + +#nullable disable + +namespace WorkFlowCheck.DL.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20250312080859_UserRoleExtension")] + partial class UserRoleExtension + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.CheckListHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CheckListTemplateHeaderId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DocumentNumber") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("GuidNumber") + .HasColumnType("uniqueidentifier"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsStorno") + .HasColumnType("bit"); + + b.Property("LastModAt") + .HasColumnType("datetime2"); + + b.Property("LastModBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ShortName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("CheckListTemplateHeaderId"); + + b.ToTable("CheckListHeader", (string)null); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.CheckListRow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Answer") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CheckListHeaderId") + .HasColumnType("int"); + + b.Property("CheckListTemplateRowId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("GuidNumber") + .HasColumnType("uniqueidentifier"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModAt") + .HasColumnType("datetime2"); + + b.Property("LastModBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Photo") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.HasKey("Id"); + + b.HasIndex("CheckListHeaderId"); + + b.HasIndex("CheckListTemplateRowId"); + + b.ToTable("CheckListRow", (string)null); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.CheckListTemplateHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModAt") + .HasColumnType("datetime2"); + + b.Property("LastModBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ShortName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("CheckListTemplateHeader", (string)null); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.CheckListTemplateRow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AnswerType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CheckListTemplateHeaderId") + .HasColumnType("int"); + + b.Property("CheckPointId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EquipmentId") + .HasColumnType("int"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModAt") + .HasColumnType("datetime2"); + + b.Property("LastModBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("OperationDescription") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RowIndex") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CheckListTemplateHeaderId"); + + b.HasIndex("CheckPointId"); + + b.HasIndex("EquipmentId"); + + b.ToTable("CheckListTemplateRow", (string)null); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.CheckPoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModAt") + .HasColumnType("datetime2"); + + b.Property("LastModBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ShortName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("CheckPoint", (string)null); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.Equipment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EquipmentNumber") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModAt") + .HasColumnType("datetime2"); + + b.Property("LastModBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ShortName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Equipment", (string)null); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FullName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModAt") + .HasColumnType("datetime2"); + + b.Property("LastModBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Location", (string)null); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModAt") + .HasColumnType("datetime2"); + + b.Property("LastModBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RoleName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Roles", (string)null); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.RoleCheckListTemplateHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CheckListTemplateHeaderId") + .HasColumnType("int"); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CheckListTemplateHeaderId"); + + b.HasIndex("RoleId"); + + b.ToTable("RoleCheckListTemplateHeader", (string)null); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("JwtToken") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastModAt") + .HasColumnType("datetime2"); + + b.Property("LastModBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Users", (string)null); + + b.HasData( + new + { + Id = 1, + CreatedAt = new DateTime(2025, 3, 12, 8, 8, 57, 555, DateTimeKind.Utc).AddTicks(6125), + CreatedBy = "System", + Email = "admin@nuvolar.hu", + FirstName = "Administrator", + IsDeleted = false, + JwtToken = "", + LastModAt = new DateTime(2025, 3, 12, 8, 8, 57, 555, DateTimeKind.Utc).AddTicks(6129), + LastModBy = "System", + LastName = "System", + PasswordHash = "eGM0NUREZnJ0ISFFRDIxMKE5TYwthDBuUsTUEO1fBnCR3VdSCmdz47ue0RoVvnkY", + UserName = "admin" + }, + new + { + Id = 2, + CreatedAt = new DateTime(2025, 3, 12, 8, 8, 57, 566, DateTimeKind.Utc).AddTicks(6128), + CreatedBy = "System", + Email = "user@nuvolar.hu", + FirstName = "User", + IsDeleted = false, + JwtToken = "", + LastModAt = new DateTime(2025, 3, 12, 8, 8, 57, 566, DateTimeKind.Utc).AddTicks(6129), + LastModBy = "System", + LastName = "System", + PasswordHash = "eGM0NUREZnJ0ISFFRDIxMPwM2D9sQSj7zmaSBIsOGe0I9hBFwCGPVbyrYMA5EnKG", + UserName = "user" + }); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.UserRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("UserId"); + + b.ToTable("UserRoles", (string)null); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.CheckListHeader", b => + { + b.HasOne("WorkFlowCheck.DL.Entities.CheckListTemplateHeader", "CheckListTemplateHeader") + .WithMany() + .HasForeignKey("CheckListTemplateHeaderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CheckListTemplateHeader"); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.CheckListRow", b => + { + b.HasOne("WorkFlowCheck.DL.Entities.CheckListHeader", "CheckListHeader") + .WithMany("CheckListRows") + .HasForeignKey("CheckListHeaderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WorkFlowCheck.DL.Entities.CheckListTemplateRow", "CheckListTemplateRow") + .WithMany() + .HasForeignKey("CheckListTemplateRowId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CheckListHeader"); + + b.Navigation("CheckListTemplateRow"); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.CheckListTemplateRow", b => + { + b.HasOne("WorkFlowCheck.DL.Entities.CheckListTemplateHeader", "CheckListTemplateHeader") + .WithMany("CheckListTemplateRows") + .HasForeignKey("CheckListTemplateHeaderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WorkFlowCheck.DL.Entities.CheckPoint", "CheckPoint") + .WithMany("CheckListTemplateRows") + .HasForeignKey("CheckPointId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WorkFlowCheck.DL.Entities.Equipment", "Equipment") + .WithMany() + .HasForeignKey("EquipmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CheckListTemplateHeader"); + + b.Navigation("CheckPoint"); + + b.Navigation("Equipment"); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.RoleCheckListTemplateHeader", b => + { + b.HasOne("WorkFlowCheck.DL.Entities.CheckListTemplateHeader", "CheckListTemplateHeader") + .WithMany() + .HasForeignKey("CheckListTemplateHeaderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WorkFlowCheck.DL.Entities.Role", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CheckListTemplateHeader"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.UserRole", b => + { + b.HasOne("WorkFlowCheck.DL.Entities.Role", "Role") + .WithMany("UserRoles") + .HasForeignKey("RoleId"); + + b.HasOne("WorkFlowCheck.DL.Entities.User", "User") + .WithMany("UserRoles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.CheckListHeader", b => + { + b.Navigation("CheckListRows"); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.CheckListTemplateHeader", b => + { + b.Navigation("CheckListTemplateRows"); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.CheckPoint", b => + { + b.Navigation("CheckListTemplateRows"); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.Role", b => + { + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.User", b => + { + b.Navigation("UserRoles"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/WorkFlowCheck.DL/Migrations/20250312080859_UserRoleExtension.cs b/src/WorkFlowCheck.DL/Migrations/20250312080859_UserRoleExtension.cs new file mode 100644 index 0000000..bcee77f --- /dev/null +++ b/src/WorkFlowCheck.DL/Migrations/20250312080859_UserRoleExtension.cs @@ -0,0 +1,99 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace WorkFlowCheck.DL.Migrations +{ + /// + public partial class UserRoleExtension : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_UserRoles_Roles_RoleId", + table: "UserRoles"); + + migrationBuilder.AddColumn( + name: "JwtToken", + table: "Users", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + + migrationBuilder.AlterColumn( + name: "RoleId", + table: "UserRoles", + type: "int", + nullable: true, + oldClrType: typeof(int), + oldType: "int"); + + migrationBuilder.UpdateData( + table: "Users", + keyColumn: "Id", + keyValue: 1, + columns: new[] { "CreatedAt", "JwtToken", "LastModAt" }, + values: new object[] { new DateTime(2025, 3, 12, 8, 8, 57, 555, DateTimeKind.Utc).AddTicks(6125), "", new DateTime(2025, 3, 12, 8, 8, 57, 555, DateTimeKind.Utc).AddTicks(6129) }); + + migrationBuilder.UpdateData( + table: "Users", + keyColumn: "Id", + keyValue: 2, + columns: new[] { "CreatedAt", "JwtToken", "LastModAt" }, + values: new object[] { new DateTime(2025, 3, 12, 8, 8, 57, 566, DateTimeKind.Utc).AddTicks(6128), "", new DateTime(2025, 3, 12, 8, 8, 57, 566, DateTimeKind.Utc).AddTicks(6129) }); + + migrationBuilder.AddForeignKey( + name: "FK_UserRoles_Roles_RoleId", + table: "UserRoles", + column: "RoleId", + principalTable: "Roles", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_UserRoles_Roles_RoleId", + table: "UserRoles"); + + migrationBuilder.DropColumn( + name: "JwtToken", + table: "Users"); + + migrationBuilder.AlterColumn( + name: "RoleId", + table: "UserRoles", + type: "int", + nullable: false, + defaultValue: 0, + oldClrType: typeof(int), + oldType: "int", + oldNullable: true); + + migrationBuilder.UpdateData( + table: "Users", + keyColumn: "Id", + keyValue: 1, + columns: new[] { "CreatedAt", "LastModAt" }, + values: new object[] { new DateTime(2025, 3, 3, 15, 47, 43, 353, DateTimeKind.Utc).AddTicks(3654), new DateTime(2025, 3, 3, 15, 47, 43, 353, DateTimeKind.Utc).AddTicks(3659) }); + + migrationBuilder.UpdateData( + table: "Users", + keyColumn: "Id", + keyValue: 2, + columns: new[] { "CreatedAt", "LastModAt" }, + values: new object[] { new DateTime(2025, 3, 3, 15, 47, 43, 364, DateTimeKind.Utc).AddTicks(9120), new DateTime(2025, 3, 3, 15, 47, 43, 364, DateTimeKind.Utc).AddTicks(9122) }); + + migrationBuilder.AddForeignKey( + name: "FK_UserRoles_Roles_RoleId", + table: "UserRoles", + column: "RoleId", + principalTable: "Roles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/src/WorkFlowCheck.DL/Migrations/AppDbContextModelSnapshot.cs b/src/WorkFlowCheck.DL/Migrations/AppDbContextModelSnapshot.cs index 7e3c25a..7988c34 100644 --- a/src/WorkFlowCheck.DL/Migrations/AppDbContextModelSnapshot.cs +++ b/src/WorkFlowCheck.DL/Migrations/AppDbContextModelSnapshot.cs @@ -416,6 +416,10 @@ namespace WorkFlowCheck.DL.Migrations b.Property("IsDeleted") .HasColumnType("bit"); + b.Property("JwtToken") + .IsRequired() + .HasColumnType("nvarchar(max)"); + b.Property("LastModAt") .HasColumnType("datetime2"); @@ -442,12 +446,13 @@ namespace WorkFlowCheck.DL.Migrations new { Id = 1, - CreatedAt = new DateTime(2025, 3, 3, 15, 47, 43, 353, DateTimeKind.Utc).AddTicks(3654), + CreatedAt = new DateTime(2025, 3, 12, 8, 8, 57, 555, DateTimeKind.Utc).AddTicks(6125), CreatedBy = "System", Email = "admin@nuvolar.hu", FirstName = "Administrator", IsDeleted = false, - LastModAt = new DateTime(2025, 3, 3, 15, 47, 43, 353, DateTimeKind.Utc).AddTicks(3659), + JwtToken = "", + LastModAt = new DateTime(2025, 3, 12, 8, 8, 57, 555, DateTimeKind.Utc).AddTicks(6129), LastModBy = "System", LastName = "System", PasswordHash = "eGM0NUREZnJ0ISFFRDIxMKE5TYwthDBuUsTUEO1fBnCR3VdSCmdz47ue0RoVvnkY", @@ -456,12 +461,13 @@ namespace WorkFlowCheck.DL.Migrations new { Id = 2, - CreatedAt = new DateTime(2025, 3, 3, 15, 47, 43, 364, DateTimeKind.Utc).AddTicks(9120), + CreatedAt = new DateTime(2025, 3, 12, 8, 8, 57, 566, DateTimeKind.Utc).AddTicks(6128), CreatedBy = "System", Email = "user@nuvolar.hu", FirstName = "User", IsDeleted = false, - LastModAt = new DateTime(2025, 3, 3, 15, 47, 43, 364, DateTimeKind.Utc).AddTicks(9122), + JwtToken = "", + LastModAt = new DateTime(2025, 3, 12, 8, 8, 57, 566, DateTimeKind.Utc).AddTicks(6129), LastModBy = "System", LastName = "System", PasswordHash = "eGM0NUREZnJ0ISFFRDIxMPwM2D9sQSj7zmaSBIsOGe0I9hBFwCGPVbyrYMA5EnKG", @@ -477,7 +483,7 @@ namespace WorkFlowCheck.DL.Migrations SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - b.Property("RoleId") + b.Property("RoleId") .HasColumnType("int"); b.Property("UserId") @@ -531,7 +537,7 @@ namespace WorkFlowCheck.DL.Migrations .IsRequired(); b.HasOne("WorkFlowCheck.DL.Entities.CheckPoint", "CheckPoint") - .WithMany() + .WithMany("CheckListTemplateRows") .HasForeignKey("CheckPointId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -571,13 +577,11 @@ namespace WorkFlowCheck.DL.Migrations modelBuilder.Entity("WorkFlowCheck.DL.Entities.UserRole", b => { b.HasOne("WorkFlowCheck.DL.Entities.Role", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .WithMany("UserRoles") + .HasForeignKey("RoleId"); b.HasOne("WorkFlowCheck.DL.Entities.User", "User") - .WithMany() + .WithMany("UserRoles") .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -596,6 +600,21 @@ namespace WorkFlowCheck.DL.Migrations { b.Navigation("CheckListTemplateRows"); }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.CheckPoint", b => + { + b.Navigation("CheckListTemplateRows"); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.Role", b => + { + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("WorkFlowCheck.DL.Entities.User", b => + { + b.Navigation("UserRoles"); + }); #pragma warning restore 612, 618 } } diff --git a/src/WorkFlowCheck.Web/Middleware/JwtService.cs b/src/WorkFlowCheck.Web/Middleware/JwtService.cs index 4a09d88..b689a35 100644 --- a/src/WorkFlowCheck.Web/Middleware/JwtService.cs +++ b/src/WorkFlowCheck.Web/Middleware/JwtService.cs @@ -10,16 +10,16 @@ namespace WorkFlowCheck.Web.Middleware { public class JwtService { - + private const string SecretKey = "super_secret_key"; public readonly IConfiguration _configuration; private readonly HttpClient _httpClient; - public JwtService(HttpClient httpClient,IConfiguration configuration) + public JwtService(HttpClient httpClient, IConfiguration configuration) { _configuration = configuration; - _httpClient = httpClient; - } + _httpClient = httpClient; + } public async Task AuthenticateUserAsync(UserDTO userDTO) { @@ -32,7 +32,7 @@ namespace WorkFlowCheck.Web.Middleware var response = await Authenticate(userDTO.UserName, userDTO.Password); if (response != null && response.IsSuccess) { - + var claims = new[] { new Claim(ClaimTypes.Name, userDTO.UserName), @@ -67,6 +67,7 @@ namespace WorkFlowCheck.Web.Middleware Id = 0, UserName = username, Password = password, + RoleDTO = new List() }; // HTTP POST kérés küldése diff --git a/src/WorkFlowCheck.Web/appsettings.json b/src/WorkFlowCheck.Web/appsettings.json index 33dd27a..51ea160 100644 --- a/src/WorkFlowCheck.Web/appsettings.json +++ b/src/WorkFlowCheck.Web/appsettings.json @@ -2,9 +2,9 @@ "ApiBaseUrl": "https://wfcapi.nuvolar.hu/", "ApiKey": "RUJeLSpSMzVASUdaRCEzUyYxRSE0VyFISFRSJC0zRzhLM1hCSDU=", "Jwt": { - "SecretKey": "super_secret_key", + "SecretKey": "9aLxV2t7RpM8BfKqZwXy3GehYJDUNTCrHkVZ", "Issuer": "https://wfcapi.nuvolar.hu/", - "Audience": "https://mywebapp.com", + "Audience": "wfc-application", "TokenLifetimeMinutes": 30 }, "Logging": {