diff --git a/App.razor b/App.razor index 843f201..b8905ee 100644 --- a/App.razor +++ b/App.razor @@ -1,4 +1,42 @@ - + + + + + + + + + + + + + Not found + +

Sorry, there's nothing at this address.

+
+
+
+
+ +@* + + + + + + + Not found + +

Sorry, there's nothing at this address.

+
+
+
+
*@ + + +@* @@ -8,4 +46,4 @@

Sorry, there's nothing at this address.

-
\ No newline at end of file +
*@ \ No newline at end of file diff --git a/Data/LoginData.cs b/Data/LoginData.cs new file mode 100644 index 0000000..9ea831c --- /dev/null +++ b/Data/LoginData.cs @@ -0,0 +1,8 @@ +namespace KWWebInvApp.Data +{ + public class LoginData + { + public string username { get; set; } + public string password { get; set; } + } +} diff --git a/Data/UserServices.cs b/Data/UserServices.cs deleted file mode 100644 index e2f138b..0000000 --- a/Data/UserServices.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace KWWebInvApp.Data -{ - public class UserServices - { - public UserInfoServices.userinfo? CurrentUser { get; set; } = null; - - public bool IsAuthenticated() - { - if (CurrentUser == null) return false; - else return true; - } - } -} diff --git a/Pages/BranchMaintenance/InventoryCorrection.razor b/Pages/BranchMaintenance/InventoryCorrection.razor index b847b67..b12810e 100644 --- a/Pages/BranchMaintenance/InventoryCorrection.razor +++ b/Pages/BranchMaintenance/InventoryCorrection.razor @@ -1,8 +1,12 @@ @page "/brmaintenance/invtycorrection" + +@attribute [Authorize] + @using KWWebInvApp.Data; +@using KWWebInvApp.Services; @inject IDialogService DialogService; -@inject UserServices userService; +@inject IAuthenticationService AuthenticationStateService; Branch Inventory Correction Branch Inventory Correction @@ -138,8 +142,10 @@ else disableStatusGetRecord = true; disableStatusSave = true; + var currentUser = await AuthenticationStateService.GetAuthenticatedUserAsync(); + branchItemLedger = await branchItemLedgerServiceClient.GetRemoteDataByBrCodeModelnoAsync(selectedBranch, modelNo); - newRemarks = $"Manually edit by {userService?.CurrentUser?.fullName}. {DateTime.Now:MM/dd/yyyy hh:mm:sstt}"; + newRemarks = $"Manually edit by {currentUser?.fullName}. {DateTime.Now:MM/dd/yyyy hh:mm:sstt}"; disableStatusGetRecord = false; disableStatusSave = false; @@ -153,7 +159,9 @@ else disableStatusGetRecord = true; disableStatusSave = true; - branchItemLedger.remarks = $"|Manually edit by {userService?.CurrentUser?.fullName}. {DateTime.Now:MM/dd/yyyy hh:mm:sstt} [{branchItemLedger.beginningqty} {branchItemLedger.inqty} {branchItemLedger.outqty} {branchItemLedger.sales} {branchItemLedger.adjustment} {branchItemLedger.endingqty}]"; + var currentUser = await AuthenticationStateService.GetAuthenticatedUserAsync(); + + branchItemLedger.remarks = $"|Manually edit by {currentUser?.fullName}. {DateTime.Now:MM/dd/yyyy hh:mm:sstt} [{branchItemLedger.beginningqty} {branchItemLedger.inqty} {branchItemLedger.outqty} {branchItemLedger.sales} {branchItemLedger.adjustment} {branchItemLedger.endingqty}]"; int result = await branchItemLedgerServiceClient.UpdateRemoteBranchItemLedgerAsync(branchItemLedger); if(result > 0) diff --git a/Pages/Index.razor b/Pages/Index.razor index 04e2e56..67296c7 100644 --- a/Pages/Index.razor +++ b/Pages/Index.razor @@ -1,8 +1,6 @@ @page "/" -@using KWWebInvApp.Data -@inject UserServices userServices -@inject NavigationManager navigationManager +@attribute [Authorize] Index diff --git a/Pages/Items/ItemDetails.razor b/Pages/Items/ItemDetails.razor index f5c265c..5b41d40 100644 --- a/Pages/Items/ItemDetails.razor +++ b/Pages/Items/ItemDetails.razor @@ -1,5 +1,7 @@ @page "/items/itemdetails" +@attribute [Authorize] +

Item Details

@code { diff --git a/Pages/Items/ItemSearch.razor b/Pages/Items/ItemSearch.razor index d09c097..6f9a678 100644 --- a/Pages/Items/ItemSearch.razor +++ b/Pages/Items/ItemSearch.razor @@ -1,5 +1,7 @@ @page "/items/tracesearch" +@attribute [Authorize] +

ItemSearch

@code { diff --git a/Pages/Items/TraceHistory.razor b/Pages/Items/TraceHistory.razor index 9ce539b..e7b3b0c 100644 --- a/Pages/Items/TraceHistory.razor +++ b/Pages/Items/TraceHistory.razor @@ -1,5 +1,7 @@ @page "/items/tracehistory" +@attribute [Authorize] +

TraceHistory

@code { diff --git a/Pages/UserLogin.razor b/Pages/UserLogin.razor new file mode 100644 index 0000000..d39d1f1 --- /dev/null +++ b/Pages/UserLogin.razor @@ -0,0 +1,85 @@ +@page "/userlogin" +@using KWWebInvApp.Data; +@using KWWebInvApp.Services; +@using Microsoft.AspNetCore.WebUtilities; + +@inject IAuthenticationService AuthenticationStateService +@inject AuthenticationStateProvider AuthenticationStateProvider +@inject NavigationManager NavigationManager + +User Login +Enter your credential for verification + + + + + + + Login + Welcome to Merchandise and SAC System + @if (error != null) + { + @error + } + + + + + @submitButtonText + Reset + + + + + + + +@code { + bool submitButtonDisabled = false; + string? error, submitButtonText = "Login"; + LoginData loginData = new(); + + protected override async Task OnInitializedAsync() + { + + } + + async Task SubmitLogin() + { + error = null; + + waitingButton(true); + var webAuthenticationStateProvider = (WebAuthenticationStateProvider)AuthenticationStateProvider; + var loginSuccess = await webAuthenticationStateProvider.LoginAsync(loginData); + + if (loginSuccess) + { + var uri = NavigationManager.ToAbsoluteUri(NavigationManager.Uri); + if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("returnUrl", out var returnURL)) + NavigationManager.NavigateTo($"/{returnURL}"); + else + NavigationManager.NavigateTo("/"); + } + else + error = "Invalid username or password. Please try again."; + + waitingButton(false); + } + + void waitingButton(bool waiting = false) + { + if(waiting) + { + submitButtonDisabled = true; + submitButtonText = "Please Wait..."; + } + else + { + submitButtonDisabled = false; + submitButtonText = "Login"; + } + } +} diff --git a/Program.cs b/Program.cs index 076c785..96f38f7 100644 --- a/Program.cs +++ b/Program.cs @@ -1,5 +1,8 @@ using KWWebInvApp.Data; +using KWWebInvApp.Services; using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Hosting.StaticWebAssets; using MudBlazor.Services; @@ -8,13 +11,21 @@ var builder = WebApplication.CreateBuilder(args); StaticWebAssetsLoader.UseStaticWebAssets(builder.Environment, builder.Configuration); +builder.Services.AddSession(options => +{ + options.IdleTimeout = TimeSpan.FromDays(1); // Set the session timeout as needed +}); + // Add services to the container. builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); builder.Services.AddSingleton(); -builder.Services.AddSingleton(); builder.Services.AddMudServices(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + var app = builder.Build(); // Configure the HTTP request pipeline. @@ -25,12 +36,15 @@ if (!app.Environment.IsDevelopment()) app.UseHsts(); } + app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); +app.UseSession(); + app.MapBlazorHub(); app.MapFallbackToPage("/_Host"); diff --git a/Services/AuthenticationService.cs b/Services/AuthenticationService.cs new file mode 100644 index 0000000..2e5d717 --- /dev/null +++ b/Services/AuthenticationService.cs @@ -0,0 +1,47 @@ +using KWWebInvApp.Data; +using UserInfoServices; + +namespace KWWebInvApp.Services +{ + public class AuthenticationService : IAuthenticationService + { + private UserInfoServices.userinfo? CurrentUser; + + public async Task GetAuthenticatedUserAsync() + { + return CurrentUser; + } + + public async Task SetAuthenticatedUserAsync(UserInfoServices.userinfo currentUser) + { + CurrentUser = currentUser; + } + + public async Task LoginAsync(LoginData loginData) + { + if (!string.IsNullOrWhiteSpace(loginData.username) && !string.IsNullOrWhiteSpace(loginData.password)) + { + UserInfoServices.UserInfoServiceClient userInfoServiceClient = new(); + UserInfoServices.userinfo userAttemptingToLogin = new UserInfoServices.userinfo() + { + username = loginData.username, + pass = await userInfoServiceClient.md5EncodingAsync(loginData.password) + }; + + CurrentUser = await userInfoServiceClient.AuthenticateUserAsync(userAttemptingToLogin); + + if (CurrentUser == null) + return false; + else + return true; + } + + return false; + } + + public async Task LogoutAsync() + { + CurrentUser = null; + } + } +} diff --git a/Services/IAuthenticationService.cs b/Services/IAuthenticationService.cs new file mode 100644 index 0000000..1487b4f --- /dev/null +++ b/Services/IAuthenticationService.cs @@ -0,0 +1,12 @@ +using KWWebInvApp.Data; + +namespace KWWebInvApp.Services +{ + public interface IAuthenticationService + { + Task GetAuthenticatedUserAsync(); + Task SetAuthenticatedUserAsync(UserInfoServices.userinfo userInfo); + Task LoginAsync(LoginData loginData); + Task LogoutAsync(); + } +} diff --git a/Services/WebAuthenticationStateProvider.cs b/Services/WebAuthenticationStateProvider.cs new file mode 100644 index 0000000..3462446 --- /dev/null +++ b/Services/WebAuthenticationStateProvider.cs @@ -0,0 +1,145 @@ +using KWWebInvApp.Data; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage; +using System.Security.Claims; + +namespace KWWebInvApp.Services +{ + public class WebAuthenticationStateProvider : AuthenticationStateProvider + { + private readonly IAuthenticationService _authService; + private readonly ProtectedSessionStorage _sessionStorage; + + public WebAuthenticationStateProvider(IAuthenticationService authService, ProtectedSessionStorage sessionStorage) + { + _authService = authService; + _sessionStorage = sessionStorage; + } + + /* + + public override async Task GetAuthenticationStateAsync() + { + var user = await _authService.GetAuthenticatedUserAsync(); + + if (user == null) + return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); + + var claims = new List + { + new Claim(ClaimTypes.Name, user.username), + new Claim(ClaimTypes.Role, user.userlvl.ToString()) + // Add any other claims based on your application's requirements + }; + + var identity = new ClaimsIdentity(claims, "CustomAuth"); + var principal = new ClaimsPrincipal(identity); + + return new AuthenticationState(principal); + } + + + public override async Task GetAuthenticationStateAsync() + { + var principal = new ClaimsPrincipal(new ClaimsIdentity()); + UserInfoServices.userinfo? user = null; + + try + { + var userSessionResult = await _sessionStorage.GetAsync("UserInfoSession"); + user = (userSessionResult.Success) ? userSessionResult.Value : null; + + if (user == null) + return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); + + + var claims = new List + { + new Claim(ClaimTypes.Name, user.username), + new Claim(ClaimTypes.Role, user.userlvl.ToString()) + // Add any other claims based on your application's requirements + }; + + var identity = new ClaimsIdentity(claims, "KWWebInvAppAuth"); + principal = new ClaimsPrincipal(identity); + + } + catch { return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); } + + var authState = new AuthenticationState(principal); + + return authState; + } + + */ + + public override async Task GetAuthenticationStateAsync() + { + var principal = new ClaimsPrincipal(new ClaimsIdentity()); + UserInfoServices.userinfo? user = null; + + try + { + user = await _authService.GetAuthenticatedUserAsync(); + + if (user == null) + { + var userSessionResult = await _sessionStorage.GetAsync("UserInfoSession"); + user = (userSessionResult.Success) ? userSessionResult.Value : null; + + if (user == null) + return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); + else + await _authService.SetAuthenticatedUserAsync(user); + } + + var claims = new List + { + new Claim(ClaimTypes.Name, user.username), + new Claim(ClaimTypes.Role, user.userlvl.ToString()) + // Add any other claims based on your application's requirements + }; + + var identity = new ClaimsIdentity(claims, "KWWebInvAppAuth"); + principal = new ClaimsPrincipal(identity); + + } + catch { return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); } + + return new AuthenticationState(principal); + } + + + + public async Task LoginAsync(LoginData loginData) + { + bool loginSuccess = await _authService.LoginAsync(loginData); + + if(loginSuccess) + { + UserInfoServices.userinfo user = await _authService.GetAuthenticatedUserAsync(); + + // Supply sessionStorage with user data first before using SetAuthenticationState. + // authState will rely on the value of UserInfoSession stored here + await _sessionStorage.SetAsync("UserInfoSession", user); + + AuthenticationState authState = await GetAuthenticationStateAsync(); + SetAuthenticationState(authState); + } + + return loginSuccess; + } + + public async Task LogoutAsync() + { + await _authService.LogoutAsync(); + await _sessionStorage.DeleteAsync("UserInfoSession"); + SetAuthenticationState(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()))); + } + + public void SetAuthenticationState(AuthenticationState authState) + { + NotifyAuthenticationStateChanged(Task.FromResult(authState)); + } + } +} diff --git a/Shared/Login.razor b/Shared/Login.razor deleted file mode 100644 index 97b810b..0000000 --- a/Shared/Login.razor +++ /dev/null @@ -1,81 +0,0 @@ -@using KWWebInvApp.Data -@inject UserServices userService -@inject IDialogService DialogService -@inject NavigationManager navigationManager - - - - - - - Login - Welcome to Merchandise and SAC System - @if (error != null) - { - @error - } - - - - - @submitButtonText - Reset - - - - - - - - -@code { - - bool submitButtonDisabled = false; - string? pass, error, submitButtonText = "Login"; - UserInfoServices.userinfo userAttemtingToLogin = new(); - - //protected override async Task OnInitializedAsync() - //{ - - //} - - async Task SubmitLogin() - { - error = null; - - if (String.IsNullOrEmpty(userAttemtingToLogin.username) || String.IsNullOrEmpty(pass)) - { - error = "Username and Password is required"; - return; - } - - UserInfoServices.UserInfoServiceClient userInfoServiceClient = new(); - - waitingButton(true); - userAttemtingToLogin.pass = await userInfoServiceClient.md5EncodingAsync(pass); - userService.CurrentUser = await userInfoServiceClient.AuthenticateUserAsync(userAttemtingToLogin); - waitingButton(); - - if (userService.CurrentUser == null) - error = "Invalid Username or Password"; - else - navigationManager.NavigateTo("/"); - } - - void waitingButton(bool waiting = false) - { - if(waiting) - { - submitButtonDisabled = true; - submitButtonText = "Please Wait..."; - } - else - { - submitButtonDisabled = false; - submitButtonText = "Login"; - } - } -} diff --git a/Shared/MainLayout.razor b/Shared/MainLayout.razor index 47fa0f1..44b25c5 100644 --- a/Shared/MainLayout.razor +++ b/Shared/MainLayout.razor @@ -1,6 +1,10 @@ @inherits LayoutComponentBase + @using KWWebInvApp.Data -@inject UserServices userServices +@using KWWebInvApp.Services; + +@inject IAuthenticationService AuthenticationStateService +@inject AuthenticationStateProvider AuthenticationStateProvider @inject NavigationManager navigationManager @@ -8,52 +12,58 @@ - - @if (userServices.IsAuthenticated()) - { - - - - - - } - - - @if (userServices.IsAuthenticated()) - { - - - KW Web App - - - - - - @Body - - - } - else - { - - - - - - } + + + + + + + + + + + + + + + + + + + KW Web Inventory + + + + + + + + + @Body + + @code { bool _drawerOpen = true; + private UserInfoServices.userinfo? user; + + protected override async void OnAfterRender(bool firstRender) + { + base.OnAfterRender(firstRender); + + user = await AuthenticationStateService.GetAuthenticatedUserAsync(); + } void DrawerToggle() { _drawerOpen = !_drawerOpen; } - void Logout() + async void Logout() { - userServices.CurrentUser = null; + var webAuthenticationStateProvider = (WebAuthenticationStateProvider)AuthenticationStateProvider; + await webAuthenticationStateProvider.LogoutAsync(); navigationManager.NavigateTo("/"); } } \ No newline at end of file diff --git a/Shared/RedirectToLogin.razor b/Shared/RedirectToLogin.razor new file mode 100644 index 0000000..4362608 --- /dev/null +++ b/Shared/RedirectToLogin.razor @@ -0,0 +1,34 @@ +@using KWWebInvApp.Data +@using KWWebInvApp.Services; + +@inject NavigationManager navigationManager +@inject AuthenticationStateProvider AuthenticationStateProvider + +@code { + protected override async Task OnInitializedAsync() + { + //var returnUrl = Navigation.ToBaseRelativePath(Navigation.Uri); + + //if (string.IsNullOrWhiteSpace(returnUrl)) + // Navigation.NavigateTo("/userlogin", true); + //else + //{ + // if (returnUrl == "userlogin") + // Navigation.NavigateTo("/userlogin", true); + // else + // Navigation.NavigateTo($"/userlogin?returnUrl={returnUrl}", true); + //} + } + + protected override void OnAfterRender(bool firstRender) + { + base.OnAfterRender(firstRender); + + var returnUrl = navigationManager.ToBaseRelativePath(navigationManager.Uri); + + if (string.IsNullOrWhiteSpace(returnUrl)) + navigationManager.NavigateTo("/userlogin", true); + else + navigationManager.NavigateTo($"/userlogin?returnUrl={returnUrl}", true); + } +}