diff --git a/FaKrosnoApi/Controllers/CustomerOrdersController.cs b/FaKrosnoApi/Controllers/CustomerOrdersController.cs index 4d0f632..39fa4a3 100644 --- a/FaKrosnoApi/Controllers/CustomerOrdersController.cs +++ b/FaKrosnoApi/Controllers/CustomerOrdersController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using SytelineSaAppEfDataModel.Dtos; using SytelineSaAppEfDataModel.Services; diff --git a/FaKrosnoApi/Controllers/EdiCustomerOrdersController.cs b/FaKrosnoApi/Controllers/EdiCustomerOrdersController.cs index bdea8b7..f3954ef 100644 --- a/FaKrosnoApi/Controllers/EdiCustomerOrdersController.cs +++ b/FaKrosnoApi/Controllers/EdiCustomerOrdersController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using SytelineSaAppEfDataModel.Dtos; using SytelineSaAppEfDataModel.Services; diff --git a/FaKrosnoApi/Controllers/ErrorLogController.cs b/FaKrosnoApi/Controllers/ErrorLogController.cs index 2ead3d5..ce15be4 100644 --- a/FaKrosnoApi/Controllers/ErrorLogController.cs +++ b/FaKrosnoApi/Controllers/ErrorLogController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using SytelineSaAppEfDataModel.Dtos; using SytelineSaAppEfDataModel.Services; @@ -6,6 +7,7 @@ namespace FaKrosnoApi.Controllers { [ApiController] [Route("api/[controller]")] + [Authorize] public class ErrorLogController(IErrorLogService service) : Controller { [HttpGet] diff --git a/FaKrosnoApi/Controllers/ScheduleOrderDetailsController.cs b/FaKrosnoApi/Controllers/ScheduleOrderDetailsController.cs deleted file mode 100644 index 0b83aed..0000000 --- a/FaKrosnoApi/Controllers/ScheduleOrderDetailsController.cs +++ /dev/null @@ -1,18 +0,0 @@ -using FaKrosnoEfDataModel.Dtos; -using FaKrosnoEfDataModel.Services; -using Microsoft.AspNetCore.Mvc; - -namespace FaKrosnoApi.Controllers -{ - [ApiController] - [Route("api/[controller]")] - public class ScheduleOrderDetailsController(IScheduleOrderDetailsService service) : Controller - { - [HttpGet("order/{scheduleOrderId:int}")] - public async Task>> GetByScheduleOrderId(int scheduleOrderId) - { - IEnumerable? scheduleOrderDetails = await service.GetScheduleOrderDetailsAsync(scheduleOrderId); - return Ok(scheduleOrderDetails); - } - } -} diff --git a/FaKrosnoApi/Controllers/ScheduleOrdersController.cs b/FaKrosnoApi/Controllers/ScheduleOrdersController.cs index df96541..5185da8 100644 --- a/FaKrosnoApi/Controllers/ScheduleOrdersController.cs +++ b/FaKrosnoApi/Controllers/ScheduleOrdersController.cs @@ -1,6 +1,4 @@ -using AutoMapper; -using FaKrosnoEfDataModel; -using FaKrosnoEfDataModel.Dtos; +using FaKrosnoEfDataModel.Dtos; using FaKrosnoEfDataModel.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -9,7 +7,6 @@ namespace FaKrosnoApi.Controllers { [ApiController] [Route("api/[controller]")] - //[Authorize] public class ScheduleOrdersController(IScheduleOrderService service) : Controller { [HttpGet] diff --git a/FaKrosnoApi/Controllers/UsersController.cs b/FaKrosnoApi/Controllers/UsersController.cs index 1c152fd..8cd7b9d 100644 --- a/FaKrosnoApi/Controllers/UsersController.cs +++ b/FaKrosnoApi/Controllers/UsersController.cs @@ -1,5 +1,9 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; using FaKrosnoApi.Models; using Microsoft.AspNetCore.Mvc; +using Microsoft.IdentityModel.Tokens; using OrdersManagementDataModel.Dtos; using OrdersManagementDataModel.Services; @@ -7,7 +11,7 @@ namespace FaKrosnoApi.Controllers; [ApiController] [Route("api/[controller]")] -public class UsersController(IUserService service) : Controller +public class UsersController(IUserService service, IConfiguration configuration) : Controller { [HttpGet] public async Task>> GetAll() @@ -30,51 +34,41 @@ public class UsersController(IUserService service) : Controller return user != null ? Ok(user) : NotFound(); } - [HttpPost("authenticate")] - public async Task Authenticate([FromBody] AuthenticateRequestModel? request) + [HttpPost("login")] + public async Task Login([FromBody] AuthenticateRequestModel loginDto) { - if (request == null || string.IsNullOrEmpty(request.Login) || string.IsNullOrEmpty(request.Password)) - { - return BadRequest(new { message = "Login i hasło są wymagane" }); - } - - var user = await service.GetByUsername(request.Login); - - var x = BCrypt.Net.BCrypt.Verify(request.Password, user?.PasswordHash); + // Sprawdź poprawność użytkownika (np. w bazie danych) + var user = await service.GetByUsername(loginDto.Login); - if (user == null || !BCrypt.Net.BCrypt.Verify(request.Password, user.PasswordHash)) + if(user == null || !BCrypt.Net.BCrypt.Verify(loginDto.Password, user.PasswordHash)) { - return Unauthorized(new { message = "Nieprawidłowy login lub hasło" }); + return Unauthorized("Nieprawidłowa nazwa użytkownika lub hasło."); } - - var userDto = new UserDto + + var claims = new[] { - Id = user.Id, - Login = user.Login, - IsTemporaryPassword = user.IsTemporaryPassword, - IsActive = user.IsActive, - ActiveFrom = user.ActiveFrom, - ActiveTo = user.ActiveTo, - Email = user.Email, - FirstName = user.FirstName, - LastName = user.LastName, - CreatedDate = user.CreatedDate, - LastLoginDate = user.LastLoginDate, - FailedLoginAttempts = user.FailedLoginAttempts, - IsLocked = user.IsLocked, - LockoutEndDate = user.LockoutEndDate, - RowPointer = user.RowPointer + new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), + new Claim(ClaimTypes.Name, user.Login), }; - user.LastLoginDate = DateTime.Now; - user.FailedLoginAttempts = 0; - await service.Login(user); + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:Key"])); + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); - return Ok(userDto); + var token = new JwtSecurityToken( + issuer: configuration["Jwt:Issuer"], + audience: configuration["Jwt:Audience"], + claims: claims, + expires: DateTime.Now.AddHours(1), // Token ważny przez 1 godzinę + signingCredentials: creds); + + return Ok(new + { + token = new JwtSecurityTokenHandler().WriteToken(token), + expires = token.ValidTo + }); } - - + [HttpPost] public async Task> Add([FromBody] UserDto user) { diff --git a/FaKrosnoApi/Program.cs b/FaKrosnoApi/Program.cs index 200bf04..4417df4 100644 --- a/FaKrosnoApi/Program.cs +++ b/FaKrosnoApi/Program.cs @@ -12,6 +12,8 @@ using OrdersManagementDataModel; using OrdersManagementDataModel.Services; using SytelineSaAppEfDataModel; using SytelineSaAppEfDataModel.Services; +using NSwag; +using NSwag.Generation.Processors.Security; using FaKrosnoMappingProfile = FaKrosnoEfDataModel.MappingProfile; using SytelineSaAppMappingProfile = SytelineSaAppEfDataModel.MappingProfile; using OrdersManagementMappingProfile = OrdersManagementDataModel.MappingProfile; @@ -30,12 +32,43 @@ builder.Services.Configure(builder.Configuration.GetSection("J builder.WebHost.UseUrls("http://*:5001"); +builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ValidIssuer = builder.Configuration["Jwt:Issuer"], + ValidAudience = builder.Configuration["Jwt:Audience"], + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"] ?? string.Empty)) + }; + }); + builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); + +// Konfiguracja NSwag z obsługą Bearer Token builder.Services.AddOpenApiDocument(config => { config.Title = "FaKrosnoApi"; config.Version = "v1"; + + // Dodaj definicję zabezpieczeń Bearer Token + config.AddSecurity("Bearer", new OpenApiSecurityScheme + { + Name = "Authorization", + Type = OpenApiSecuritySchemeType.Http, + Scheme = "Bearer", + BearerFormat = "JWT", + In = OpenApiSecurityApiKeyLocation.Header, + Description = "Wprowadź token JWT w formacie: Bearer {token}" + }); + + // Zastosuj zabezpieczenia globalnie + config.OperationProcessors.Add(new OperationSecurityScopeProcessor("Bearer")); }); builder.Services.AddHangfire(config => config @@ -52,29 +85,9 @@ builder.Services.AddHangfire(config => config })); builder.Services.AddHangfireServer(); -// Configure AutoMapper builder.Services.AddAutoMapper(typeof(FaKrosnoMappingProfile), typeof(SytelineSaAppMappingProfile), typeof(OrdersManagementMappingProfile)); -// Configure JWT Authentication -builder.Services.AddAuthentication(options => -{ - options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; -}).AddJwtBearer(options => -{ - options.TokenValidationParameters = new TokenValidationParameters - { - ValidateIssuer = true, - ValidateAudience = true, - ValidateLifetime = true, - ValidateIssuerSigningKey = true, - ValidIssuer = builder.Configuration["Jwt:Issuer"], - ValidAudience = builder.Configuration["Jwt:Audience"], - IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])) - }; -}); - builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -104,11 +117,4 @@ app.MapControllers(); app.UseHangfireDashboard(); -// var scopeFactory = app.Services.GetRequiredService(); -// using (var scope = scopeFactory.CreateScope()) -// { -// var scheduledJob = scope.ServiceProvider.GetRequiredService(); -// scheduledJob.Start(); -// } - -app.Run(); \ No newline at end of file +app.Run(); diff --git a/FaKrosnoApi/appsettings.json b/FaKrosnoApi/appsettings.json index 73aa65d..0c30353 100644 --- a/FaKrosnoApi/appsettings.json +++ b/FaKrosnoApi/appsettings.json @@ -1,7 +1,7 @@ { "ConnectionStrings": { - "FaKrosnoConnection": "Server=192.168.0.7;Database=fakrosno;User Id=sa;Password=Tetum#2021!;TrustServerCertificate=true", - "SytelineSaAppConnection": "Server=192.168.0.7;Database=SL_PROD_SA_APP;User Id=sa;Password=Tetum#2021!;TrustServerCertificate=true", + "FaKrosnoConnection": "Server=192.168.0.7;Database=fakrosnotest;User Id=sa;Password=Tetum#2021!;TrustServerCertificate=true", + "SytelineSaAppConnection": "Server=192.168.0.7;Database=SL_PRODTEST_SA_APP;User Id=sa;Password=Tetum#2021!;TrustServerCertificate=true", "OrdersManagementConnection": "Server=192.168.0.7;Database=OrdersManagement;User Id=sa;Password=Tetum#2021!;TrustServerCertificate=true" }, "Logging": { @@ -11,7 +11,7 @@ } }, "Jwt": { - "Key": "ThisIsASecretKeyForJwt", + "Key": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6A7B8C9D0E1F", "Issuer": "FaKrosnoApi", "Audience": "FaKrosnoClient" }, diff --git a/OrdersManagement/Components/App.razor b/OrdersManagement/Components/App.razor index 290d777..9b043e6 100644 --- a/OrdersManagement/Components/App.razor +++ b/OrdersManagement/Components/App.razor @@ -18,19 +18,6 @@ - @inject AuthenticationStateProvider AuthenticationStateProvider - @inject NavigationManager NavigationManager - - @{ - var authState = AuthenticationStateProvider.GetAuthenticationStateAsync().Result; - var user = authState.User; - - if (!(user.Identity is { IsAuthenticated: true }) && NavigationManager.Uri != NavigationManager.BaseUri + "login") - { - NavigationManager.NavigateTo("/login"); - } - } - diff --git a/OrdersManagement/Components/Pages/Admin/Users/LoginModule.razor b/OrdersManagement/Components/Pages/Admin/Users/LoginModule.razor index a78e87c..c40d28c 100644 --- a/OrdersManagement/Components/Pages/Admin/Users/LoginModule.razor +++ b/OrdersManagement/Components/Pages/Admin/Users/LoginModule.razor @@ -1,4 +1,9 @@ @page "/login" + +@rendermode InteractiveServer +@attribute [AllowAnonymous] + +@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Components.Authorization @using OrdersManagement.Models @using Syncfusion.Blazor.Inputs @@ -102,18 +107,18 @@ { Console.WriteLine($"Próba logowania dla: {LoginModel.Login}"); var user = await UserService.AuthenticateUserAsync(LoginModel.Login, LoginModel.Password); + if (user != null) { Console.WriteLine($"Użytkownik {user.Login} znaleziony."); if (user.IsTemporaryPassword) { ShowChangePassword = true; - StateHasChanged(); // Wymagane, aby odświeżyć UI + StateHasChanged(); } else { - await ((CustomAuthenticationStateProvider)AuthenticationStateProvider).MarkUserAsAuthenticated(user); - NavigationManager.NavigateTo("/"); + NavigationManager.NavigateTo("/ScheduleOrders"); } } else diff --git a/OrdersManagement/Components/Pages/CustomerOrder.razor b/OrdersManagement/Components/Pages/CustomerOrder.razor index 6bf4906..cf1dcad 100644 --- a/OrdersManagement/Components/Pages/CustomerOrder.razor +++ b/OrdersManagement/Components/Pages/CustomerOrder.razor @@ -1,5 +1,7 @@ @page "/CustomerOrder/{CustomerOrderId:guid}" +@rendermode InteractiveServer + @inject CustomerOrderService CustomerOrderService @inject ScheduleOrderService ScheduleOrderService @using SytelineSaAppEfDataModel.Dtos diff --git a/OrdersManagement/Components/Pages/CustomerOrders.razor b/OrdersManagement/Components/Pages/CustomerOrders.razor index fbd1c0c..354a824 100644 --- a/OrdersManagement/Components/Pages/CustomerOrders.razor +++ b/OrdersManagement/Components/Pages/CustomerOrders.razor @@ -1,4 +1,7 @@ @page "/CustomerOrders" + +@rendermode InteractiveServer + @inject CustomerOrderService CustomerOrderService @inject NavigationManager NavigationManager @using SytelineSaAppEfDataModel.Dtos diff --git a/OrdersManagement/Components/Pages/EdiCustomerOrder.razor b/OrdersManagement/Components/Pages/EdiCustomerOrder.razor index ac00f2b..43159fa 100644 --- a/OrdersManagement/Components/Pages/EdiCustomerOrder.razor +++ b/OrdersManagement/Components/Pages/EdiCustomerOrder.razor @@ -1,6 +1,9 @@ @page "/EdiCustomerOrder/{CustomerOrderId:guid}" +@rendermode InteractiveServer + @inject EdiCustomerOrderService EdiCustomerOrderService +@using Microsoft.AspNetCore.Authorization @using SytelineSaAppEfDataModel.Dtos @using Syncfusion.Blazor.Grids @using Syncfusion.Blazor.Cards diff --git a/OrdersManagement/Components/Pages/EdiCustomerOrders.razor b/OrdersManagement/Components/Pages/EdiCustomerOrders.razor index 4633c99..42eb571 100644 --- a/OrdersManagement/Components/Pages/EdiCustomerOrders.razor +++ b/OrdersManagement/Components/Pages/EdiCustomerOrders.razor @@ -1,6 +1,6 @@ @page "/EdiCustomerOrders" -@attribute [Authorize] +@rendermode InteractiveServer @inject EdiCustomerOrderService EdiCustomerOrderService @inject NavigationManager NavigationManager @@ -108,7 +108,7 @@ @if (_responses.Any(x => x.Status == 1)) { - foreach (ResponseModel response in _responses.Where(x => x.Status == 1)) + foreach (ResponseModel? response in _responses.Where(x => x.Status == 1)) {

Zamówienie EDI @response.Identifier zostało poprawnie zaksięgowane w Zamówieniach klienta pod numerem '@response.ExternalIdentifier'

@@ -116,7 +116,7 @@ } @if (_responses.Any(x => x.Status == 0)) { - foreach (ResponseModel response in _responses.Where(x => x.Status == 0)) + foreach (ResponseModel? response in _responses.Where(x => x.Status == 0)) {

Błąd: Zamówienie EDI @response.Identifier nie zostało poprawnie zaksięgowane w Zamówieniach klienta.
Lista błędów:
@response.Message

@@ -138,7 +138,7 @@ private IEnumerable _ediCustomerOrders = []; private List _selectedEdiCustomerOrders = new(); - private List _responses = new(); + private List _responses = new(); private bool _isVisible; private bool? _filter = false; @@ -161,11 +161,11 @@ { if (!_selectedEdiCustomerOrders.Any()) return false; - _responses = new List(); + _responses = new List(); foreach (EdiCustomerOrderDto selectedEdiCustomerOrder in _selectedEdiCustomerOrders) { - ResponseModel response = await EdiCustomerOrderService.SendOrderToSyteline(selectedEdiCustomerOrder.RowPointer, selectedEdiCustomerOrder.CustomerOrderNumber); + ResponseModel? response = await EdiCustomerOrderService.SendOrderToSyteline(selectedEdiCustomerOrder.RowPointer, selectedEdiCustomerOrder.CustomerOrderNumber); if (response.Status == 1) { diff --git a/OrdersManagement/Components/Pages/RedirectToLogin.razor b/OrdersManagement/Components/Pages/RedirectToLogin.razor new file mode 100644 index 0000000..d8f1d13 --- /dev/null +++ b/OrdersManagement/Components/Pages/RedirectToLogin.razor @@ -0,0 +1,8 @@ +@inject NavigationManager Navigation + +@code { + protected override void OnInitialized() + { + Navigation.NavigateTo("/login"); + } +} \ No newline at end of file diff --git a/OrdersManagement/Components/Pages/ScheduleOrder.razor b/OrdersManagement/Components/Pages/ScheduleOrder.razor index 27bb5fa..6cbccb3 100644 --- a/OrdersManagement/Components/Pages/ScheduleOrder.razor +++ b/OrdersManagement/Components/Pages/ScheduleOrder.razor @@ -1,8 +1,10 @@ @page "/ScheduleOrder/{ScheduleOrderId:int}" +@rendermode InteractiveServer + +@using Microsoft.AspNetCore.Authorization @using Syncfusion.Blazor.Grids @using Syncfusion.Blazor.Lists -@inject ScheduleOrderDetailsService ScheduleOrderDetailsService @inject ScheduleOrderService ScheduleOrderService
diff --git a/OrdersManagement/Components/Pages/ScheduleOrders.razor b/OrdersManagement/Components/Pages/ScheduleOrders.razor index a628b44..bf33fc8 100644 --- a/OrdersManagement/Components/Pages/ScheduleOrders.razor +++ b/OrdersManagement/Components/Pages/ScheduleOrders.razor @@ -1,14 +1,14 @@ @page "/" -@attribute [Authorize] - -@inherits LayoutComponentBase - @using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Authorization +@using Microsoft.IdentityModel.Tokens @using OrdersManagement.Components.Pages.Shared @using Syncfusion.Blazor.Grids @inject ScheduleOrderService ScheduleOrderService +@inject AuthenticationStateProvider AuthStateProvider +@* //@inject AuthTokenHandler TokenHandler *@
Zamówienia DELFOR
@@ -18,17 +18,19 @@ @code { private IEnumerable _scheduleOrders = []; - protected override async Task OnInitializedAsync() + protected override async Task OnAfterRenderAsync(bool firstRender) { - _scheduleOrders = await FetchScheduleOrdersAsync(); + if (firstRender) + { + _scheduleOrders = await FetchScheduleOrdersAsync(); + StateHasChanged(); + } } private async Task> FetchScheduleOrdersAsync() { _scheduleOrders = await ScheduleOrderService.GetScheduleOrdersAsync() ?? new List(); _scheduleOrders = _scheduleOrders.OrderByDescending(x => x.LastUpdateDate).ToList(); - StateHasChanged(); - return _scheduleOrders; } @@ -38,5 +40,4 @@ { _gridRef = grid; } - -} +} \ No newline at end of file diff --git a/OrdersManagement/CustomAuthenticationStateProvider.cs b/OrdersManagement/CustomAuthenticationStateProvider.cs index e50c952..98cbae0 100644 --- a/OrdersManagement/CustomAuthenticationStateProvider.cs +++ b/OrdersManagement/CustomAuthenticationStateProvider.cs @@ -1,29 +1,62 @@ +using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; +using Blazored.LocalStorage; using Microsoft.AspNetCore.Components.Authorization; -using OrdersManagementDataModel.Dtos; namespace OrdersManagement; -public class CustomAuthenticationStateProvider : AuthenticationStateProvider +public class CustomAuthenticationStateProvider(ILocalStorageService localStorage) : AuthenticationStateProvider { - private UserDto? _currentUser; + private string? _token; + private ClaimsPrincipal _currentUser = new(new ClaimsIdentity()); public override Task GetAuthenticationStateAsync() { - var identity = _currentUser != null ? new ClaimsIdentity([new Claim(ClaimTypes.Name, _currentUser.Login)], "CustomAuth") : new ClaimsIdentity(); - return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(identity))); + if (string.IsNullOrEmpty(_token)) + { + return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()))); + } + + var handler = new JwtSecurityTokenHandler(); + var jwtToken = handler.ReadJwtToken(_token); + var identity = new ClaimsIdentity(jwtToken.Claims, "jwt"); + _currentUser = new ClaimsPrincipal(identity); + return Task.FromResult(new AuthenticationState(_currentUser)); } - public Task MarkUserAsAuthenticated(UserDto? user) + public async Task MarkUserAsAuthenticated(string? token) { - _currentUser = user; - NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); - return Task.CompletedTask; + _token = token; + await localStorage.SetItemAsync("authToken", token); + var handler = new JwtSecurityTokenHandler(); + var jwtToken = handler.ReadJwtToken(token); + var identity = new ClaimsIdentity(jwtToken.Claims, "jwt"); + _currentUser = new ClaimsPrincipal(identity); + NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(_currentUser))); } - public void MarkUserAsLoggedOut() + public async Task MarkUserAsLoggedOut() { - _currentUser = null; - NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); + _token = null; + await localStorage.RemoveItemAsync("authToken"); + _currentUser = new ClaimsPrincipal(new ClaimsIdentity()); + NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(_currentUser))); } + + public async Task InitializeAsync() + { + _token = await localStorage.GetItemAsync("authToken"); + + if (!string.IsNullOrEmpty(_token)) + { + var handler = new JwtSecurityTokenHandler(); + var jwtToken = handler.ReadJwtToken(_token); + var identity = new ClaimsIdentity(jwtToken.Claims, "jwt"); + _currentUser = new ClaimsPrincipal(identity); + NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(_currentUser))); + } + } + + public string? GetToken() => _token; } + diff --git a/OrdersManagement/Models/LoginResponseDto.cs b/OrdersManagement/Models/LoginResponseDto.cs new file mode 100644 index 0000000..5c47747 --- /dev/null +++ b/OrdersManagement/Models/LoginResponseDto.cs @@ -0,0 +1,7 @@ +namespace OrdersManagement.Models; + +public class LoginResponseDto +{ + public string? Token { get; set; } + public DateTime Expires { get; set; } +} \ No newline at end of file diff --git a/OrdersManagement/OrdersManagement.csproj b/OrdersManagement/OrdersManagement.csproj index 44aa5a3..3176ae0 100644 --- a/OrdersManagement/OrdersManagement.csproj +++ b/OrdersManagement/OrdersManagement.csproj @@ -9,6 +9,8 @@ + + diff --git a/OrdersManagement/Program.cs b/OrdersManagement/Program.cs index ea58e38..4d26c63 100644 --- a/OrdersManagement/Program.cs +++ b/OrdersManagement/Program.cs @@ -1,44 +1,58 @@ -using Microsoft.AspNetCore.Authentication.Cookies; +using Blazored.LocalStorage; +using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.IdentityModel.Tokens; using OrdersManagement; using OrdersManagement.Components; using OrdersManagement.Services; using Syncfusion.Blazor; +using System.Text; var builder = WebApplication.CreateBuilder(args); string faKrosnoApiUrl = builder.Configuration["FaKrosnoApiUrl"] ?? "http://localhost:5001"; - builder.Services.AddSyncfusionBlazor(); builder.Services.AddBlazorBootstrap(); +builder.Services.AddBlazoredLocalStorage(); -builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) - .AddCookie(options => +builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => { - options.LoginPath = "/login"; - options.LogoutPath = "/login"; + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ValidIssuer = builder.Configuration["Jwt:Issuer"], + ValidAudience = builder.Configuration["Jwt:Audience"], + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])) + }; }); builder.Services.AddAuthorizationCore(); builder.Services.AddScoped(); -builder.Services.AddScoped(); -builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(faKrosnoApiUrl) }); -builder.Services.AddRazorComponents().AddInteractiveServerComponents(); +builder.Services.AddHttpClient("FaKrosnoApi", client => +{ + client.BaseAddress = new Uri(faKrosnoApiUrl); + client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")); +}); +builder.Services.AddRazorComponents() + .AddInteractiveServerComponents(); -// Usługi aplikacji builder.Services.AddScoped(); -builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); var app = builder.Build(); -// Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error", createScopeForErrors: true); @@ -49,7 +63,6 @@ app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); -// Włącz autentykację i autoryzację w pipeline app.UseAuthentication(); app.UseAuthorization(); diff --git a/OrdersManagement/Services/CustomerOrderService.cs b/OrdersManagement/Services/CustomerOrderService.cs index aa44b82..2adf0d2 100644 --- a/OrdersManagement/Services/CustomerOrderService.cs +++ b/OrdersManagement/Services/CustomerOrderService.cs @@ -1,17 +1,36 @@ +using Microsoft.AspNetCore.Components.Authorization; using SytelineSaAppEfDataModel.Dtos; namespace OrdersManagement.Services; -public class CustomerOrderService(HttpClient httpClient) +public class CustomerOrderService( + IHttpClientFactory httpClientFactory, + AuthenticationStateProvider authenticationStateProvider) + : ServiceBase(httpClientFactory, authenticationStateProvider) { public async Task?> GetCustomerOrdersAsync() { - return await httpClient.GetFromJsonAsync>("api/CustomerOrders"); + try + { + return await GetListAsync("api/CustomerOrders"); + } + catch (HttpRequestException ex) + { + Console.WriteLine($"Błąd HTTP w GetCustomerOrdersAsync: {ex.Message}"); + return null; + } } public async Task GetCustomerOrderAsync(Guid customerOrderNumber) { - return await httpClient.GetFromJsonAsync( - $"api/CustomerOrders/by-order-number/?customerOrderNumber={customerOrderNumber}"); + try + { + return await GetByIdAsync($"api/CustomerOrders/by-order-number/?customerOrderNumber={customerOrderNumber}"); + } + catch (HttpRequestException ex) + { + Console.WriteLine($"Błąd HTTP w GetScheduleOrderAsync: {ex.Message}"); + return null; + } } } \ No newline at end of file diff --git a/OrdersManagement/Services/EdiCustomerOrderService.cs b/OrdersManagement/Services/EdiCustomerOrderService.cs index 60811ab..01c9507 100644 --- a/OrdersManagement/Services/EdiCustomerOrderService.cs +++ b/OrdersManagement/Services/EdiCustomerOrderService.cs @@ -1,41 +1,63 @@ -using OrdersManagement.Models; +using Microsoft.AspNetCore.Components.Authorization; +using OrdersManagement.Models; using SytelineSaAppEfDataModel.Dtos; namespace OrdersManagement.Services { - public class EdiCustomerOrderService(HttpClient httpClient) + public class EdiCustomerOrderService(IHttpClientFactory httpClientFactory, AuthenticationStateProvider authenticationStateProvider, ErrorLogService errorLogService) : ServiceBase(httpClientFactory, authenticationStateProvider) { public async Task?> GetEdiCustomerOrdersAsync() { - return await httpClient.GetFromJsonAsync>("api/EdiCustomerOrders"); + try + { + return await GetListAsync("api/EdiCustomerOrders"); + } + catch (HttpRequestException ex) + { + Console.WriteLine($"Błąd HTTP w GetEdiCustomerOrdersAsync: {ex.Message}"); + return null; + } } public async Task GetEdiCustomerOrderAsync(Guid customerOrderNumber) { - return await httpClient.GetFromJsonAsync( - $"api/EdiCustomerOrders/by-order-number/?customerOrderNumber={customerOrderNumber}"); + try + { + return await GetByIdAsync($"api/EdiCustomerOrders/by-order-number/?customerOrderNumber={customerOrderNumber}"); + } + catch (HttpRequestException ex) + { + Console.WriteLine($"Błąd HTTP w GetEdiCustomerOrdersAsync: {ex.Message}"); + return null; + } } - public async Task SendOrderToSyteline(Guid customerOrderNumber, string orderNumber) + public async Task SendOrderToSyteline(Guid customerOrderNumber, string orderNumber) { - HttpResponseMessage responseMessage = await httpClient.PostAsync( - $"api/EdiCustomerOrders/send-to-syteline?customerOrderNumber={customerOrderNumber}", null); - - if (responseMessage.IsSuccessStatusCode) + try { - return new ResponseModel(1, orderNumber, null, null); + HttpResponseMessage responseMessage = await PostAsync($"api/EdiCustomerOrders/send-to-syteline?customerOrderNumber={customerOrderNumber}"); + + if (responseMessage.IsSuccessStatusCode) + { + return new ResponseModel(1, orderNumber, null, null); + } + + string? errorMessage = null; + IEnumerable? logs = await errorLogService.GetErrorLogsAsync(customerOrderNumber); + + if (logs != null) + { + errorMessage = string.Join("\r\n", logs.Select(x => x.ErrMsg)); + } + + return new ResponseModel(0, orderNumber, errorMessage, null); } - - string? errorMessage = null; - IEnumerable? logs = await httpClient.GetFromJsonAsync>( - $"api/ErrorLog/by-order-number/?customerOrderNumber={customerOrderNumber}"); - - if (logs != null) + catch (HttpRequestException ex) { - errorMessage = string.Join("\r\n", logs.Select(x => x.ErrMsg)); + Console.WriteLine($"Błąd HTTP w GetEdiCustomerOrdersAsync: {ex.Message}"); + return null; } - - return new ResponseModel(0, orderNumber, errorMessage, null); } } } diff --git a/OrdersManagement/Services/ErrorLogService.cs b/OrdersManagement/Services/ErrorLogService.cs new file mode 100644 index 0000000..fe1823e --- /dev/null +++ b/OrdersManagement/Services/ErrorLogService.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Components.Authorization; +using SytelineSaAppEfDataModel.Dtos; + +namespace OrdersManagement.Services; + +public class ErrorLogService( + IHttpClientFactory httpClientFactory, + AuthenticationStateProvider authenticationStateProvider) + : ServiceBase(httpClientFactory, authenticationStateProvider) +{ + public async Task?> GetErrorLogsAsync(Guid customerOrderNumber) + { + try + { + return await GetListAsync($"api/CustomerOrders/by-order-number/?customerOrderNumber={customerOrderNumber}"); + } + catch (HttpRequestException ex) + { + Console.WriteLine($"Błąd HTTP w GetCustomerOrdersAsync: {ex.Message}"); + return null; + } + } +} \ No newline at end of file diff --git a/OrdersManagement/Services/ScheduleOrderDetailsService.cs b/OrdersManagement/Services/ScheduleOrderDetailsService.cs deleted file mode 100644 index 94114cc..0000000 --- a/OrdersManagement/Services/ScheduleOrderDetailsService.cs +++ /dev/null @@ -1,13 +0,0 @@ -using FaKrosnoEfDataModel.Dtos; - -namespace OrdersManagement.Services -{ - public class ScheduleOrderDetailsService(HttpClient httpClient) - { - public async Task?> GetScheduleOrderDetails(int scheduleOrderId) - { - return await httpClient.GetFromJsonAsync>( - $"api/scheduleOrderDetails/order/{scheduleOrderId}"); - } - } -} diff --git a/OrdersManagement/Services/ScheduleOrderService.cs b/OrdersManagement/Services/ScheduleOrderService.cs index a3bc588..7fc47d4 100644 --- a/OrdersManagement/Services/ScheduleOrderService.cs +++ b/OrdersManagement/Services/ScheduleOrderService.cs @@ -1,18 +1,38 @@ -using FaKrosnoEfDataModel.Dtos; +using System.Net.Http.Headers; +using Blazored.LocalStorage; +using FaKrosnoEfDataModel.Dtos; +using Microsoft.AspNetCore.Components.Authorization; -namespace OrdersManagement.Services +namespace OrdersManagement.Services; + +public class ScheduleOrderService( + IHttpClientFactory httpClientFactory, + AuthenticationStateProvider authenticationStateProvider) + : ServiceBase(httpClientFactory, authenticationStateProvider) { - public class ScheduleOrderService(HttpClient httpClient) + public async Task?> GetScheduleOrdersAsync() { - public async Task?> GetScheduleOrdersAsync() + try { - return await httpClient.GetFromJsonAsync>("api/ScheduleOrders"); + return await GetListAsync("api/ScheduleOrders"); } - - public async Task GetScheduleOrderAsync(int scheduleOrderId) + catch (HttpRequestException ex) { - return await httpClient.GetFromJsonAsync( - $"api/ScheduleOrders/{scheduleOrderId}"); + Console.WriteLine($"Błąd HTTP w GetScheduleOrdersAsync: {ex.Message}"); + return null; } } -} + + public async Task GetScheduleOrderAsync(int scheduleOrderId) + { + try + { + return await GetByIdAsync($"api/ScheduleOrders/{scheduleOrderId}"); + } + catch (HttpRequestException ex) + { + Console.WriteLine($"Błąd HTTP w GetScheduleOrderAsync: {ex.Message}"); + return null; + } + } +} \ No newline at end of file diff --git a/OrdersManagement/Services/ServiceBase.cs b/OrdersManagement/Services/ServiceBase.cs new file mode 100644 index 0000000..be71ff5 --- /dev/null +++ b/OrdersManagement/Services/ServiceBase.cs @@ -0,0 +1,52 @@ +using Microsoft.AspNetCore.Components.Authorization; + +namespace OrdersManagement.Services; + +public class ServiceBase where T : class +{ + private readonly AuthenticationStateProvider _authenticationStateProvider; + private readonly HttpClient _httpClient; + + protected ServiceBase(IHttpClientFactory httpClientFactory, AuthenticationStateProvider authenticationStateProvider) + { + _authenticationStateProvider = authenticationStateProvider; + _httpClient = httpClientFactory.CreateClient("FaKrosnoApi"); + + _ = Configure(); + } + + protected async Task?> GetListAsync(string request) + { + var response = await _httpClient.GetAsync(request); + response.EnsureSuccessStatusCode(); + return await response.Content.ReadFromJsonAsync>(); + } + + protected async Task GetByIdAsync(string request) + { + var response = await _httpClient.GetAsync(request); + response.EnsureSuccessStatusCode(); + return await response.Content.ReadFromJsonAsync(); + } + + protected async Task PostAsync(string request) + { + var response = await _httpClient.PostAsync(request, null); + response.EnsureSuccessStatusCode(); + return response; + } + + private async Task Configure() + { + var token = await GetToken(); + + _httpClient.DefaultRequestHeaders.Authorization = + new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); + } + + private async Task GetToken() + { + await ((CustomAuthenticationStateProvider)_authenticationStateProvider).InitializeAsync(); + return ((CustomAuthenticationStateProvider)_authenticationStateProvider).GetToken(); + } +} \ No newline at end of file diff --git a/OrdersManagement/Services/UserService.cs b/OrdersManagement/Services/UserService.cs index 1f4c341..07b7c67 100644 --- a/OrdersManagement/Services/UserService.cs +++ b/OrdersManagement/Services/UserService.cs @@ -1,42 +1,62 @@ +using Blazored.LocalStorage; +using Microsoft.AspNetCore.Components.Authorization; +using OrdersManagement.Models; using OrdersManagementDataModel.Dtos; namespace OrdersManagement.Services; -public class UserService(HttpClient httpClient) +public class UserService(IHttpClientFactory clientFactory, AuthenticationStateProvider authStateProvider) { + private readonly HttpClient _httpClient = clientFactory.CreateClient("FaKrosnoApi"); + public async Task?> GetUsersAsync() { - return await httpClient.GetFromJsonAsync>("api/Users"); - } - - public async Task AuthenticateUserAsync(string login, string password) - { - var response = await httpClient.PostAsJsonAsync("api/users/authenticate", new { Login = login, Password = password }); - return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync() : null; + return await _httpClient.GetFromJsonAsync>("api/Users"); } + public async Task AuthenticateUserAsync(string login, string password) + { + try + { + var response = await _httpClient.PostAsJsonAsync("api/Users/login", new { Login = login, Password = password }); + response.EnsureSuccessStatusCode(); + var result = await response.Content.ReadFromJsonAsync(); + + if (result?.Token == null) return null; + + await ((CustomAuthenticationStateProvider)authStateProvider).MarkUserAsAuthenticated(result.Token); + return await GetUserByUsernameAsync(login); + + } + catch (Exception ex) + { + Console.WriteLine($"Błąd logowania: {ex.Message}"); + return null; + } + } + public async Task GetUserAsync(Guid userId) { - return await httpClient.GetFromJsonAsync($"api/Users/by-id/?id={userId}"); + return await _httpClient.GetFromJsonAsync($"api/Users/by-id/?id={userId}"); } public async Task GetUserByUsernameAsync(string username) { - return await httpClient.GetFromJsonAsync($"api/Users/by-username/?username={username}"); + return await _httpClient.GetFromJsonAsync($"api/Users/by-username/?username={username}"); } public async Task AddUserAsync(UserDto user) { - return await httpClient.PostAsJsonAsync("api/Users", user); + return await _httpClient.PostAsJsonAsync("api/Users", user); } public async Task UpdateUserAsync(UserDto user) { - await httpClient.PutAsJsonAsync("api/Users", user); + await _httpClient.PutAsJsonAsync("api/Users", user); } public async Task DeleteUserAsync(Guid userId) { - await httpClient.DeleteAsync($"api/Users/?id={userId}"); + await _httpClient.DeleteAsync($"api/Users/?id={userId}"); } } \ No newline at end of file diff --git a/OrdersManagement/appsettings.json b/OrdersManagement/appsettings.json index 10f68b8..2b7db47 100644 --- a/OrdersManagement/appsettings.json +++ b/OrdersManagement/appsettings.json @@ -5,5 +5,10 @@ "Microsoft.AspNetCore": "Warning" } }, + "Jwt": { + "Key": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6A7B8C9D0E1F", + "Issuer": "FaKrosnoApi", + "Audience": "FaKrosnoClient" + }, "AllowedHosts": "*" }