From 5dbb91de94703fa2a6f4ae8c713c225ea0f517a1 Mon Sep 17 00:00:00 2001 From: max Date: Sat, 2 Aug 2025 16:49:20 +0200 Subject: [PATCH 1/2] [CHANGE] Refactored monads & removed old auth projects --- DotBased.ASP.Auth/AuthDataCache.cs | 98 ------------- DotBased.ASP.Auth/AuthenticationService.cs | 13 -- DotBased.ASP.Auth/BasedAuthConfiguration.cs | 65 --------- DotBased.ASP.Auth/BasedAuthDefaults.cs | 13 -- .../BasedServerAuthenticationStateProvider.cs | 40 ----- .../Domains/Auth/AuthenticationStateModel.cs | 25 ---- .../Domains/Auth/PermissionModel.cs | 8 - DotBased.ASP.Auth/Domains/Auth/RoleModel.cs | 12 -- .../Domains/Identity/GroupItemModel.cs | 8 - .../Domains/Identity/GroupModel.cs | 13 -- .../Domains/Identity/UserItemModel.cs | 10 -- .../Domains/Identity/UserModel.cs | 31 ---- DotBased.ASP.Auth/Domains/LoginModel.cs | 8 - DotBased.ASP.Auth/Domains/RegisterModel.cs | 10 -- DotBased.ASP.Auth/DotBased.ASP.Auth.csproj | 21 --- .../DotBasedAuthDependencyInjection.cs | 55 ------- DotBased.ASP.Auth/IAuthDataRepository.cs | 22 --- DotBased.ASP.Auth/ISessionStateProvider.cs | 8 - DotBased.ASP.Auth/MemoryAuthDataRepository.cs | 107 -------------- .../Models/Configuration/AuthConfiguration.cs | 11 -- .../Configuration/CacheConfiguration.cs | 6 - .../Configuration/LockoutConfiguration.cs | 6 - .../Configuration/PasswordConfiguration.cs | 6 - .../Configuration/ProviderConfiguration.cs | 6 - .../Configuration/RepositoryConfiguration.cs | 6 - .../Models/Configuration/UserConfiguration.cs | 6 - DotBased.ASP.Auth/SecurityManager.cs | 9 -- DotBased.ASP.Auth/SecurityService.cs | 137 ------------------ DotBased.AspNet.Auth/BasedAuthExtensions.cs | 11 -- .../DotBased.AspNet.Auth.csproj | 31 ---- DotBased.sln | 17 --- DotBased/Monads/Result.cs | 93 ++++++++++++ DotBased/Result.cs | 77 ---------- DotBased/Utilities/Cryptography.cs | 25 ++-- 34 files changed, 106 insertions(+), 908 deletions(-) delete mode 100644 DotBased.ASP.Auth/AuthDataCache.cs delete mode 100644 DotBased.ASP.Auth/AuthenticationService.cs delete mode 100644 DotBased.ASP.Auth/BasedAuthConfiguration.cs delete mode 100644 DotBased.ASP.Auth/BasedAuthDefaults.cs delete mode 100644 DotBased.ASP.Auth/BasedServerAuthenticationStateProvider.cs delete mode 100644 DotBased.ASP.Auth/Domains/Auth/AuthenticationStateModel.cs delete mode 100644 DotBased.ASP.Auth/Domains/Auth/PermissionModel.cs delete mode 100644 DotBased.ASP.Auth/Domains/Auth/RoleModel.cs delete mode 100644 DotBased.ASP.Auth/Domains/Identity/GroupItemModel.cs delete mode 100644 DotBased.ASP.Auth/Domains/Identity/GroupModel.cs delete mode 100644 DotBased.ASP.Auth/Domains/Identity/UserItemModel.cs delete mode 100644 DotBased.ASP.Auth/Domains/Identity/UserModel.cs delete mode 100644 DotBased.ASP.Auth/Domains/LoginModel.cs delete mode 100644 DotBased.ASP.Auth/Domains/RegisterModel.cs delete mode 100644 DotBased.ASP.Auth/DotBased.ASP.Auth.csproj delete mode 100644 DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs delete mode 100644 DotBased.ASP.Auth/IAuthDataRepository.cs delete mode 100644 DotBased.ASP.Auth/ISessionStateProvider.cs delete mode 100644 DotBased.ASP.Auth/MemoryAuthDataRepository.cs delete mode 100644 DotBased.ASP.Auth/Models/Configuration/AuthConfiguration.cs delete mode 100644 DotBased.ASP.Auth/Models/Configuration/CacheConfiguration.cs delete mode 100644 DotBased.ASP.Auth/Models/Configuration/LockoutConfiguration.cs delete mode 100644 DotBased.ASP.Auth/Models/Configuration/PasswordConfiguration.cs delete mode 100644 DotBased.ASP.Auth/Models/Configuration/ProviderConfiguration.cs delete mode 100644 DotBased.ASP.Auth/Models/Configuration/RepositoryConfiguration.cs delete mode 100644 DotBased.ASP.Auth/Models/Configuration/UserConfiguration.cs delete mode 100644 DotBased.ASP.Auth/SecurityManager.cs delete mode 100644 DotBased.ASP.Auth/SecurityService.cs delete mode 100644 DotBased.AspNet.Auth/BasedAuthExtensions.cs delete mode 100644 DotBased.AspNet.Auth/DotBased.AspNet.Auth.csproj create mode 100644 DotBased/Monads/Result.cs delete mode 100755 DotBased/Result.cs diff --git a/DotBased.ASP.Auth/AuthDataCache.cs b/DotBased.ASP.Auth/AuthDataCache.cs deleted file mode 100644 index 76287e4..0000000 --- a/DotBased.ASP.Auth/AuthDataCache.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Collections.ObjectModel; -using DotBased.ASP.Auth.Domains.Auth; -using Microsoft.AspNetCore.Components.Authorization; - -namespace DotBased.ASP.Auth; - -public class AuthDataCache -{ - public AuthDataCache(BasedAuthConfiguration configuration) - { - _configuration = configuration; - } - - private readonly BasedAuthConfiguration _configuration; - - private readonly AuthStateCacheCollection _authenticationStateCollection = []; - - public Result PurgeSessionState(string id) => _authenticationStateCollection.Remove(id) ? Result.Ok() : Result.Failed("Failed to purge session state from cache! Or the session was not cached..."); - - public void CacheSessionState(AuthenticationStateModel stateModel, AuthenticationState? state = null) => _authenticationStateCollection[stateModel.Id] = - new AuthStateCacheNode(stateModel, state); - - public Result> RequestSessionState(string id) - { - if (!_authenticationStateCollection.TryGetValue(id, out var node)) - return Result>.Failed("No cached object found!"); - string failedMsg; - if (node.StateModel != null) - { - if (node.IsValidLifespan(_configuration.CachedAuthSessionLifespan)) - return Result>.Ok(new Tuple(node.StateModel, node.State)); - failedMsg = $"Session has invalid lifespan, removing entry: [{id}] from cache!"; - } - else - failedMsg = $"Returned object is null, removing entry: [{id}] from cache!"; - _authenticationStateCollection.Remove(id); - return Result>.Failed(failedMsg); - } -} - -public class AuthStateCacheNode where TStateModel : class where TState : class -{ - public AuthStateCacheNode(TStateModel stateModel, TState? state) - { - StateModel = stateModel; - State = state; - } - public TStateModel? StateModel { get; private set; } - public TState? State { get; private set; } - public DateTime DateCached { get; private set; } = DateTime.Now; - - public void UpdateObject(TStateModel obj) - { - StateModel = obj; - DateCached = DateTime.Now; - } - - /// - /// Checks if the cached object is within the given lifespan. - /// - /// The max. lifespan - public bool IsValidLifespan(TimeSpan lifespan) => DateCached.Add(lifespan) > DateTime.Now; - - public override bool Equals(object? obj) - { - if (obj is AuthStateCacheNode cacheObj) - return StateModel != null && StateModel.Equals(cacheObj.StateModel); - return false; - } - - public override int GetHashCode() => typeof(TStateModel).GetHashCode(); - public override string ToString() => typeof(TStateModel).ToString(); -} - -public class AuthStateCacheCollection : KeyedCollection> where TStateModel : class where TState : class -{ - protected override string GetKeyForItem(AuthStateCacheNode item) => item.StateModel?.ToString() ?? string.Empty; - - public new AuthStateCacheNode? this[string id] - { - get => TryGetValue(id, out AuthStateCacheNode? nodeValue) ? nodeValue : null; - set - { - if (value == null) - return; - if (TryGetValue(id, out AuthStateCacheNode? nodeValue)) - Remove(nodeValue); - Add(value); - } - } - - public void Insert(AuthStateCacheNode node) - { - if (Contains(node)) - Remove(node); - Add(node); - } -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/AuthenticationService.cs b/DotBased.ASP.Auth/AuthenticationService.cs deleted file mode 100644 index 9b4d3b2..0000000 --- a/DotBased.ASP.Auth/AuthenticationService.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace DotBased.ASP.Auth.Services; - -public class AuthenticationService -{ - public AuthenticationService() - { - /* - * - Login - * - Logout - * - Register - */ - } -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/BasedAuthConfiguration.cs b/DotBased.ASP.Auth/BasedAuthConfiguration.cs deleted file mode 100644 index a6871ba..0000000 --- a/DotBased.ASP.Auth/BasedAuthConfiguration.cs +++ /dev/null @@ -1,65 +0,0 @@ -namespace DotBased.ASP.Auth; - -public class BasedAuthConfiguration -{ - /// - /// Allow users to registrate. - /// - public bool AllowRegistration { get; set; } - //TODO: Callback when a user registers, so the application can handle sending emails or generate a code to complete the registration. - //TODO: Callback for validation email, phone number - /// - /// Allow no passwords on users, not recommended! - /// - public bool AllowEmptyPassword { get; set; } = false; - /// - /// This path is used for redirecting to the login page. - /// - public string LoginPath { get; set; } = string.Empty; - /// - /// The path that will be used if the logout is requested. - /// - public string LogoutPath { get; set; } = string.Empty; - /// - /// The page that the client will be redirected to after logging out. - /// - public string LoggedOutPath { get; set; } = string.Empty; - /// - /// The max age before a AuthenticationState will expire (default: 7 days). - /// - public TimeSpan AuthenticationStateMaxAgeBeforeExpire { get; set; } = TimeSpan.FromDays(7); - /// - /// How long a session state will be cached (default: 15 min) - /// - public TimeSpan CachedAuthSessionLifespan { get; set; } = TimeSpan.FromMinutes(15); - /// - /// Can be used to seed a default user and/or group for first time use. - /// - public Action? SeedData { get; set; } - - public Type? AuthDataRepositoryType { get; private set; } - - public void SetDataRepositoryType() where TDataProviderType : IAuthDataRepository => - AuthDataRepositoryType = typeof(TDataProviderType); - - public Type? SessionStateProviderType { get; private set; } - - public void SetSessionStateProviderType() - where TSessionStateProviderType : ISessionStateProvider => - SessionStateProviderType = typeof(TSessionStateProviderType); -} - -public class BasedPasswordOptions -{ - -} - -public class BasedUserOptions -{ - -} - -public class BasedLockoutOptions -{ - -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/BasedAuthDefaults.cs b/DotBased.ASP.Auth/BasedAuthDefaults.cs deleted file mode 100644 index 824740b..0000000 --- a/DotBased.ASP.Auth/BasedAuthDefaults.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Web; - -namespace DotBased.ASP.Auth; - -public static class BasedAuthDefaults -{ - public const string AuthenticationScheme = "DotBasedAuthentication"; - public const string StorageKey = "dotbased_session"; - - public static IComponentRenderMode InteractiveServerWithoutPrerender { get; } = - new InteractiveServerRenderMode(prerender: false); -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/BasedServerAuthenticationStateProvider.cs b/DotBased.ASP.Auth/BasedServerAuthenticationStateProvider.cs deleted file mode 100644 index 0863561..0000000 --- a/DotBased.ASP.Auth/BasedServerAuthenticationStateProvider.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Security.Claims; -using DotBased.Logging; -using Microsoft.AspNetCore.Components.Authorization; -using Microsoft.AspNetCore.Components.Server; -using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage; -using ILogger = DotBased.Logging.ILogger; - -namespace DotBased.ASP.Auth; - -// RevalidatingServerAuthenticationStateProvider -// AuthenticationStateProvider -// Handles roles -public class BasedServerAuthenticationStateProvider : ServerAuthenticationStateProvider -{ - public BasedServerAuthenticationStateProvider(BasedAuthConfiguration configuration, ProtectedLocalStorage localStorage, SecurityService securityService) - { - _config = configuration; - _localStorage = localStorage; - _securityService = securityService; - _logger = LogService.RegisterLogger(); - } - - private BasedAuthConfiguration _config; - private readonly ProtectedLocalStorage _localStorage; - private readonly SecurityService _securityService; - private readonly ILogger _logger; - private readonly AuthenticationState _anonState = new(new ClaimsPrincipal()); - - - public override async Task GetAuthenticationStateAsync() - { - _logger.Debug("Getting authentication state..."); - var sessionIdResult = await _localStorage.GetAsync(BasedAuthDefaults.StorageKey); - if (!sessionIdResult.Success || sessionIdResult.Value == null) - return _anonState; - _logger.Debug("Found state [{State}], getting session from {Service}", sessionIdResult.Value, nameof(SecurityService)); - var stateResult = await _securityService.GetAuthenticationStateFromSessionAsync(sessionIdResult.Value); - return stateResult is { Success: true, Value: not null } ? stateResult.Value : _anonState; - } -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/Domains/Auth/AuthenticationStateModel.cs b/DotBased.ASP.Auth/Domains/Auth/AuthenticationStateModel.cs deleted file mode 100644 index 78a5c79..0000000 --- a/DotBased.ASP.Auth/Domains/Auth/AuthenticationStateModel.cs +++ /dev/null @@ -1,25 +0,0 @@ -using DotBased.ASP.Auth.Domains.Identity; - -namespace DotBased.ASP.Auth.Domains.Auth; - -public class AuthenticationStateModel -{ - public AuthenticationStateModel(UserModel user) - { - UserId = user.Id; - } - - public string Id { get; set; } = Guid.NewGuid().ToString(); - public string UserId { get; set; } - public DateTime CreationDate { get; set; } = DateTime.Now; - - public override bool Equals(object? obj) - { - if (obj is AuthenticationStateModel authStateModel) - return authStateModel.Id == Id; - return false; - } - // ReSharper disable once NonReadonlyMemberInGetHashCode - public override int GetHashCode() => Id.GetHashCode(); - public override string ToString() => Id; -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/Domains/Auth/PermissionModel.cs b/DotBased.ASP.Auth/Domains/Auth/PermissionModel.cs deleted file mode 100644 index 93353fe..0000000 --- a/DotBased.ASP.Auth/Domains/Auth/PermissionModel.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace DotBased.ASP.Auth.Domains.Auth; - -public class PermissionModel -{ - public string Name { get; set; } = string.Empty; - public string Description { get; set; } = string.Empty; - public string Permission { get; set; } = string.Empty; -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/Domains/Auth/RoleModel.cs b/DotBased.ASP.Auth/Domains/Auth/RoleModel.cs deleted file mode 100644 index 92b15c7..0000000 --- a/DotBased.ASP.Auth/Domains/Auth/RoleModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using DotBased.Objects; - -namespace DotBased.ASP.Auth.Domains.Auth; - -public class RoleModel -{ - public string Id { get; set; } = Guid.NewGuid().ToString(); - public string Name { get; set; } = string.Empty; - public string Description { get; set; } = string.Empty; - public List Permissions { get; set; } = []; - public List> Attributes { get; set; } = []; -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/Domains/Identity/GroupItemModel.cs b/DotBased.ASP.Auth/Domains/Identity/GroupItemModel.cs deleted file mode 100644 index 30508d8..0000000 --- a/DotBased.ASP.Auth/Domains/Identity/GroupItemModel.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace DotBased.ASP.Auth.Domains.Identity; - -public class GroupItemModel -{ - public string Id { get; set; } = Guid.NewGuid().ToString(); - public string Name { get; set; } = string.Empty; - public string Description { get; set; } = string.Empty; -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/Domains/Identity/GroupModel.cs b/DotBased.ASP.Auth/Domains/Identity/GroupModel.cs deleted file mode 100644 index 73ff377..0000000 --- a/DotBased.ASP.Auth/Domains/Identity/GroupModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using DotBased.ASP.Auth.Domains.Auth; -using DotBased.Objects; - -namespace DotBased.ASP.Auth.Domains.Identity; - -public class GroupModel -{ - public string Id { get; set; } = Guid.NewGuid().ToString(); - public string Name { get; set; } = string.Empty; - public string Description { get; set; } = string.Empty; - public List Roles { get; set; } = []; - public List> Attributes { get; set; } = []; -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/Domains/Identity/UserItemModel.cs b/DotBased.ASP.Auth/Domains/Identity/UserItemModel.cs deleted file mode 100644 index 1d07566..0000000 --- a/DotBased.ASP.Auth/Domains/Identity/UserItemModel.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace DotBased.ASP.Auth.Domains.Identity; - -public class UserItemModel -{ - public string Id { get; set; } = string.Empty; - public string UserName { get; set; } = string.Empty; - public string Email { get; set; } = string.Empty; - public string Name { get; set; } = string.Empty; - public string FamilyName { get; set; } = string.Empty; -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/Domains/Identity/UserModel.cs b/DotBased.ASP.Auth/Domains/Identity/UserModel.cs deleted file mode 100644 index b9a9f51..0000000 --- a/DotBased.ASP.Auth/Domains/Identity/UserModel.cs +++ /dev/null @@ -1,31 +0,0 @@ -using DotBased.ASP.Auth.Domains.Auth; -using DotBased.Objects; - -namespace DotBased.ASP.Auth.Domains.Identity; - -public class UserModel -{ - public string UserName { get; set; } = string.Empty; - public string PasswordHash { get; set; } = string.Empty; - public string Email { get; set; } = string.Empty; - public string PhoneNumber { get; set; } = string.Empty; - public string Name { get; set; } = string.Empty; - public string FamilyName { get; set; } = string.Empty; - public DateTime Dob { get; set; } - - public string Id { get; set; } = Guid.NewGuid().ToString(); - public bool Enabled { get; set; } - public bool EmailValidated { get; set; } - public bool PhoneNumberConfirmed { get; set; } - public bool Lockout { get; set; } - public DateTime LockoutEnd { get; set; } - public DateTime CreationStamp { get; set; } - public DateTime SecurityStamp { get; set; } - public DateTime ConcurrencyStamp { get; set; } - public int AccessFailedCount { get; set; } - public bool ExternalAuthentication { get; set; } - - public List Groups { get; set; } = []; - public List Roles { get; set; } = []; - public List> Attributes { get; set; } = []; -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/Domains/LoginModel.cs b/DotBased.ASP.Auth/Domains/LoginModel.cs deleted file mode 100644 index a011eb6..0000000 --- a/DotBased.ASP.Auth/Domains/LoginModel.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace DotBased.ASP.Auth.Domains; - -public class LoginModel -{ - public string UserName { get; set; } = string.Empty; - public string Email { get; set; } = string.Empty; - public string Password { get; set; } = string.Empty; -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/Domains/RegisterModel.cs b/DotBased.ASP.Auth/Domains/RegisterModel.cs deleted file mode 100644 index 877476c..0000000 --- a/DotBased.ASP.Auth/Domains/RegisterModel.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace DotBased.ASP.Auth.Domains; - -public class RegisterModel -{ - public string UserName { get; set; } = string.Empty; - public string Email { get; set; } = string.Empty; - public string Password { get; set; } = string.Empty; - public string Name { get; set; } = string.Empty; - public string FamilyName { get; set; } = string.Empty; -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/DotBased.ASP.Auth.csproj b/DotBased.ASP.Auth/DotBased.ASP.Auth.csproj deleted file mode 100644 index e51c97a..0000000 --- a/DotBased.ASP.Auth/DotBased.ASP.Auth.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - net8.0 - enable - enable - - - - - - - - - - - - - - - diff --git a/DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs b/DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs deleted file mode 100644 index 59fa9e0..0000000 --- a/DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs +++ /dev/null @@ -1,55 +0,0 @@ -using DotBased.ASP.Auth.Services; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Components.Authorization; -using Microsoft.Extensions.DependencyInjection; - -namespace DotBased.ASP.Auth; - -public static class DotBasedAuthDependencyInjection -{ - /// - /// Use the DotBased authentication implementation - /// - /// Use UseBasedServerAuth()! - /// Service collection - /// DotBased auth configuration - public static IServiceCollection AddBasedServerAuth(this IServiceCollection services, Action? configurationAction = null) - { - var Configuration = new BasedAuthConfiguration(); - configurationAction?.Invoke(Configuration); - - services.AddSingleton(Configuration); - if (Configuration.AuthDataRepositoryType == null) - throw new ArgumentNullException(nameof(Configuration.AuthDataRepositoryType), $"No '{nameof(IAuthDataRepository)}' configured!"); - services.AddScoped(typeof(IAuthDataRepository), Configuration.AuthDataRepositoryType); - - services.AddSingleton(); - services.AddScoped(); - - services.AddScoped(); - services.AddAuthentication(options => - { - options.DefaultScheme = BasedAuthDefaults.AuthenticationScheme; - });/*.AddScheme(BasedAuthDefaults.AuthenticationScheme, null);*/ - services.AddAuthorization(); - services.AddCascadingAuthenticationState(); - return services; - } - - public static WebApplication UseBasedServerAuth(this WebApplication app) - { - app.UseAuthentication(); - app.UseAuthorization(); - - // Data - var authConfig = app.Services.GetService(); - if (authConfig == null) - throw new NullReferenceException($"{nameof(BasedAuthConfiguration)} is null!"); - if (authConfig.AuthDataRepositoryType == null) - throw new NullReferenceException($"{nameof(authConfig.AuthDataRepositoryType)} is null, cannot instantiate an instance of {nameof(IAuthDataRepository)}"); - var dataProvider = (IAuthDataRepository?)Activator.CreateInstance(authConfig.AuthDataRepositoryType); - if (dataProvider != null) authConfig.SeedData?.Invoke(dataProvider); - - return app; - } -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/IAuthDataRepository.cs b/DotBased.ASP.Auth/IAuthDataRepository.cs deleted file mode 100644 index f1ed79c..0000000 --- a/DotBased.ASP.Auth/IAuthDataRepository.cs +++ /dev/null @@ -1,22 +0,0 @@ -using DotBased.ASP.Auth.Domains.Auth; -using DotBased.ASP.Auth.Domains.Identity; - -namespace DotBased.ASP.Auth; - -public interface IAuthDataRepository -{ - public Task CreateUserAsync(UserModel user); - public Task UpdateUserAsync(UserModel user); - public Task DeleteUserAsync(UserModel user); - public Task> GetUserAsync(string id, string email, string username); - public Task> GetUsersAsync(int start = 0, int amount = 30, string search = ""); - public Task CreateGroupAsync(GroupModel group); - public Task UpdateGroupAsync(GroupModel group); - public Task DeleteGroupAsync(GroupModel group); - public Task> GetGroupAsync(string id); - public Task> GetGroupsAsync(int start = 0, int amount = 30, string search = ""); - public Task CreateAuthenticationStateAsync(AuthenticationStateModel authenticationState); - public Task UpdateAuthenticationStateAsync(AuthenticationStateModel authenticationState); - public Task DeleteAuthenticationStateAsync(AuthenticationStateModel authenticationState); - public Task> GetAuthenticationStateAsync(string id); -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/ISessionStateProvider.cs b/DotBased.ASP.Auth/ISessionStateProvider.cs deleted file mode 100644 index ee8850a..0000000 --- a/DotBased.ASP.Auth/ISessionStateProvider.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace DotBased.ASP.Auth; - -public interface ISessionStateProvider -{ - public const string SessionStateName = "BasedServerSession"; - public Task> GetSessionStateAsync(); - public Task SetSessionStateAsync(string state); -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/MemoryAuthDataRepository.cs b/DotBased.ASP.Auth/MemoryAuthDataRepository.cs deleted file mode 100644 index 708db7a..0000000 --- a/DotBased.ASP.Auth/MemoryAuthDataRepository.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using DotBased.ASP.Auth.Domains.Auth; -using DotBased.ASP.Auth.Domains.Identity; -using DotBased.Extensions; - -namespace DotBased.ASP.Auth; -/// -/// In memory data provider, for testing only! -/// -[SuppressMessage("ReSharper", "CollectionNeverUpdated.Local")] -public class MemoryAuthDataRepository : IAuthDataRepository -{ - public async Task CreateUserAsync(UserModel user) - { - if (MemoryData.users.Any(x => x.Id == user.Id || x.Email == user.Email)) - return Result.Failed("User already exists."); - MemoryData.users.Add(user); - return Result.Ok(); - } - - public async Task UpdateUserAsync(UserModel user) - { - if (MemoryData.users.All(x => x.Id != user.Id)) - return Result.Failed("User does not exist!"); - - return Result.Ok(); - } - - public Task DeleteUserAsync(UserModel user) - { - throw new NotImplementedException(); - } - - public async Task> GetUserAsync(string id, string email, string username) - { - UserModel? userModel = null; - if (!id.IsNullOrEmpty()) - userModel = MemoryData.users.FirstOrDefault(u => u.Id.Equals(id, StringComparison.OrdinalIgnoreCase)); - if (!email.IsNullOrEmpty()) - userModel = MemoryData.users.FirstOrDefault(u => u.Email.Equals(email, StringComparison.OrdinalIgnoreCase)); - if (!username.IsNullOrEmpty()) - userModel = MemoryData.users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.OrdinalIgnoreCase)); - return userModel != null ? Result.Ok(userModel) : Result.Failed("No user found!"); - } - - public Task> GetUsersAsync(int start = 0, int amount = 30, string search = "") - { - throw new NotImplementedException(); - } - - public Task CreateGroupAsync(GroupModel group) - { - throw new NotImplementedException(); - } - - public Task UpdateGroupAsync(GroupModel group) - { - throw new NotImplementedException(); - } - - public Task DeleteGroupAsync(GroupModel group) - { - throw new NotImplementedException(); - } - - public Task> GetGroupAsync(string id) - { - throw new NotImplementedException(); - } - - public Task> GetGroupsAsync(int start = 0, int amount = 30, string search = "") - { - throw new NotImplementedException(); - } - - public async Task CreateAuthenticationStateAsync(AuthenticationStateModel authenticationState) - { - if (MemoryData.AuthenticationStates.Contains(authenticationState)) return Result.Failed("Item already exists!"); - MemoryData.AuthenticationStates.Add(authenticationState); - return Result.Ok(); - } - - public Task UpdateAuthenticationStateAsync(AuthenticationStateModel authenticationState) - { - throw new NotImplementedException(); - } - - public async Task DeleteAuthenticationStateAsync(AuthenticationStateModel authenticationState) - { - MemoryData.AuthenticationStates.Remove(authenticationState); - return Result.Ok(); - } - - public async Task> GetAuthenticationStateAsync(string id) - { - var item = MemoryData.AuthenticationStates.FirstOrDefault(x => x.Id == id); - if (item == null) return Result.Failed("Could not get the session state!"); - return Result.Ok(item); - } -} - -internal static class MemoryData -{ - public static readonly List users = []; - public static readonly List Groups = []; - public static readonly List AuthenticationStates = []; -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/Models/Configuration/AuthConfiguration.cs b/DotBased.ASP.Auth/Models/Configuration/AuthConfiguration.cs deleted file mode 100644 index 2bbf690..0000000 --- a/DotBased.ASP.Auth/Models/Configuration/AuthConfiguration.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace DotBased.ASP.Auth.Models.Configuration; - -public class AuthConfiguration -{ - public CacheConfiguration Cache { get; set; } = new(); - public LockoutConfiguration Lockout { get; set; } = new(); - public PasswordConfiguration Password { get; set; } = new(); - public ProviderConfiguration Provider { get; set; } = new(); - public RepositoryConfiguration Repository { get; set; } = new(); - public UserConfiguration User { get; set; } = new(); -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/Models/Configuration/CacheConfiguration.cs b/DotBased.ASP.Auth/Models/Configuration/CacheConfiguration.cs deleted file mode 100644 index 8647941..0000000 --- a/DotBased.ASP.Auth/Models/Configuration/CacheConfiguration.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace DotBased.ASP.Auth.Models.Configuration; - -public class CacheConfiguration -{ - -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/Models/Configuration/LockoutConfiguration.cs b/DotBased.ASP.Auth/Models/Configuration/LockoutConfiguration.cs deleted file mode 100644 index b59ae65..0000000 --- a/DotBased.ASP.Auth/Models/Configuration/LockoutConfiguration.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace DotBased.ASP.Auth.Models.Configuration; - -public class LockoutConfiguration -{ - -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/Models/Configuration/PasswordConfiguration.cs b/DotBased.ASP.Auth/Models/Configuration/PasswordConfiguration.cs deleted file mode 100644 index c590cdd..0000000 --- a/DotBased.ASP.Auth/Models/Configuration/PasswordConfiguration.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace DotBased.ASP.Auth.Models.Configuration; - -public class PasswordConfiguration -{ - -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/Models/Configuration/ProviderConfiguration.cs b/DotBased.ASP.Auth/Models/Configuration/ProviderConfiguration.cs deleted file mode 100644 index cf3f702..0000000 --- a/DotBased.ASP.Auth/Models/Configuration/ProviderConfiguration.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace DotBased.ASP.Auth.Models.Configuration; - -public class ProviderConfiguration -{ - -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/Models/Configuration/RepositoryConfiguration.cs b/DotBased.ASP.Auth/Models/Configuration/RepositoryConfiguration.cs deleted file mode 100644 index cb55903..0000000 --- a/DotBased.ASP.Auth/Models/Configuration/RepositoryConfiguration.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace DotBased.ASP.Auth.Models.Configuration; - -public class RepositoryConfiguration -{ - -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/Models/Configuration/UserConfiguration.cs b/DotBased.ASP.Auth/Models/Configuration/UserConfiguration.cs deleted file mode 100644 index a4dd082..0000000 --- a/DotBased.ASP.Auth/Models/Configuration/UserConfiguration.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace DotBased.ASP.Auth.Models.Configuration; - -public class UserConfiguration -{ - -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/SecurityManager.cs b/DotBased.ASP.Auth/SecurityManager.cs deleted file mode 100644 index 0067eae..0000000 --- a/DotBased.ASP.Auth/SecurityManager.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace DotBased.ASP.Auth.Managers; - -public class SecurityManager -{ - public SecurityManager() - { - - } -} \ No newline at end of file diff --git a/DotBased.ASP.Auth/SecurityService.cs b/DotBased.ASP.Auth/SecurityService.cs deleted file mode 100644 index 748e0a7..0000000 --- a/DotBased.ASP.Auth/SecurityService.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System.Security.Claims; -using DotBased.ASP.Auth.Domains; -using DotBased.ASP.Auth.Domains.Auth; -using DotBased.ASP.Auth.Domains.Identity; -using DotBased.Extensions; -using DotBased.Logging; -using Microsoft.AspNetCore.Components.Authorization; -using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage; - -namespace DotBased.ASP.Auth; - -public class SecurityService -{ - public SecurityService(IAuthDataRepository authDataRepository, AuthDataCache dataCache, ProtectedLocalStorage localStorage) - { - _authDataRepository = authDataRepository; - _dataCache = dataCache; - _localStorage = localStorage; - _logger = LogService.RegisterLogger(); - } - - private readonly IAuthDataRepository _authDataRepository; - private readonly AuthDataCache _dataCache; - private readonly ProtectedLocalStorage _localStorage; - private readonly ILogger _logger; - - public async Task> GetAuthenticationStateFromSessionAsync(string id) - { - if (id.IsNullOrEmpty()) - return Result.Failed("No valid id!"); - AuthenticationStateModel? authStateModel = null; - var stateCache = _dataCache.RequestSessionState(id); - if (!stateCache.Success || stateCache.Value == null) - { - var stateResult = await _authDataRepository.GetAuthenticationStateAsync(id); - if (stateResult is { Success: true, Value: not null }) - { - authStateModel = stateResult.Value; - _dataCache.CacheSessionState(authStateModel); - } - } - else - { - if (stateCache.Value.Item2 != null) - return Result.Ok(stateCache.Value.Item2); - authStateModel = stateCache.Value.Item1; - } - - if (authStateModel == null) - return Result.Failed("Failed to get auth state!"); - - var userResult = await _authDataRepository.GetUserAsync(authStateModel.UserId, string.Empty, string.Empty); - if (userResult is not { Success: true, Value: not null }) - return Result.Failed("Failed to get user from state!"); - var claims = new List() - { - new(ClaimTypes.Sid, userResult.Value.Id), - new(ClaimTypes.Name, userResult.Value.Name), - new(ClaimTypes.NameIdentifier, userResult.Value.UserName), - new(ClaimTypes.Surname, userResult.Value.FamilyName), - new(ClaimTypes.Email, userResult.Value.Email) - }; - claims.AddRange(userResult.Value.Groups.Select(group => new Claim(ClaimTypes.GroupSid, group.Id))); - claims.AddRange(userResult.Value.Roles.Select(role => new Claim(ClaimTypes.Role, role.Name))); - claims.AddRange(userResult.Value.Groups.Select(g => g.Roles).SelectMany(gRolesList => gRolesList, (_, role) => new Claim(ClaimTypes.Role, role.Name))); - var claimsIdentity = new ClaimsIdentity(claims, BasedAuthDefaults.AuthenticationScheme); - var authState = new AuthenticationState(new ClaimsPrincipal(claimsIdentity)); - _dataCache.CacheSessionState(authStateModel, authState); - return Result.Ok(authState); - } - - public async Task> LoginAsync(LoginModel login) - { - try - { - UserModel? user = null; - Result usrResult; - if (!login.UserName.IsNullOrEmpty()) - { - usrResult = await _authDataRepository.GetUserAsync(string.Empty, string.Empty, login.UserName); - if (usrResult is { Success: true, Value: not null }) - user = usrResult.Value; - } - else if (!login.Email.IsNullOrEmpty()) - { - usrResult = await _authDataRepository.GetUserAsync(string.Empty, login.Email, string.Empty); - if (usrResult is { Success: true, Value: not null }) - user = usrResult.Value; - } - else - return Result.Failed("Username & Email is empty, cannot login!"); - - if (user == null || !usrResult.Success) - return Result.Failed("No user found!"); - - if (user.PasswordHash != login.Password) //TODO: Hash password and compare - return Result.Failed("Login failed, invalid password."); - var state = new AuthenticationStateModel(user); - var authResult = await _authDataRepository.CreateAuthenticationStateAsync(state); - if (!authResult.Success) - return Result.Failed("Failed to store session to database!"); - _dataCache.CacheSessionState(state); - await _localStorage.SetAsync(BasedAuthDefaults.StorageKey, state.Id); - return Result.Ok(state); - } - catch (Exception e) - { - _logger.Error(e, "Failed to login!"); - return Result.Failed("Login failed, exception thrown!"); - } - } - - public async Task LogoutAsync(string state) - { - try - { - if (state.IsNullOrEmpty()) - return Result.Failed($"Argument {nameof(state)} is empty!"); - - var stateResult = await _authDataRepository.GetAuthenticationStateAsync(state); - if (!stateResult.Success || stateResult.Value == null) - return stateResult; - var authState = stateResult.Value; - - _dataCache.PurgeSessionState(state); - var updatedStateResult = await _authDataRepository.DeleteAuthenticationStateAsync(authState); - if (updatedStateResult.Success) return updatedStateResult; - _logger.Warning(updatedStateResult.Message); - return updatedStateResult; - } - catch (Exception e) - { - _logger.Error(e, "Failed to logout!"); - return Result.Failed("Failed to logout, exception thrown!"); - } - } -} \ No newline at end of file diff --git a/DotBased.AspNet.Auth/BasedAuthExtensions.cs b/DotBased.AspNet.Auth/BasedAuthExtensions.cs deleted file mode 100644 index 023a35f..0000000 --- a/DotBased.AspNet.Auth/BasedAuthExtensions.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace DotBased.AspNet.Auth; - -public static class BasedAuthExtensions -{ - public static IServiceCollection AddBasedAuthentication(this IServiceCollection services) - { - return services; - } -} \ No newline at end of file diff --git a/DotBased.AspNet.Auth/DotBased.AspNet.Auth.csproj b/DotBased.AspNet.Auth/DotBased.AspNet.Auth.csproj deleted file mode 100644 index 3af943e..0000000 --- a/DotBased.AspNet.Auth/DotBased.AspNet.Auth.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - net8.0 - enable - enable - - - - - - - - - - - - - - ..\..\..\..\..\usr\lib64\dotnet\shared\Microsoft.AspNetCore.App\8.0.11\Microsoft.AspNetCore.Authentication.dll - - - ..\..\..\.nuget\packages\microsoft.extensions.dependencyinjection.abstractions\8.0.2\lib\net8.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll - - - - - - - - diff --git a/DotBased.sln b/DotBased.sln index 4221777..00949e9 100755 --- a/DotBased.sln +++ b/DotBased.sln @@ -8,8 +8,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotBased.Logging.Serilog", EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules", "Modules", "{2156FB93-C252-4B33-8A0C-73C82FABB163}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotBased.ASP.Auth", "DotBased.ASP.Auth\DotBased.ASP.Auth.csproj", "{CBD4111D-F1CA-466A-AC73-9EAB7F235B3D}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotBased.Logging.MEL", "DotBased.Logging.MEL\DotBased.Logging.MEL.csproj", "{D4D9B584-A524-4CBB-9B61-9CD65ED4AF0D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{DBDB4538-85D4-45AC-9E0A-A684467AEABA}" @@ -18,10 +16,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestWebApi", "TestWebApi\Te EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazor.Wasm", "Blazor.Wasm\Blazor.Wasm.csproj", "{AC8343A5-7953-4E1D-A926-406BE4D7E819}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AspNet", "AspNet", "{624E7B11-8A18-46E5-AB1F-6AF6097F9D4D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotBased.AspNet.Auth", "DotBased.AspNet.Auth\DotBased.AspNet.Auth.csproj", "{6F407D81-DFAC-4936-ACDD-D75E9FDE2E7B}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -40,10 +34,6 @@ Global {EBBDAF9A-BFC7-4BDC-8C51-0501B59A1DDC}.Debug|Any CPU.Build.0 = Debug|Any CPU {EBBDAF9A-BFC7-4BDC-8C51-0501B59A1DDC}.Release|Any CPU.ActiveCfg = Release|Any CPU {EBBDAF9A-BFC7-4BDC-8C51-0501B59A1DDC}.Release|Any CPU.Build.0 = Release|Any CPU - {CBD4111D-F1CA-466A-AC73-9EAB7F235B3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CBD4111D-F1CA-466A-AC73-9EAB7F235B3D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CBD4111D-F1CA-466A-AC73-9EAB7F235B3D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CBD4111D-F1CA-466A-AC73-9EAB7F235B3D}.Release|Any CPU.Build.0 = Release|Any CPU {D4D9B584-A524-4CBB-9B61-9CD65ED4AF0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D4D9B584-A524-4CBB-9B61-9CD65ED4AF0D}.Debug|Any CPU.Build.0 = Debug|Any CPU {D4D9B584-A524-4CBB-9B61-9CD65ED4AF0D}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -56,18 +46,11 @@ Global {AC8343A5-7953-4E1D-A926-406BE4D7E819}.Debug|Any CPU.Build.0 = Debug|Any CPU {AC8343A5-7953-4E1D-A926-406BE4D7E819}.Release|Any CPU.ActiveCfg = Release|Any CPU {AC8343A5-7953-4E1D-A926-406BE4D7E819}.Release|Any CPU.Build.0 = Release|Any CPU - {6F407D81-DFAC-4936-ACDD-D75E9FDE2E7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6F407D81-DFAC-4936-ACDD-D75E9FDE2E7B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6F407D81-DFAC-4936-ACDD-D75E9FDE2E7B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6F407D81-DFAC-4936-ACDD-D75E9FDE2E7B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {EBBDAF9A-BFC7-4BDC-8C51-0501B59A1DDC} = {2156FB93-C252-4B33-8A0C-73C82FABB163} - {CBD4111D-F1CA-466A-AC73-9EAB7F235B3D} = {2156FB93-C252-4B33-8A0C-73C82FABB163} {D4D9B584-A524-4CBB-9B61-9CD65ED4AF0D} = {2156FB93-C252-4B33-8A0C-73C82FABB163} {BADA4BAF-142B-47A8-95FC-B25E1D3D0020} = {DBDB4538-85D4-45AC-9E0A-A684467AEABA} {AC8343A5-7953-4E1D-A926-406BE4D7E819} = {DBDB4538-85D4-45AC-9E0A-A684467AEABA} - {624E7B11-8A18-46E5-AB1F-6AF6097F9D4D} = {2156FB93-C252-4B33-8A0C-73C82FABB163} - {6F407D81-DFAC-4936-ACDD-D75E9FDE2E7B} = {624E7B11-8A18-46E5-AB1F-6AF6097F9D4D} EndGlobalSection EndGlobal diff --git a/DotBased/Monads/Result.cs b/DotBased/Monads/Result.cs new file mode 100644 index 0000000..c36423d --- /dev/null +++ b/DotBased/Monads/Result.cs @@ -0,0 +1,93 @@ +namespace DotBased.Monads; + +public class Result +{ + protected Result() + { + IsSuccess = true; + } + + protected Result(Exception exception) + { + IsSuccess = false; + Error = ResultError.Error(exception); + } + + protected Result(ResultError error) + { + IsSuccess = false; + Error = error; + } + + public bool IsSuccess { get; } + public ResultError? Error { get; set; } + + public static implicit operator Result(Exception exception) => new(exception); + public static implicit operator Result(ResultError error) => new(error); + + public static Result Success() => new(); + public static Result Fail(ResultError error) => new(error); + public static Result Exception(Exception exception) => new(exception); + + + public TMatch Match(Func success, Func failure) => IsSuccess ? success() : failure(Error!); + + public void Match(Action success, Action failure) + { + if (IsSuccess) + { + success(); + } + else + { + failure(Error!); + } + } +} + +public class Result : Result +{ + protected Result(TResult result) + { + _result = result; + } + + protected Result(Exception exception) : base(exception) + { + _result = default; + } + + protected Result(ResultError error) : base(error) + { + _result = default; + } + + private readonly TResult? _result; + public TResult Value => IsSuccess ? _result! : throw new InvalidOperationException("Result is invalid"); + + public static implicit operator Result(TResult result) => new(result); + public static implicit operator Result(Exception exception) => new(exception); + public static implicit operator Result(ResultError error) => new(error); + + public static Result Success(TResult result) => new(result); + public new static Result Fail(ResultError error) => new(error); + public new static Result Exception(Exception exception) => new(exception); + + public TMatch Match(Func success, Func failure) => + IsSuccess && Value != null ? success(Value) : failure(Error ?? ResultError.Fail("No error and value is null!")); +} + +public class ResultError +{ + private ResultError(string description, Exception? exception) + { + Description = description; + Exception = exception; + } + + public string Description { get; } + public Exception? Exception { get; } + + public static ResultError Fail(string description) => new(description, null); + public static ResultError Error(Exception exception, string description = "") => new(description, exception); +} \ No newline at end of file diff --git a/DotBased/Result.cs b/DotBased/Result.cs deleted file mode 100755 index 49e8ba6..0000000 --- a/DotBased/Result.cs +++ /dev/null @@ -1,77 +0,0 @@ -namespace DotBased; - -/// -/// Simple result class for returning a result state or a message and an exception. -/// -public class Result -{ - public Result(bool success, string message, Exception? exception) - { - Success = success; - Message = message; - Exception = exception; - } - - public Result(Result bObj) - { - Success = bObj.Success; - Message = bObj.Message; - Exception = bObj.Exception; - } - - public bool Success { get; set; } - public string Message { get; set; } - public Exception? Exception { get; set; } - - public static Result Ok() => new(true, string.Empty, null); - public static Result Failed(string message, Exception? exception = null) => new(false, message, exception); -} - -public class Result : Result -{ - public Result(bool success, string message, TValue? value, Exception? exception) : base(success, message, exception) - { - Value = value; - } - public Result(Result bObj) : base(bObj) - { - - } - public TValue? Value { get; set; } - - public static Result Ok(TValue value) => new(true, string.Empty, value, null); - - public new static Result Failed(string message, Exception? exception = null) => - new(false, message, default, exception); -} - -public class ListResult : Result -{ - public ListResult(bool success, string message, int totalCount, IEnumerable? items, Exception? exception) : base(success, message, exception) - { - Items = items != null ? new List(items) : new List(); - TotalCount = totalCount; - } - - public ListResult(Result bObj) : base(bObj) - { - Items = new List(); - } - - public readonly IReadOnlyList Items; - /// - /// The amount of items that this result contains. - /// - public int Count => Items.Count; - - /// - /// The total amount of item that is available. - /// - public int TotalCount { get; } - - public static ListResult Ok(IEnumerable items, int totalCount = -1) => - new(true, string.Empty, totalCount, items, null); - - public new static ListResult Failed(string message, Exception? exception = null) => - new(false, message, -1, null, exception); -} \ No newline at end of file diff --git a/DotBased/Utilities/Cryptography.cs b/DotBased/Utilities/Cryptography.cs index f0e6192..31cc651 100755 --- a/DotBased/Utilities/Cryptography.cs +++ b/DotBased/Utilities/Cryptography.cs @@ -1,4 +1,5 @@ using System.Security.Cryptography; +using DotBased.Monads; namespace DotBased.Utilities; @@ -12,7 +13,7 @@ public static class Cryptography var outputStream = new StringWriter(); var parameters = csp.ExportParameters(false); if (parameters.Exponent == null || parameters.Modulus == null) - return Result.Failed("RSAParameters are empty!"); + return ResultError.Fail("RSAParameters are empty!"); using (var stream = new MemoryStream()) { var writer = new BinaryWriter(stream); @@ -23,7 +24,7 @@ public static class Cryptography innerWriter.Write((byte)0x30); // SEQUENCE EncodeLength(innerWriter, 13); innerWriter.Write((byte)0x06); // OBJECT IDENTIFIER - byte[] rsaEncryptionOid = new byte[] { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 }; + var rsaEncryptionOid = new byte[] { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 }; EncodeLength(innerWriter, rsaEncryptionOid.Length); innerWriter.Write(rsaEncryptionOid); innerWriter.Write((byte)0x05); // NULL @@ -44,20 +45,20 @@ public static class Cryptography bitStringWriter.Write(paramsStream.GetBuffer(), 0, paramsLength); } - int bitStringLength = (int)bitStringStream.Length; + var bitStringLength = (int)bitStringStream.Length; EncodeLength(innerWriter, bitStringLength); innerWriter.Write(bitStringStream.GetBuffer(), 0, bitStringLength); } - int length = (int)innerStream.Length; + var length = (int)innerStream.Length; EncodeLength(writer, length); writer.Write(innerStream.GetBuffer(), 0, length); } - char[] base64 = Convert.ToBase64String(stream.GetBuffer(), 0, (int)stream.Length).ToCharArray(); + var base64 = Convert.ToBase64String(stream.GetBuffer(), 0, (int)stream.Length).ToCharArray(); // WriteLine terminates with \r\n, we want only \n outputStream.Write("-----BEGIN PUBLIC KEY-----\n"); - for (int i = 0; i < base64.Length; i += 64) + for (var i = 0; i < base64.Length; i += 64) { outputStream.Write(base64, i, Math.Min(64, base64.Length - i)); outputStream.Write("\n"); @@ -66,7 +67,7 @@ public static class Cryptography outputStream.Write("-----END PUBLIC KEY-----"); } - return Result.Ok(outputStream.ToString()); + return outputStream.ToString(); } private static void EncodeLength(BinaryWriter stream, int length) @@ -82,15 +83,15 @@ public static class Cryptography default: { // Long form - int temp = length; - int bytesRequired = 0; + var temp = length; + var bytesRequired = 0; while (temp > 0) { temp >>= 8; bytesRequired++; } stream.Write((byte)(bytesRequired | 0x80)); - for (int i = bytesRequired - 1; i >= 0; i--) + for (var i = bytesRequired - 1; i >= 0; i--) { stream.Write((byte)(length >> (8 * i) & 0xff)); } @@ -102,7 +103,7 @@ public static class Cryptography private static void EncodeIntegerBigEndian(BinaryWriter stream, byte[] value, bool forceUnsigned = true) { stream.Write((byte)0x02); // INTEGER - int prefixZeros = value.TakeWhile(t => t == 0).Count(); + var prefixZeros = value.TakeWhile(t => t == 0).Count(); if (value.Length - prefixZeros == 0) { EncodeLength(stream, 1); @@ -120,7 +121,7 @@ public static class Cryptography { EncodeLength(stream, value.Length - prefixZeros); } - for (int i = prefixZeros; i < value.Length; i++) + for (var i = prefixZeros; i < value.Length; i++) { stream.Write(value[i]); } From 38832fd597d121e7a14282ea3b516abcaf607004 Mon Sep 17 00:00:00 2001 From: max Date: Sat, 2 Aug 2025 17:05:50 +0200 Subject: [PATCH 2/2] [CHANGE] Refactored DotBased utilities --- DotBased/Utilities/Culture.cs | 16 +++++++-------- DotBased/Utilities/Suffix.cs | 37 ++++++++++++++++++----------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/DotBased/Utilities/Culture.cs b/DotBased/Utilities/Culture.cs index 7411011..643fa6e 100755 --- a/DotBased/Utilities/Culture.cs +++ b/DotBased/Utilities/Culture.cs @@ -5,9 +5,9 @@ namespace DotBased.Utilities; public static class Culture { - private static List _sysCultures = new List(); - private static Dictionary _regions = new Dictionary(); - private static readonly ILogger _logger = LogService.RegisterLogger(typeof(Culture)); + private static List _sysCultures = []; + private static readonly Dictionary Regions = new(); + private static readonly ILogger Logger = LogService.RegisterLogger(typeof(Culture)); /// /// Get all system known cultures. @@ -16,7 +16,7 @@ public static class Culture /// The list with 's the system knows public static IEnumerable GetSystemCultures() { - _logger.Debug("Getting system cultures..."); + Logger.Debug("Getting system cultures..."); if (_sysCultures.Count == 0) _sysCultures = CultureInfo.GetCultures(CultureTypes.AllCultures).ToList(); return _sysCultures; @@ -29,16 +29,16 @@ public static class Culture /// A list with regions from the system public static Dictionary GetRegions() { - if (_regions.Count == 0) + if (Regions.Count == 0) { var cultureInfos = GetSystemCultures().Where(cul => !cul.IsNeutralCulture).Where(cul => cul.LCID != 0x7F); foreach (var culture in cultureInfos) { var region = new RegionInfo(culture.Name); - _regions.Add(culture.Name, region); + Regions.Add(culture.Name, region); } } - return _regions; + return Regions; } /// @@ -52,7 +52,7 @@ public static class Culture _sysCultures.Clear(); break; case CacheType.Region: - _regions.Clear(); + Regions.Clear(); break; default: throw new ArgumentOutOfRangeException(nameof(type), type, null); diff --git a/DotBased/Utilities/Suffix.cs b/DotBased/Utilities/Suffix.cs index 60ed314..890f93b 100755 --- a/DotBased/Utilities/Suffix.cs +++ b/DotBased/Utilities/Suffix.cs @@ -12,29 +12,30 @@ public static class Suffix /// Converts the bytes to the memory suffix. /// /// The bytes to convert - /// How manay decimal places will be placed + /// How manny decimal places will be placed /// The suffixed bytes in the correct format public static string BytesToSizeSuffix(long bytes, int decimalPlaces = 1) { - if (decimalPlaces < 0) - decimalPlaces = 1; - switch (bytes) + if (decimalPlaces < 0) decimalPlaces = 1; + + if (bytes == 0) + return $"{0.ToString($"N{decimalPlaces}")} bytes"; + + var negative = bytes < 0; + var absBytes = (ulong)(negative ? -bytes : bytes); + + var mag = (int)Math.Log(absBytes, 1024); + var adjustedSize = absBytes / Math.Pow(1024, mag); + + if (Math.Round(adjustedSize, decimalPlaces) >= 1000 && mag < SizeSuffixes.Length - 1) { - case < 0: - return "-" + BytesToSizeSuffix(-bytes, decimalPlaces); - case 0: - return string.Format("{0:n" + decimalPlaces + "} bytes", 0); - } - - int mag = (int)Math.Log(bytes, 1024); - - decimal adjustedSize = (decimal)bytes / (1L << (mag * 10)); - - if (Math.Round(adjustedSize, decimalPlaces) >= 1000) - { - mag += 1; + mag++; adjustedSize /= 1024; } - return string.Format("{0:n" + decimalPlaces + "} {1}", adjustedSize, SizeSuffixes[mag]); + + var format = $"N{decimalPlaces}"; + var result = $"{adjustedSize.ToString(format)} {SizeSuffixes[mag]}"; + + return negative ? "-" + result : result; } } \ No newline at end of file