From 2361e12847bf682ada1fc42c5e384527d4ced0fb Mon Sep 17 00:00:00 2001 From: max Date: Sat, 21 Dec 2024 15:30:17 +0100 Subject: [PATCH 01/24] [ADD] Base Authority initial commit --- .../DotBasedAuthDependencyInjection.cs | 2 +- DotBased.AspNet.Auth/BasedAuthExtensions.cs | 11 ------- .../Attributes/ProtectAttribute.cs | 10 ++++++ DotBased.AspNet.Authority/AuthorityBuilder.cs | 13 ++++++++ .../AuthorityDefaults.cs | 11 +++++++ .../AuthorityProviderExtensions.cs | 17 ++++++++++ .../DotBased.AspNet.Authority.csproj | 18 ++++------- .../Interfaces/IAttributeRepository.cs | 6 ++++ .../Interfaces/IAuthorityRepository.cs | 6 ++++ .../Interfaces/IRoleRepository.cs | 6 ++++ .../Interfaces/IUserRepository.cs | 6 ++++ .../Models/Authority/AuthorityUser.cs | 10 ++++++ .../Models/Authority/AuthorityUserBase.cs | 32 +++++++++++++++++++ .../Services/AuthorityService.cs | 6 ++++ DotBased.sln | 12 +++---- TestWebApi/Program.cs | 9 ++++++ TestWebApi/SeedAuthorityData.cs | 9 ++++++ TestWebApi/TestWebApi.csproj | 1 + 18 files changed, 155 insertions(+), 30 deletions(-) delete mode 100644 DotBased.AspNet.Auth/BasedAuthExtensions.cs create mode 100644 DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs create mode 100644 DotBased.AspNet.Authority/AuthorityBuilder.cs create mode 100644 DotBased.AspNet.Authority/AuthorityDefaults.cs create mode 100644 DotBased.AspNet.Authority/AuthorityProviderExtensions.cs rename DotBased.AspNet.Auth/DotBased.AspNet.Auth.csproj => DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj (64%) create mode 100644 DotBased.AspNet.Authority/Interfaces/IAttributeRepository.cs create mode 100644 DotBased.AspNet.Authority/Interfaces/IAuthorityRepository.cs create mode 100644 DotBased.AspNet.Authority/Interfaces/IRoleRepository.cs create mode 100644 DotBased.AspNet.Authority/Interfaces/IUserRepository.cs create mode 100644 DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs create mode 100644 DotBased.AspNet.Authority/Models/Authority/AuthorityUserBase.cs create mode 100644 DotBased.AspNet.Authority/Services/AuthorityService.cs create mode 100644 TestWebApi/SeedAuthorityData.cs diff --git a/DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs b/DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs index 59fa9e0..b060926 100644 --- a/DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs +++ b/DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs @@ -30,7 +30,7 @@ public static class DotBasedAuthDependencyInjection services.AddAuthentication(options => { options.DefaultScheme = BasedAuthDefaults.AuthenticationScheme; - });/*.AddScheme(BasedAuthDefaults.AuthenticationScheme, null);*/ + }); services.AddAuthorization(); services.AddCascadingAuthenticationState(); return services; 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.Authority/Attributes/ProtectAttribute.cs b/DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs new file mode 100644 index 0000000..13394b8 --- /dev/null +++ b/DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs @@ -0,0 +1,10 @@ +namespace DotBased.AspNet.Authority.Attributes; + +/// +/// Indicates that the property should be protected. +/// +[AttributeUsage(AttributeTargets.Property)] +public class ProtectAttribute : Attribute +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/AuthorityBuilder.cs b/DotBased.AspNet.Authority/AuthorityBuilder.cs new file mode 100644 index 0000000..e778248 --- /dev/null +++ b/DotBased.AspNet.Authority/AuthorityBuilder.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace DotBased.AspNet.Authority; + +public class AuthorityBuilder +{ + public AuthorityBuilder(IServiceCollection services) + { + Services = services; + } + + public IServiceCollection Services { get; } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/AuthorityDefaults.cs b/DotBased.AspNet.Authority/AuthorityDefaults.cs new file mode 100644 index 0000000..a0b4726 --- /dev/null +++ b/DotBased.AspNet.Authority/AuthorityDefaults.cs @@ -0,0 +1,11 @@ +namespace DotBased.AspNet.Authority; + +public static class AuthorityDefaults +{ + public static class Scheme + { + public const string AuthenticationScheme = "Authority.Scheme.Authentication"; + public const string ExternalScheme = "Authority.Scheme.External"; + } + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs new file mode 100644 index 0000000..71f7730 --- /dev/null +++ b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs @@ -0,0 +1,17 @@ +using DotBased.AspNet.Authority.Interfaces; +using Microsoft.Extensions.DependencyInjection; + +namespace DotBased.AspNet.Authority; + +public static class AuthorityProviderExtensions +{ + public static AuthorityBuilder AddAuthorityProvider(this IServiceCollection services) where TModel : class + { + return new AuthorityBuilder(services); + } + + public static AuthorityBuilder AddAuthorityStore(this AuthorityBuilder authorityBuilder) where TStore : IAuthorityRepository + { + return authorityBuilder; + } +} \ No newline at end of file diff --git a/DotBased.AspNet.Auth/DotBased.AspNet.Auth.csproj b/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj similarity index 64% rename from DotBased.AspNet.Auth/DotBased.AspNet.Auth.csproj rename to DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj index 3af943e..14ddfd0 100644 --- a/DotBased.AspNet.Auth/DotBased.AspNet.Auth.csproj +++ b/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj @@ -7,8 +7,9 @@ - - + + ..\..\..\.nuget\packages\microsoft.extensions.dependencyinjection.abstractions\8.0.2\lib\net8.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll + @@ -16,16 +17,9 @@ - - ..\..\..\..\..\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.AspNet.Authority/Interfaces/IAttributeRepository.cs b/DotBased.AspNet.Authority/Interfaces/IAttributeRepository.cs new file mode 100644 index 0000000..d0f90d1 --- /dev/null +++ b/DotBased.AspNet.Authority/Interfaces/IAttributeRepository.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Interfaces; + +public interface IAttributeRepository +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Interfaces/IAuthorityRepository.cs b/DotBased.AspNet.Authority/Interfaces/IAuthorityRepository.cs new file mode 100644 index 0000000..3f09635 --- /dev/null +++ b/DotBased.AspNet.Authority/Interfaces/IAuthorityRepository.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Interfaces; + +public interface IAuthorityRepository +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Interfaces/IRoleRepository.cs b/DotBased.AspNet.Authority/Interfaces/IRoleRepository.cs new file mode 100644 index 0000000..9ad9dc8 --- /dev/null +++ b/DotBased.AspNet.Authority/Interfaces/IRoleRepository.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Interfaces; + +public interface IRoleRepository +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Interfaces/IUserRepository.cs b/DotBased.AspNet.Authority/Interfaces/IUserRepository.cs new file mode 100644 index 0000000..0bc7ad3 --- /dev/null +++ b/DotBased.AspNet.Authority/Interfaces/IUserRepository.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Interfaces; + +public interface IUserRepository +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs new file mode 100644 index 0000000..6c4f8a5 --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs @@ -0,0 +1,10 @@ +namespace DotBased.AspNet.Authority.Models.Authority; + +public class AuthorityUser : AuthorityUserBase +{ + public AuthorityUser() + { + Id = Guid.NewGuid(); + CreatedDate = DateTime.Now; + } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityUserBase.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityUserBase.cs new file mode 100644 index 0000000..184bd7f --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityUserBase.cs @@ -0,0 +1,32 @@ +using DotBased.AspNet.Authority.Attributes; + +namespace DotBased.AspNet.Authority.Models.Authority; + +public abstract class AuthorityUserBase where TKey : IEquatable +{ + public TKey Id { get; set; } + + public bool Enabled { get; set; } + + public bool Locked { get; set; } + + public string UserName { get; set; } + + public string PasswordHash { get; set; } + + public DateTime CreatedDate { get; set; } + + public bool TwoFactorEnabled { get; set; } + + + [Protect] + public string EmailAddress { get; set; } + + public bool EmailConfirmed { get; set; } + + [Protect] + public string PhoneNumber { get; set; } + + public bool PhoneNumberConfirmed { get; set; } + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Services/AuthorityService.cs b/DotBased.AspNet.Authority/Services/AuthorityService.cs new file mode 100644 index 0000000..9f90f61 --- /dev/null +++ b/DotBased.AspNet.Authority/Services/AuthorityService.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Services; + +public class AuthorityService +{ + +} \ No newline at end of file diff --git a/DotBased.sln b/DotBased.sln index 4221777..0275d48 100755 --- a/DotBased.sln +++ b/DotBased.sln @@ -20,7 +20,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazor.Wasm", "Blazor.Wasm\ 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}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotBased.AspNet.Authority", "DotBased.AspNet.Authority\DotBased.AspNet.Authority.csproj", "{A3ADC9AF-39B7-4EC4-8022-946118A8C322}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -56,10 +56,10 @@ 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 + {A3ADC9AF-39B7-4EC4-8022-946118A8C322}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A3ADC9AF-39B7-4EC4-8022-946118A8C322}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A3ADC9AF-39B7-4EC4-8022-946118A8C322}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A3ADC9AF-39B7-4EC4-8022-946118A8C322}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {EBBDAF9A-BFC7-4BDC-8C51-0501B59A1DDC} = {2156FB93-C252-4B33-8A0C-73C82FABB163} @@ -68,6 +68,6 @@ Global {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} + {A3ADC9AF-39B7-4EC4-8022-946118A8C322} = {624E7B11-8A18-46E5-AB1F-6AF6097F9D4D} EndGlobalSection EndGlobal diff --git a/TestWebApi/Program.cs b/TestWebApi/Program.cs index 32c7f05..f18c0ae 100644 --- a/TestWebApi/Program.cs +++ b/TestWebApi/Program.cs @@ -2,6 +2,7 @@ using DotBased.Logging; using DotBased.Logging.MEL; using DotBased.Logging.Serilog; using Serilog; +using TestWebApi; using ILogger = Serilog.ILogger; var builder = WebApplication.CreateBuilder(args); @@ -18,6 +19,12 @@ LogService.AddLogAdapter(new BasedSerilogAdapter(serilogLogger)); builder.Logging.ClearProviders(); builder.Logging.AddDotBasedLoggerProvider(LogService.Options); +/*builder.Services.AddAuthentication(options => +{ + options.DefaultScheme = BasedAuthenticationDefaults.BasedAuthenticationScheme; + options.DefaultChallengeScheme = BasedAuthenticationDefaults.BasedAuthenticationScheme; +}).AddCookie();*/ + // Add services to the container. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); @@ -25,6 +32,8 @@ builder.Services.AddSwaggerGen(); var app = builder.Build(); +await SeedAuthorityData.InitializeData(app.Services); + // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { diff --git a/TestWebApi/SeedAuthorityData.cs b/TestWebApi/SeedAuthorityData.cs new file mode 100644 index 0000000..5233886 --- /dev/null +++ b/TestWebApi/SeedAuthorityData.cs @@ -0,0 +1,9 @@ +namespace TestWebApi; + +public class SeedAuthorityData +{ + public static async Task InitializeData(IServiceProvider serviceProvider) + { + // Get the needed services and create users, roles, attributes. etc. + } +} \ No newline at end of file diff --git a/TestWebApi/TestWebApi.csproj b/TestWebApi/TestWebApi.csproj index c3c9828..ef97892 100644 --- a/TestWebApi/TestWebApi.csproj +++ b/TestWebApi/TestWebApi.csproj @@ -13,6 +13,7 @@ + From 7ebe1e1752562ea67a12ef575fcd832daa37050e Mon Sep 17 00:00:00 2001 From: max Date: Sat, 21 Dec 2024 16:14:27 +0100 Subject: [PATCH 02/24] [CHANGE] Extended base user model --- .../Models/Authority/AuthorityUserBase.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityUserBase.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityUserBase.cs index 184bd7f..83b8166 100644 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityUserBase.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityUserBase.cs @@ -17,6 +17,9 @@ public abstract class AuthorityUserBase where TKey : IEquatable public DateTime CreatedDate { get; set; } public bool TwoFactorEnabled { get; set; } + + public string ConcurrencyStamp { get; set; } + public string SecurityStamp { get; set; } [Protect] From 44e64793b7ddca23e49a05a6d9b4a5ab9bb29ec9 Mon Sep 17 00:00:00 2001 From: max Date: Sun, 22 Dec 2024 02:15:34 +0100 Subject: [PATCH 03/24] [ADD] Adding models, repositories. Implementing business logic. --- .../Attributes/ProtectAttribute.cs | 2 +- .../DotBased.AspNet.Authority.csproj | 1 - .../Interfaces/ISecurityVersionRepository.cs | 7 +++ .../Interfaces/IUserRepository.cs | 6 ++- .../Interfaces/IVersionRepository.cs | 6 +++ .../Models/Authority/AuthorityAttribute.cs | 25 +++++++++++ .../Models/Authority/AuthorityGroup.cs | 26 +++++++++++ .../Models/Authority/AuthorityRole.cs | 28 ++++++++++++ .../Models/Authority/AuthorityUser.cs | 44 ++++++++++++++++++- .../Models/Authority/AuthorityUserBase.cs | 35 --------------- .../Repositories/AuthorityRepository.cs | 6 +++ .../Services/AuthorityService.cs | 2 +- 12 files changed, 147 insertions(+), 41 deletions(-) create mode 100644 DotBased.AspNet.Authority/Interfaces/ISecurityVersionRepository.cs create mode 100644 DotBased.AspNet.Authority/Interfaces/IVersionRepository.cs create mode 100644 DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs create mode 100644 DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs create mode 100644 DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs delete mode 100644 DotBased.AspNet.Authority/Models/Authority/AuthorityUserBase.cs create mode 100644 DotBased.AspNet.Authority/Repositories/AuthorityRepository.cs diff --git a/DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs b/DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs index 13394b8..58775b1 100644 --- a/DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs +++ b/DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs @@ -1,7 +1,7 @@ namespace DotBased.AspNet.Authority.Attributes; /// -/// Indicates that the property should be protected. +/// Indicates to protect the property before saving to the repository. /// [AttributeUsage(AttributeTargets.Property)] public class ProtectAttribute : Attribute diff --git a/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj b/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj index 14ddfd0..b99e760 100644 --- a/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj +++ b/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj @@ -19,7 +19,6 @@ - diff --git a/DotBased.AspNet.Authority/Interfaces/ISecurityVersionRepository.cs b/DotBased.AspNet.Authority/Interfaces/ISecurityVersionRepository.cs new file mode 100644 index 0000000..866d22c --- /dev/null +++ b/DotBased.AspNet.Authority/Interfaces/ISecurityVersionRepository.cs @@ -0,0 +1,7 @@ +namespace DotBased.AspNet.Authority.Interfaces; + +public interface ISecurityVersionRepository +{ + public Task GetSecurityVersionAsync(TRepositoryObject obj); + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Interfaces/IUserRepository.cs b/DotBased.AspNet.Authority/Interfaces/IUserRepository.cs index 0bc7ad3..c7035a3 100644 --- a/DotBased.AspNet.Authority/Interfaces/IUserRepository.cs +++ b/DotBased.AspNet.Authority/Interfaces/IUserRepository.cs @@ -1,6 +1,8 @@ namespace DotBased.AspNet.Authority.Interfaces; -public interface IUserRepository +public interface IUserRepository : IVersionRepository, ISecurityVersionRepository where TUser : class where TId : IEquatable { - + public Task GetUserByIdAsync(TId id); + + public Task GetUserIdAsync(TUser user); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Interfaces/IVersionRepository.cs b/DotBased.AspNet.Authority/Interfaces/IVersionRepository.cs new file mode 100644 index 0000000..b88ec89 --- /dev/null +++ b/DotBased.AspNet.Authority/Interfaces/IVersionRepository.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Interfaces; + +public interface IVersionRepository +{ + public Task GetVersionAsync(TRepositoryObject obj); +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs new file mode 100644 index 0000000..dc4e5b5 --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs @@ -0,0 +1,25 @@ +namespace DotBased.AspNet.Authority.Models.Authority; + +public class AuthorityAttribute +{ + public AuthorityAttribute(string attributeKey, string bound) : this() + { + AttributeKey = attributeKey; + BoundId = bound; + } + + public AuthorityAttribute() + { + + } + + public string AttributeKey { get; set; } // ClaimType/Authority.attribute.enabled + + public string BoundId { get; set; } // Bound to User, Group, Role id + + public string? AttributeValue { get; set; } + + public string? Type { get; set; } // AspNet.Claim.Role/Property/Data.JSON, Data.Raw, Data.Base64 etc. + + public long Version { get; set; } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs new file mode 100644 index 0000000..df010e8 --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs @@ -0,0 +1,26 @@ +namespace DotBased.AspNet.Authority.Models.Authority; + +public class AuthorityGroup : AuthorityGroup +{ + public AuthorityGroup(string name) : this() + { + Name = name; + } + + public AuthorityGroup() + { + Id = Guid.NewGuid(); + CreatedDate = DateTime.Now; + } +} + +public abstract class AuthorityGroup where TKey : IEquatable +{ + public TKey Id { get; set; } + + public string? Name { get; set; } + + public long Version { get; set; } + + public DateTime CreatedDate { get; set; } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs new file mode 100644 index 0000000..90ccee5 --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs @@ -0,0 +1,28 @@ +namespace DotBased.AspNet.Authority.Models.Authority; + +public class AuthorityRole : AuthorityRole +{ + public AuthorityRole(string name) : this() + { + Name = name; + } + + public AuthorityRole() + { + Id = Guid.NewGuid(); + CreatedDate = DateTime.Now; + } +} + +public abstract class AuthorityRole where TKey : IEquatable +{ + public TKey Id { get; set; } + + public string? Name { get; set; } + + public long Version { get; set; } + + public DateTime CreatedDate { get; set; } + + public override string ToString() => Name ?? string.Empty; +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs index 6c4f8a5..7ed6f9b 100644 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs @@ -1,10 +1,52 @@ +using DotBased.AspNet.Authority.Attributes; + namespace DotBased.AspNet.Authority.Models.Authority; -public class AuthorityUser : AuthorityUserBase +public class AuthorityUser : AuthorityUser { + public AuthorityUser(string userName) : this() + { + UserName = userName; + } + public AuthorityUser() { Id = Guid.NewGuid(); CreatedDate = DateTime.Now; } +} + +public abstract class AuthorityUser where TKey : IEquatable +{ + public TKey Id { get; set; } + + public bool Enabled { get; set; } + + public bool Locked { get; set; } + + public DateTime LockedDate { get; set; } + + public string? UserName { get; set; } + + public string? PasswordHash { get; set; } + + public DateTime CreatedDate { get; set; } + + public bool TwoFactorEnabled { get; set; } + + public long Version { get; set; } + + public long SecurityVersion { get; set; } + + [Protect] + public string? EmailAddress { get; set; } + + public bool EmailConfirmed { get; set; } + + [Protect] + public string? PhoneNumber { get; set; } + + public bool PhoneNumberConfirmed { get; set; } + + public override string ToString() => UserName ?? EmailAddress ?? string.Empty; } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityUserBase.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityUserBase.cs deleted file mode 100644 index 83b8166..0000000 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityUserBase.cs +++ /dev/null @@ -1,35 +0,0 @@ -using DotBased.AspNet.Authority.Attributes; - -namespace DotBased.AspNet.Authority.Models.Authority; - -public abstract class AuthorityUserBase where TKey : IEquatable -{ - public TKey Id { get; set; } - - public bool Enabled { get; set; } - - public bool Locked { get; set; } - - public string UserName { get; set; } - - public string PasswordHash { get; set; } - - public DateTime CreatedDate { get; set; } - - public bool TwoFactorEnabled { get; set; } - - public string ConcurrencyStamp { get; set; } - public string SecurityStamp { get; set; } - - - [Protect] - public string EmailAddress { get; set; } - - public bool EmailConfirmed { get; set; } - - [Protect] - public string PhoneNumber { get; set; } - - public bool PhoneNumberConfirmed { get; set; } - -} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/AuthorityRepository.cs b/DotBased.AspNet.Authority/Repositories/AuthorityRepository.cs new file mode 100644 index 0000000..239c114 --- /dev/null +++ b/DotBased.AspNet.Authority/Repositories/AuthorityRepository.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Repositories; + +public class AuthorityRepository // Inherit the repository interfaces? +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Services/AuthorityService.cs b/DotBased.AspNet.Authority/Services/AuthorityService.cs index 9f90f61..2c61b55 100644 --- a/DotBased.AspNet.Authority/Services/AuthorityService.cs +++ b/DotBased.AspNet.Authority/Services/AuthorityService.cs @@ -2,5 +2,5 @@ namespace DotBased.AspNet.Authority.Services; public class AuthorityService { - + public long GenerateVersion() => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); } \ No newline at end of file From 797323789ecb0bb85bd461f888cf960d1ce7ea9e Mon Sep 17 00:00:00 2001 From: max Date: Mon, 23 Dec 2024 00:59:13 +0100 Subject: [PATCH 04/24] [ADD] Added base options --- .../Interfaces/ISecurityVersionRepository.cs | 7 ------- .../Interfaces/IUserRepository.cs | 5 +++-- .../Interfaces/IVersionRepository.cs | 6 ------ .../Models/Authority/AuthorityAttribute.cs | 2 +- .../Models/Authority/AuthorityUser.cs | 2 ++ .../Models/Options/AuthorityOptions.cs | 10 ++++++++++ .../Models/Options/LockdownOptions.cs | 6 ++++++ .../Models/Options/LockoutOptions.cs | 8 ++++++++ .../Models/Options/PasswordOptions.cs | 14 ++++++++++++++ .../Models/Options/ProviderOptions.cs | 6 ++++++ .../Models/Options/SignInOptions.cs | 8 ++++++++ .../Models/Options/UserOptions.cs | 11 +++++++++++ 12 files changed, 69 insertions(+), 16 deletions(-) delete mode 100644 DotBased.AspNet.Authority/Interfaces/ISecurityVersionRepository.cs delete mode 100644 DotBased.AspNet.Authority/Interfaces/IVersionRepository.cs create mode 100644 DotBased.AspNet.Authority/Models/Options/AuthorityOptions.cs create mode 100644 DotBased.AspNet.Authority/Models/Options/LockdownOptions.cs create mode 100644 DotBased.AspNet.Authority/Models/Options/LockoutOptions.cs create mode 100644 DotBased.AspNet.Authority/Models/Options/PasswordOptions.cs create mode 100644 DotBased.AspNet.Authority/Models/Options/ProviderOptions.cs create mode 100644 DotBased.AspNet.Authority/Models/Options/SignInOptions.cs create mode 100644 DotBased.AspNet.Authority/Models/Options/UserOptions.cs diff --git a/DotBased.AspNet.Authority/Interfaces/ISecurityVersionRepository.cs b/DotBased.AspNet.Authority/Interfaces/ISecurityVersionRepository.cs deleted file mode 100644 index 866d22c..0000000 --- a/DotBased.AspNet.Authority/Interfaces/ISecurityVersionRepository.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace DotBased.AspNet.Authority.Interfaces; - -public interface ISecurityVersionRepository -{ - public Task GetSecurityVersionAsync(TRepositoryObject obj); - -} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Interfaces/IUserRepository.cs b/DotBased.AspNet.Authority/Interfaces/IUserRepository.cs index c7035a3..c2c420c 100644 --- a/DotBased.AspNet.Authority/Interfaces/IUserRepository.cs +++ b/DotBased.AspNet.Authority/Interfaces/IUserRepository.cs @@ -1,8 +1,9 @@ namespace DotBased.AspNet.Authority.Interfaces; -public interface IUserRepository : IVersionRepository, ISecurityVersionRepository where TUser : class where TId : IEquatable +public interface IUserRepository where TUser : class where TId : IEquatable { public Task GetUserByIdAsync(TId id); - public Task GetUserIdAsync(TUser user); + public Task SetVersion(TUser user, long version); + public Task SetSecurityVersion(TUser user, long version); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Interfaces/IVersionRepository.cs b/DotBased.AspNet.Authority/Interfaces/IVersionRepository.cs deleted file mode 100644 index b88ec89..0000000 --- a/DotBased.AspNet.Authority/Interfaces/IVersionRepository.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace DotBased.AspNet.Authority.Interfaces; - -public interface IVersionRepository -{ - public Task GetVersionAsync(TRepositoryObject obj); -} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs index dc4e5b5..1057db7 100644 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs @@ -17,7 +17,7 @@ public class AuthorityAttribute public string BoundId { get; set; } // Bound to User, Group, Role id - public string? AttributeValue { get; set; } + public object? AttributeValue { get; set; } public string? Type { get; set; } // AspNet.Claim.Role/Property/Data.JSON, Data.Raw, Data.Base64 etc. diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs index 7ed6f9b..bbc919f 100644 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs @@ -22,6 +22,8 @@ public abstract class AuthorityUser where TKey : IEquatable public bool Enabled { get; set; } + public bool Confirmed { get; set; } + public bool Locked { get; set; } public DateTime LockedDate { get; set; } diff --git a/DotBased.AspNet.Authority/Models/Options/AuthorityOptions.cs b/DotBased.AspNet.Authority/Models/Options/AuthorityOptions.cs new file mode 100644 index 0000000..77f631c --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Options/AuthorityOptions.cs @@ -0,0 +1,10 @@ +namespace DotBased.AspNet.Authority.Models.Options; + +public class AuthorityOptions +{ + public LockdownOptions Lockdown { get; set; } = new(); + public LockoutOptions Lockout { get; set; } = new(); + public PasswordOptions Password { get; set; } = new(); + public ProviderOptions Provider { get; set; } = new(); + public UserOptions User { get; set; } = new(); +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Options/LockdownOptions.cs b/DotBased.AspNet.Authority/Models/Options/LockdownOptions.cs new file mode 100644 index 0000000..aefbf76 --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Options/LockdownOptions.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Models.Options; + +public class LockdownOptions +{ + public bool EnableLockout { get; set; } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Options/LockoutOptions.cs b/DotBased.AspNet.Authority/Models/Options/LockoutOptions.cs new file mode 100644 index 0000000..6debdbd --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Options/LockoutOptions.cs @@ -0,0 +1,8 @@ +namespace DotBased.AspNet.Authority.Models.Options; + +public class LockoutOptions +{ + public bool EnableLockout { get; set; } = true; + public int FailedAttempts { get; set; } = 3; + public TimeSpan LockoutTimeout { get; set; } = TimeSpan.FromMinutes(30); +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Options/PasswordOptions.cs b/DotBased.AspNet.Authority/Models/Options/PasswordOptions.cs new file mode 100644 index 0000000..28000ba --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Options/PasswordOptions.cs @@ -0,0 +1,14 @@ +namespace DotBased.AspNet.Authority.Models.Options; + +public class PasswordOptions +{ + public int RequiredLength { get; set; } = 10; + public int MinimalUniqueChars { get; set; } = 1; + public bool RequireLowercase { get; set; } + public bool RequireUppercase { get; set; } + public bool RequireDigit { get; set; } + public bool RequireNonAlphanumeric { get; set; } + + public List PasswordBlackList { get; set; } = ["password", "1234"]; + public StringComparer PasswordBlackListComparer { get; set; } = StringComparer.OrdinalIgnoreCase; +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Options/ProviderOptions.cs b/DotBased.AspNet.Authority/Models/Options/ProviderOptions.cs new file mode 100644 index 0000000..ba11c01 --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Options/ProviderOptions.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Models.Options; + +public class ProviderOptions +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Options/SignInOptions.cs b/DotBased.AspNet.Authority/Models/Options/SignInOptions.cs new file mode 100644 index 0000000..f25e2be --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Options/SignInOptions.cs @@ -0,0 +1,8 @@ +namespace DotBased.AspNet.Authority.Models.Options; + +public class SignInOptions +{ + public bool RequireValidatedEmail { get; set; } + public bool RequireValidatedPhoneNumber { get; set; } + public bool RequireConfirmedAccount { get; set; } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Options/UserOptions.cs b/DotBased.AspNet.Authority/Models/Options/UserOptions.cs new file mode 100644 index 0000000..54011e7 --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Options/UserOptions.cs @@ -0,0 +1,11 @@ +namespace DotBased.AspNet.Authority.Models.Options; + +public class UserOptions +{ + public bool EnableRegister { get; set; } + public bool RequireUniqueEmail { get; set; } + public string AllowedCharacters { get; set; } = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@"; + + public List UserNameBlackList { get; set; } = ["admin", "administrator", "dev", "developer"]; + public StringComparer UserNameBlackListComparer { get; set; } = StringComparer.OrdinalIgnoreCase; +} \ No newline at end of file From 5c4ebd2b32e65b4eb0236b4c5107f10825129c20 Mon Sep 17 00:00:00 2001 From: max Date: Mon, 23 Dec 2024 01:26:21 +0100 Subject: [PATCH 05/24] [ADD] Added verifiers, validators & config. --- DotBased.AspNet.Authority/AuthorityProviderExtensions.cs | 6 +++++- DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj | 4 ++++ .../Models/Authority/AuthorityAttribute.cs | 5 +++-- DotBased.AspNet.Authority/Models/Options/SignInOptions.cs | 4 ++-- .../Services/{AuthorityService.cs => AuthorityManager.cs} | 2 +- DotBased.AspNet.Authority/Validators/IPasswordValidator.cs | 6 ++++++ DotBased.AspNet.Authority/Validators/IUserValidator.cs | 6 ++++++ DotBased.AspNet.Authority/Validators/PasswordValidator.cs | 6 ++++++ DotBased.AspNet.Authority/Validators/UserValidator.cs | 6 ++++++ DotBased.AspNet.Authority/Verifiers/IEmailVerifier.cs | 6 ++++++ DotBased.AspNet.Authority/Verifiers/IPhoneNumberVerifier.cs | 6 ++++++ DotBased.AspNet.Authority/Verifiers/IUserVerifier.cs | 6 ++++++ 12 files changed, 57 insertions(+), 6 deletions(-) rename DotBased.AspNet.Authority/Services/{AuthorityService.cs => AuthorityManager.cs} (77%) create mode 100644 DotBased.AspNet.Authority/Validators/IPasswordValidator.cs create mode 100644 DotBased.AspNet.Authority/Validators/IUserValidator.cs create mode 100644 DotBased.AspNet.Authority/Validators/PasswordValidator.cs create mode 100644 DotBased.AspNet.Authority/Validators/UserValidator.cs create mode 100644 DotBased.AspNet.Authority/Verifiers/IEmailVerifier.cs create mode 100644 DotBased.AspNet.Authority/Verifiers/IPhoneNumberVerifier.cs create mode 100644 DotBased.AspNet.Authority/Verifiers/IUserVerifier.cs diff --git a/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs index 71f7730..c67c51a 100644 --- a/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs +++ b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs @@ -1,12 +1,16 @@ using DotBased.AspNet.Authority.Interfaces; +using DotBased.AspNet.Authority.Models.Options; using Microsoft.Extensions.DependencyInjection; namespace DotBased.AspNet.Authority; public static class AuthorityProviderExtensions { - public static AuthorityBuilder AddAuthorityProvider(this IServiceCollection services) where TModel : class + public static AuthorityBuilder AddAuthorityProvider(this IServiceCollection services, Action optionsAction) where TModel : class { + services.AddOptions(); + // Configure required classes, services, etc. + services.Configure(optionsAction); return new AuthorityBuilder(services); } diff --git a/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj b/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj index b99e760..f056f35 100644 --- a/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj +++ b/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj @@ -21,4 +21,8 @@ + + + + diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs index 1057db7..5bc4d6e 100644 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs @@ -2,7 +2,7 @@ namespace DotBased.AspNet.Authority.Models.Authority; public class AuthorityAttribute { - public AuthorityAttribute(string attributeKey, string bound) : this() + public AuthorityAttribute(string attributeKey, string bound) { AttributeKey = attributeKey; BoundId = bound; @@ -10,7 +10,8 @@ public class AuthorityAttribute public AuthorityAttribute() { - + AttributeKey = string.Empty; + BoundId = string.Empty; } public string AttributeKey { get; set; } // ClaimType/Authority.attribute.enabled diff --git a/DotBased.AspNet.Authority/Models/Options/SignInOptions.cs b/DotBased.AspNet.Authority/Models/Options/SignInOptions.cs index f25e2be..8c142db 100644 --- a/DotBased.AspNet.Authority/Models/Options/SignInOptions.cs +++ b/DotBased.AspNet.Authority/Models/Options/SignInOptions.cs @@ -2,7 +2,7 @@ namespace DotBased.AspNet.Authority.Models.Options; public class SignInOptions { - public bool RequireValidatedEmail { get; set; } - public bool RequireValidatedPhoneNumber { get; set; } + public bool RequireVerifiedEmail { get; set; } + public bool RequireVerifiedPhoneNumber { get; set; } public bool RequireConfirmedAccount { get; set; } } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Services/AuthorityService.cs b/DotBased.AspNet.Authority/Services/AuthorityManager.cs similarity index 77% rename from DotBased.AspNet.Authority/Services/AuthorityService.cs rename to DotBased.AspNet.Authority/Services/AuthorityManager.cs index 2c61b55..3cb5eb7 100644 --- a/DotBased.AspNet.Authority/Services/AuthorityService.cs +++ b/DotBased.AspNet.Authority/Services/AuthorityManager.cs @@ -1,6 +1,6 @@ namespace DotBased.AspNet.Authority.Services; -public class AuthorityService +public class AuthorityManager { public long GenerateVersion() => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs b/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs new file mode 100644 index 0000000..2fe5b5c --- /dev/null +++ b/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Validators; + +public interface IPasswordValidator +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/IUserValidator.cs b/DotBased.AspNet.Authority/Validators/IUserValidator.cs new file mode 100644 index 0000000..cb7e245 --- /dev/null +++ b/DotBased.AspNet.Authority/Validators/IUserValidator.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Validators; + +public interface IUserValidator +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/PasswordValidator.cs b/DotBased.AspNet.Authority/Validators/PasswordValidator.cs new file mode 100644 index 0000000..33ce063 --- /dev/null +++ b/DotBased.AspNet.Authority/Validators/PasswordValidator.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Validators; + +public class PasswordValidator +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/UserValidator.cs b/DotBased.AspNet.Authority/Validators/UserValidator.cs new file mode 100644 index 0000000..1175fc8 --- /dev/null +++ b/DotBased.AspNet.Authority/Validators/UserValidator.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Validators; + +public class UserValidator +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Verifiers/IEmailVerifier.cs b/DotBased.AspNet.Authority/Verifiers/IEmailVerifier.cs new file mode 100644 index 0000000..63172f0 --- /dev/null +++ b/DotBased.AspNet.Authority/Verifiers/IEmailVerifier.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Verifiers; + +public interface IEmailVerifier +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Verifiers/IPhoneNumberVerifier.cs b/DotBased.AspNet.Authority/Verifiers/IPhoneNumberVerifier.cs new file mode 100644 index 0000000..92e25bd --- /dev/null +++ b/DotBased.AspNet.Authority/Verifiers/IPhoneNumberVerifier.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Verifiers; + +public interface IPhoneNumberVerifier +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Verifiers/IUserVerifier.cs b/DotBased.AspNet.Authority/Verifiers/IUserVerifier.cs new file mode 100644 index 0000000..c41347f --- /dev/null +++ b/DotBased.AspNet.Authority/Verifiers/IUserVerifier.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Verifiers; + +public class IUserVerifier +{ + +} \ No newline at end of file From 361af3403621dcffdfc2ec57b9d35d1ce5e0748a Mon Sep 17 00:00:00 2001 From: max Date: Mon, 23 Dec 2024 15:59:24 +0100 Subject: [PATCH 06/24] [CHANGE] Add extension method --- DotBased.AspNet.Authority/AuthorityProviderExtensions.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs index c67c51a..82326b0 100644 --- a/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs +++ b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs @@ -18,4 +18,9 @@ public static class AuthorityProviderExtensions { return authorityBuilder; } + + public static AuthorityBuilder MapAuthorityEndpoints(this AuthorityBuilder builder) + { + return builder; + } } \ No newline at end of file From ebfafa2f29a0dbd3228e72ae50e4369a3b098fd5 Mon Sep 17 00:00:00 2001 From: max Date: Wed, 25 Dec 2024 22:50:04 +0100 Subject: [PATCH 07/24] [ADD] Implementing services/handlers --- .../Attributes/ProtectAttribute.cs | 2 +- .../AuthorityProviderExtensions.cs | 33 +++++-- .../Crypto/Cryptographer.cs | 14 +++ .../Crypto/ICryptographer.cs | 7 ++ .../Crypto/IPasswordHasher.cs | 6 ++ .../Crypto/PasswordHasher.cs | 9 ++ .../DotBased.AspNet.Authority.csproj | 1 - .../Interfaces/IAttributeRepository.cs | 6 -- .../Interfaces/IAuthorityRepository.cs | 6 -- .../Interfaces/IRoleRepository.cs | 6 -- .../Models/Options/AuthorityOptions.cs | 1 + .../Models/Options/RepositoryOptions.cs | 10 ++ .../Models/Validation/ValidationError.cs | 24 +++++ .../Models/Validation/ValidationResult.cs | 21 ++++ .../Repositories/AuthorityRepository.cs | 6 -- .../Repositories/IAttributeRepository.cs | 6 ++ .../Repositories/IAuthorityRepository.cs | 7 ++ .../Repositories/IGroupRepository.cs | 6 ++ .../Repositories/IRoleRepository.cs | 6 ++ .../IUserRepository.cs | 2 +- .../Services/AuthorityGroupManager.cs | 6 ++ .../Services/AuthorityManager.cs | 96 ++++++++++++++++++- .../Services/AuthorityRoleManager.cs | 6 ++ .../Services/AuthorityUserManager.cs | 28 ++++++ .../Validators/IPasswordValidator.cs | 5 +- .../Validators/IUserValidator.cs | 2 +- .../Validators/PasswordOptionsValidator.cs | 66 +++++++++++++ .../Validators/PasswordValidator.cs | 6 -- .../Validators/UserValidator.cs | 2 +- .../Verifiers/IUserVerifier.cs | 2 +- TestWebApi/Program.cs | 6 ++ 31 files changed, 360 insertions(+), 44 deletions(-) create mode 100644 DotBased.AspNet.Authority/Crypto/Cryptographer.cs create mode 100644 DotBased.AspNet.Authority/Crypto/ICryptographer.cs create mode 100644 DotBased.AspNet.Authority/Crypto/IPasswordHasher.cs create mode 100644 DotBased.AspNet.Authority/Crypto/PasswordHasher.cs delete mode 100644 DotBased.AspNet.Authority/Interfaces/IAttributeRepository.cs delete mode 100644 DotBased.AspNet.Authority/Interfaces/IAuthorityRepository.cs delete mode 100644 DotBased.AspNet.Authority/Interfaces/IRoleRepository.cs create mode 100644 DotBased.AspNet.Authority/Models/Options/RepositoryOptions.cs create mode 100644 DotBased.AspNet.Authority/Models/Validation/ValidationError.cs create mode 100644 DotBased.AspNet.Authority/Models/Validation/ValidationResult.cs delete mode 100644 DotBased.AspNet.Authority/Repositories/AuthorityRepository.cs create mode 100644 DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs create mode 100644 DotBased.AspNet.Authority/Repositories/IAuthorityRepository.cs create mode 100644 DotBased.AspNet.Authority/Repositories/IGroupRepository.cs create mode 100644 DotBased.AspNet.Authority/Repositories/IRoleRepository.cs rename DotBased.AspNet.Authority/{Interfaces => Repositories}/IUserRepository.cs (85%) create mode 100644 DotBased.AspNet.Authority/Services/AuthorityGroupManager.cs create mode 100644 DotBased.AspNet.Authority/Services/AuthorityRoleManager.cs create mode 100644 DotBased.AspNet.Authority/Services/AuthorityUserManager.cs create mode 100644 DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs delete mode 100644 DotBased.AspNet.Authority/Validators/PasswordValidator.cs diff --git a/DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs b/DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs index 58775b1..1d1749e 100644 --- a/DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs +++ b/DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs @@ -1,7 +1,7 @@ namespace DotBased.AspNet.Authority.Attributes; /// -/// Indicates to protect the property before saving to the repository. +/// Indicates to protect the property before saving/loading to the repository. /// [AttributeUsage(AttributeTargets.Property)] public class ProtectAttribute : Attribute diff --git a/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs index 82326b0..b99a7ec 100644 --- a/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs +++ b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs @@ -1,20 +1,41 @@ -using DotBased.AspNet.Authority.Interfaces; +using DotBased.AspNet.Authority.Crypto; +using DotBased.AspNet.Authority.Models.Authority; using DotBased.AspNet.Authority.Models.Options; +using DotBased.AspNet.Authority.Services; +using DotBased.AspNet.Authority.Validators; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; namespace DotBased.AspNet.Authority; public static class AuthorityProviderExtensions { - public static AuthorityBuilder AddAuthorityProvider(this IServiceCollection services, Action optionsAction) where TModel : class + public static AuthorityBuilder AddAuthority(this IServiceCollection services, Action? optionsAction = null) + => services.AddAuthority(optionsAction); + + public static AuthorityBuilder AddAuthority(this IServiceCollection services, Action? optionsAction = null) + where TUser : class where TGroup : class where TRole : class { - services.AddOptions(); - // Configure required classes, services, etc. - services.Configure(optionsAction); + if (optionsAction != null) + { + services.AddOptions(); + services.Configure(optionsAction); + } + services.TryAddScoped(); + services.TryAddScoped(); + services.TryAddScoped, PasswordOptionsValidator>(); + services.TryAddScoped, UserValidator>(); + /*services.TryAddScoped(); + services.TryAddScoped(); + services.TryAddScoped();*/ + services.TryAddScoped(); + services.TryAddScoped>(); + services.TryAddScoped>(); + services.TryAddScoped>(); return new AuthorityBuilder(services); } - public static AuthorityBuilder AddAuthorityStore(this AuthorityBuilder authorityBuilder) where TStore : IAuthorityRepository + public static AuthorityBuilder AddAuthorityRepository(this AuthorityBuilder authorityBuilder) where TRepository : class { return authorityBuilder; } diff --git a/DotBased.AspNet.Authority/Crypto/Cryptographer.cs b/DotBased.AspNet.Authority/Crypto/Cryptographer.cs new file mode 100644 index 0000000..d6a3416 --- /dev/null +++ b/DotBased.AspNet.Authority/Crypto/Cryptographer.cs @@ -0,0 +1,14 @@ +namespace DotBased.AspNet.Authority.Crypto; + +public class Cryptographer : ICryptographer +{ + public Task EncryptAsync(string data) + { + throw new NotImplementedException(); + } + + public Task DecryptAsync(string data) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Crypto/ICryptographer.cs b/DotBased.AspNet.Authority/Crypto/ICryptographer.cs new file mode 100644 index 0000000..a043271 --- /dev/null +++ b/DotBased.AspNet.Authority/Crypto/ICryptographer.cs @@ -0,0 +1,7 @@ +namespace DotBased.AspNet.Authority.Crypto; + +public interface ICryptographer +{ + public Task EncryptAsync(string data); + public Task DecryptAsync(string data); +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Crypto/IPasswordHasher.cs b/DotBased.AspNet.Authority/Crypto/IPasswordHasher.cs new file mode 100644 index 0000000..23cd3ef --- /dev/null +++ b/DotBased.AspNet.Authority/Crypto/IPasswordHasher.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Crypto; + +public interface IPasswordHasher +{ + public Task HashPasswordAsync(string password); +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Crypto/PasswordHasher.cs b/DotBased.AspNet.Authority/Crypto/PasswordHasher.cs new file mode 100644 index 0000000..38ad043 --- /dev/null +++ b/DotBased.AspNet.Authority/Crypto/PasswordHasher.cs @@ -0,0 +1,9 @@ +namespace DotBased.AspNet.Authority.Crypto; + +public class PasswordHasher : IPasswordHasher +{ + public async Task HashPasswordAsync(string password) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj b/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj index f056f35..53d8b22 100644 --- a/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj +++ b/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj @@ -17,7 +17,6 @@ - diff --git a/DotBased.AspNet.Authority/Interfaces/IAttributeRepository.cs b/DotBased.AspNet.Authority/Interfaces/IAttributeRepository.cs deleted file mode 100644 index d0f90d1..0000000 --- a/DotBased.AspNet.Authority/Interfaces/IAttributeRepository.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace DotBased.AspNet.Authority.Interfaces; - -public interface IAttributeRepository -{ - -} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Interfaces/IAuthorityRepository.cs b/DotBased.AspNet.Authority/Interfaces/IAuthorityRepository.cs deleted file mode 100644 index 3f09635..0000000 --- a/DotBased.AspNet.Authority/Interfaces/IAuthorityRepository.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace DotBased.AspNet.Authority.Interfaces; - -public interface IAuthorityRepository -{ - -} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Interfaces/IRoleRepository.cs b/DotBased.AspNet.Authority/Interfaces/IRoleRepository.cs deleted file mode 100644 index 9ad9dc8..0000000 --- a/DotBased.AspNet.Authority/Interfaces/IRoleRepository.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace DotBased.AspNet.Authority.Interfaces; - -public interface IRoleRepository -{ - -} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Options/AuthorityOptions.cs b/DotBased.AspNet.Authority/Models/Options/AuthorityOptions.cs index 77f631c..6ab3e04 100644 --- a/DotBased.AspNet.Authority/Models/Options/AuthorityOptions.cs +++ b/DotBased.AspNet.Authority/Models/Options/AuthorityOptions.cs @@ -6,5 +6,6 @@ public class AuthorityOptions public LockoutOptions Lockout { get; set; } = new(); public PasswordOptions Password { get; set; } = new(); public ProviderOptions Provider { get; set; } = new(); + public RepositoryOptions Repository { get; set; } = new(); public UserOptions User { get; set; } = new(); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Options/RepositoryOptions.cs b/DotBased.AspNet.Authority/Models/Options/RepositoryOptions.cs new file mode 100644 index 0000000..454af3e --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Options/RepositoryOptions.cs @@ -0,0 +1,10 @@ +namespace DotBased.AspNet.Authority.Models.Options; + +public class RepositoryOptions +{ + /// + /// Use data encryption when a property has the defined. + /// Default: true + /// + public bool UseDataProtection { get; set; } = true; +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Validation/ValidationError.cs b/DotBased.AspNet.Authority/Models/Validation/ValidationError.cs new file mode 100644 index 0000000..d65c178 --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Validation/ValidationError.cs @@ -0,0 +1,24 @@ +namespace DotBased.AspNet.Authority.Models.Validation; + +public class ValidationError +{ + public ValidationError(string validator, string errorCode, string description) + { + Validator = validator; + ErrorCode = errorCode; + Description = description; + } + + /// + /// The validator name that generated this error. + /// + public string Validator { get; } + /// + /// The error code + /// + public string ErrorCode { get; } + /// + /// Error description + /// + public string Description { get; } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Validation/ValidationResult.cs b/DotBased.AspNet.Authority/Models/Validation/ValidationResult.cs new file mode 100644 index 0000000..aea2d80 --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Validation/ValidationResult.cs @@ -0,0 +1,21 @@ +namespace DotBased.AspNet.Authority.Models.Validation; + +public class ValidationResult +{ + public ValidationResult(bool success, IEnumerable? errors = null) + { + if (errors != null) + { + Errors = errors.ToList(); + } + Success = success; + } + + public bool Success { get; } + public IReadOnlyList Errors { get; } = []; + + public static ValidationResult Failed(IEnumerable errors) => new(false, errors); + public static ValidationResult Ok() => new(true); + + public override string ToString() => Success ? "Success" : $"Failed ({Errors.Count} errors)"; +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/AuthorityRepository.cs b/DotBased.AspNet.Authority/Repositories/AuthorityRepository.cs deleted file mode 100644 index 239c114..0000000 --- a/DotBased.AspNet.Authority/Repositories/AuthorityRepository.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace DotBased.AspNet.Authority.Repositories; - -public class AuthorityRepository // Inherit the repository interfaces? -{ - -} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs b/DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs new file mode 100644 index 0000000..92272f3 --- /dev/null +++ b/DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Repositories; + +public interface IAttributeRepository where TAttribute : class where TId : IEquatable +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/IAuthorityRepository.cs b/DotBased.AspNet.Authority/Repositories/IAuthorityRepository.cs new file mode 100644 index 0000000..8c0af84 --- /dev/null +++ b/DotBased.AspNet.Authority/Repositories/IAuthorityRepository.cs @@ -0,0 +1,7 @@ +namespace DotBased.AspNet.Authority.Repositories; + +public interface IAuthorityRepository +{ + public Task GetVersion(); + public Task SetVersion(int version); +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs b/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs new file mode 100644 index 0000000..667f839 --- /dev/null +++ b/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Repositories; + +public interface IGroupRepository where TGroup : class where TId : IEquatable +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs b/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs new file mode 100644 index 0000000..e3c9b4c --- /dev/null +++ b/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Repositories; + +public interface IRoleRepository where TRole : class where TId : IEquatable +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Interfaces/IUserRepository.cs b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs similarity index 85% rename from DotBased.AspNet.Authority/Interfaces/IUserRepository.cs rename to DotBased.AspNet.Authority/Repositories/IUserRepository.cs index c2c420c..23c4c6e 100644 --- a/DotBased.AspNet.Authority/Interfaces/IUserRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs @@ -1,4 +1,4 @@ -namespace DotBased.AspNet.Authority.Interfaces; +namespace DotBased.AspNet.Authority.Repositories; public interface IUserRepository where TUser : class where TId : IEquatable { diff --git a/DotBased.AspNet.Authority/Services/AuthorityGroupManager.cs b/DotBased.AspNet.Authority/Services/AuthorityGroupManager.cs new file mode 100644 index 0000000..b8db5ea --- /dev/null +++ b/DotBased.AspNet.Authority/Services/AuthorityGroupManager.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Services; + +public class AuthorityGroupManager +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Services/AuthorityManager.cs b/DotBased.AspNet.Authority/Services/AuthorityManager.cs index 3cb5eb7..2cf9d19 100644 --- a/DotBased.AspNet.Authority/Services/AuthorityManager.cs +++ b/DotBased.AspNet.Authority/Services/AuthorityManager.cs @@ -1,6 +1,100 @@ +using System.Reflection; +using DotBased.AspNet.Authority.Attributes; +using DotBased.AspNet.Authority.Crypto; +using DotBased.AspNet.Authority.Models.Options; +using DotBased.AspNet.Authority.Repositories; +using DotBased.Logging; +using Microsoft.Extensions.Options; + namespace DotBased.AspNet.Authority.Services; -public class AuthorityManager +public class AuthorityManager { + public AuthorityManager( + IOptions options, + IServiceProvider services, + IAuthorityRepository repository, + ICryptographer cryptographer) + { + _logger = LogService.RegisterLogger(); + Options = options.Value ?? new AuthorityOptions(); + Services = services; + Repository = repository; + Cryptographer = cryptographer; + } + + private readonly ILogger _logger; + + public IServiceProvider Services { get; } + public AuthorityOptions Options { get; } + public IAuthorityRepository Repository { get; } + public ICryptographer Cryptographer { get; } + + public long GenerateVersion() => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + + /// + /// Protect or unprotect the properties with the + /// + /// The data model + /// True for protection false for unprotection. + /// The class with the properties to protect. + public async Task HandlePropertyProtection(TModel data, bool protection) + { + var props = GetProtectedPropertiesValues(data); + if (Cryptographer == null) + { + _logger.Warning("No cryptographer specified! Cannot encrypt/decrypt properties."); + return; + } + if (props.Count == 0) + { + return; + } + + var handledProperties = 0; + foreach (var property in props) + { + if (property.PropertyType != typeof(string)) + { + _logger.Warning("Property({PropName}) with type: {PropType} detected, encrypting only supports strings! Skipping property!", property.Name, property.PropertyType); + continue; + } + + string? cryptString; + if (protection) + { + cryptString = await Cryptographer.EncryptAsync(property.GetValue(data)?.ToString() ?? string.Empty); + } + else + { + cryptString = await Cryptographer.DecryptAsync(property.GetValue(data)?.ToString() ?? string.Empty); + } + + if (cryptString == null) + { + _logger.Warning("{Protection} failed for property {PropName}", protection ? "Encyption" : "Decyption", property.Name); + continue; + } + property.SetValue(data, cryptString); + handledProperties++; + } + _logger.Debug("{HandledPropCount}/{TotalPropCount} protection properties handled!", handledProperties, props.Count); + } + + public bool IsPropertieProtected(string propertieName) + { + var protectedProperties = GetProtectedProperties(); + var propertieFound = protectedProperties.Where(propInfo => propInfo.Name == propertieName); + return propertieFound.Any(); + } + + public List GetProtectedPropertiesValues(TModel model) + { + var protectedProperties = GetProtectedProperties(); + return protectedProperties.Count != 0 ? protectedProperties : []; + } + + public List GetProtectedProperties() + => typeof(TModel).GetProperties().Where(p => Attribute.IsDefined(p, typeof(ProtectAttribute))).ToList(); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Services/AuthorityRoleManager.cs b/DotBased.AspNet.Authority/Services/AuthorityRoleManager.cs new file mode 100644 index 0000000..d3b8e7d --- /dev/null +++ b/DotBased.AspNet.Authority/Services/AuthorityRoleManager.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Services; + +public class AuthorityRoleManager +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Services/AuthorityUserManager.cs b/DotBased.AspNet.Authority/Services/AuthorityUserManager.cs new file mode 100644 index 0000000..a02c8f3 --- /dev/null +++ b/DotBased.AspNet.Authority/Services/AuthorityUserManager.cs @@ -0,0 +1,28 @@ +using DotBased.AspNet.Authority.Validators; +using DotBased.Logging; + +namespace DotBased.AspNet.Authority.Services; + +public class AuthorityUserManager +{ + public AuthorityUserManager( + AuthorityManager manager, + IEnumerable>? passwordValidators, + IEnumerable>? userValidators) + { + _logger = LogService.RegisterLogger>(); + AuthorityManager = manager; + if (passwordValidators != null) + PasswordValidators = passwordValidators; + if (userValidators != null) + UserValidators = userValidators; + } + + private readonly ILogger _logger; + public AuthorityManager AuthorityManager { get; } + + public IEnumerable> PasswordValidators { get; } = []; + public IEnumerable> UserValidators { get; } = []; + + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs b/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs index 2fe5b5c..07590ae 100644 --- a/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs +++ b/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs @@ -1,6 +1,9 @@ +using DotBased.AspNet.Authority.Models.Validation; +using DotBased.AspNet.Authority.Services; + namespace DotBased.AspNet.Authority.Validators; public interface IPasswordValidator { - + public Task ValidatePasswordAsync(AuthorityUserManager userManager, string password); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/IUserValidator.cs b/DotBased.AspNet.Authority/Validators/IUserValidator.cs index cb7e245..96511d6 100644 --- a/DotBased.AspNet.Authority/Validators/IUserValidator.cs +++ b/DotBased.AspNet.Authority/Validators/IUserValidator.cs @@ -1,6 +1,6 @@ namespace DotBased.AspNet.Authority.Validators; -public interface IUserValidator +public interface IUserValidator { } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs b/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs new file mode 100644 index 0000000..1360d3e --- /dev/null +++ b/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs @@ -0,0 +1,66 @@ +using DotBased.AspNet.Authority.Models.Validation; +using DotBased.AspNet.Authority.Services; +using DotBased.Extensions; + +namespace DotBased.AspNet.Authority.Validators; + +/// +/// Validates the password against the options that is configured. +/// +/// The user model used. +public class PasswordOptionsValidator : IPasswordValidator +{ + private const string ValidatorId = "Authority.Validator.Password.Options"; + private const string ValidationBase = "Authority.Validation.Password"; + + public async Task ValidatePasswordAsync(AuthorityUserManager userManager, string password) + { + if (userManager == null) + { + throw new ArgumentNullException(nameof(userManager), "User manager is not provided!"); + } + var passwordOptions = userManager.AuthorityManager.Options.Password; + var errors = new List(); + + if (password.IsNullOrEmpty() || password.Length < passwordOptions.RequiredLength) + { + errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.Required.Length", $"Password needs to have a minimum length of {passwordOptions.RequiredLength}")); + } + + if (passwordOptions.RequireDigit && !ContainsDigit(password)) + { + errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.Required.Digit", "Password must contain a digit!")); + } + + if (passwordOptions.RequireNonAlphanumeric && ContainsNonAlphanumeric(password)) + { + errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.Required.NonAlphanumeric", "Password must contain a non alphanumeric character.")); + } + + if (passwordOptions.RequireLowercase && password.Any(char.IsLower)) + { + errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.Required.Lowercase", "Password must contains at least one lowercase character.")); + } + + if (passwordOptions.RequireUppercase && password.Any(char.IsUpper)) + { + errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.Required.Uppercase", "Password must contains at least one uppercase character.")); + } + + if (passwordOptions.PasswordBlackList.Count != 0 && passwordOptions.PasswordBlackList.Contains(password, passwordOptions.PasswordBlackListComparer)) + { + errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.Blacklisted", "Given password is not allowed (blacklisted)")); + } + + if (passwordOptions.MinimalUniqueChars > 0 && password.Distinct().Count() < passwordOptions.MinimalUniqueChars) + { + errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.UniqueChars", $"Password must contain at least {passwordOptions.MinimalUniqueChars} unique chars.")); + } + + return await Task.FromResult(errors.Count > 0 ? ValidationResult.Failed(errors) : ValidationResult.Ok()); + } + + private bool ContainsDigit(string strVal) => strVal.Any(char.IsDigit); + + private bool ContainsNonAlphanumeric(string strVal) => !strVal.Any(char.IsLetterOrDigit); +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/PasswordValidator.cs b/DotBased.AspNet.Authority/Validators/PasswordValidator.cs deleted file mode 100644 index 33ce063..0000000 --- a/DotBased.AspNet.Authority/Validators/PasswordValidator.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace DotBased.AspNet.Authority.Validators; - -public class PasswordValidator -{ - -} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/UserValidator.cs b/DotBased.AspNet.Authority/Validators/UserValidator.cs index 1175fc8..219bccb 100644 --- a/DotBased.AspNet.Authority/Validators/UserValidator.cs +++ b/DotBased.AspNet.Authority/Validators/UserValidator.cs @@ -1,6 +1,6 @@ namespace DotBased.AspNet.Authority.Validators; -public class UserValidator +public class UserValidator : IUserValidator { } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Verifiers/IUserVerifier.cs b/DotBased.AspNet.Authority/Verifiers/IUserVerifier.cs index c41347f..d6e5120 100644 --- a/DotBased.AspNet.Authority/Verifiers/IUserVerifier.cs +++ b/DotBased.AspNet.Authority/Verifiers/IUserVerifier.cs @@ -1,6 +1,6 @@ namespace DotBased.AspNet.Authority.Verifiers; -public class IUserVerifier +public interface IUserVerifier { } \ No newline at end of file diff --git a/TestWebApi/Program.cs b/TestWebApi/Program.cs index f18c0ae..323ca0c 100644 --- a/TestWebApi/Program.cs +++ b/TestWebApi/Program.cs @@ -1,3 +1,4 @@ +using DotBased.AspNet.Authority; using DotBased.Logging; using DotBased.Logging.MEL; using DotBased.Logging.Serilog; @@ -19,6 +20,11 @@ LogService.AddLogAdapter(new BasedSerilogAdapter(serilogLogger)); builder.Logging.ClearProviders(); builder.Logging.AddDotBasedLoggerProvider(LogService.Options); +builder.Services.AddAuthority(options => +{ + +}); + /*builder.Services.AddAuthentication(options => { options.DefaultScheme = BasedAuthenticationDefaults.BasedAuthenticationScheme; From 172d5838e74152ceef7dcfb1cae720f831bba6c1 Mon Sep 17 00:00:00 2001 From: max Date: Thu, 26 Dec 2024 20:01:57 +0100 Subject: [PATCH 08/24] [ADD] Pwd validator, reworked classes --- .../AuthorityProviderExtensions.cs | 18 ++++++++++++ .../Models/Authority/AuthorityUser.cs | 7 +++-- .../Repositories/IUserRepository.cs | 6 ++-- .../Services/AuthorityManager.cs | 1 + .../Services/AuthorityUserManager.cs | 28 +++++++++++++++++-- .../Validators/IPasswordValidator.cs | 4 +-- .../Validators/PasswordEqualsValidator.cs | 27 ++++++++++++++++++ .../Validators/PasswordOptionsValidator.cs | 4 +-- 8 files changed, 83 insertions(+), 12 deletions(-) create mode 100644 DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs diff --git a/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs index b99a7ec..7789c7b 100644 --- a/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs +++ b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs @@ -21,9 +21,11 @@ public static class AuthorityProviderExtensions services.AddOptions(); services.Configure(optionsAction); } + services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped, PasswordOptionsValidator>(); + services.TryAddScoped, PasswordEqualsValidator>(); services.TryAddScoped, UserValidator>(); /*services.TryAddScoped(); services.TryAddScoped(); @@ -44,4 +46,20 @@ public static class AuthorityProviderExtensions { return builder; } + + private static Type GetBaseGenericArgumentType(Type baseType) + { + var userGenericBaseTypeDefinition = typeof(TModel).BaseType?.GetGenericTypeDefinition(); + if (userGenericBaseTypeDefinition != null && userGenericBaseTypeDefinition == baseType) + { + var userBaseGenericArguments = userGenericBaseTypeDefinition.GetGenericArguments(); + if (userBaseGenericArguments.Length <= 0) + { + throw new ArgumentException("Base implementation does not have the required generic argument.", nameof(TModel)); + } + + return userBaseGenericArguments[0]; + } + throw new ArgumentException($"Given object {typeof(TModel).Name} does not have the base implementation type of: {baseType.Name}", nameof(TModel)); + } } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs index bbc919f..7f7f87a 100644 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs @@ -16,10 +16,13 @@ public class AuthorityUser : AuthorityUser } } -public abstract class AuthorityUser where TKey : IEquatable +public abstract class AuthorityUser : AuthorityUserBase where TKey : IEquatable { public TKey Id { get; set; } - +} + +public abstract class AuthorityUserBase +{ public bool Enabled { get; set; } public bool Confirmed { get; set; } diff --git a/DotBased.AspNet.Authority/Repositories/IUserRepository.cs b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs index 23c4c6e..0aa9893 100644 --- a/DotBased.AspNet.Authority/Repositories/IUserRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs @@ -1,9 +1,9 @@ namespace DotBased.AspNet.Authority.Repositories; -public interface IUserRepository where TUser : class where TId : IEquatable +public interface IUserRepository where TUser : class { - public Task GetUserByIdAsync(TId id); - public Task GetUserIdAsync(TUser user); + public Task GetUserByIdAsync(string id); + public Task GetUserIdAsync(TUser user); public Task SetVersion(TUser user, long version); public Task SetSecurityVersion(TUser user, long version); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Services/AuthorityManager.cs b/DotBased.AspNet.Authority/Services/AuthorityManager.cs index 2cf9d19..d5e607a 100644 --- a/DotBased.AspNet.Authority/Services/AuthorityManager.cs +++ b/DotBased.AspNet.Authority/Services/AuthorityManager.cs @@ -32,6 +32,7 @@ public class AuthorityManager public long GenerateVersion() => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + /// /// Protect or unprotect the properties with the diff --git a/DotBased.AspNet.Authority/Services/AuthorityUserManager.cs b/DotBased.AspNet.Authority/Services/AuthorityUserManager.cs index a02c8f3..a874760 100644 --- a/DotBased.AspNet.Authority/Services/AuthorityUserManager.cs +++ b/DotBased.AspNet.Authority/Services/AuthorityUserManager.cs @@ -1,17 +1,24 @@ +using DotBased.AspNet.Authority.Crypto; +using DotBased.AspNet.Authority.Models.Validation; +using DotBased.AspNet.Authority.Repositories; using DotBased.AspNet.Authority.Validators; using DotBased.Logging; namespace DotBased.AspNet.Authority.Services; -public class AuthorityUserManager +public class AuthorityUserManager where TUser : class { public AuthorityUserManager( AuthorityManager manager, + IUserRepository userRepository, + IPasswordHasher passwordHasher, IEnumerable>? passwordValidators, IEnumerable>? userValidators) { _logger = LogService.RegisterLogger>(); AuthorityManager = manager; + UserRepository = userRepository; + PasswordHasher = passwordHasher; if (passwordValidators != null) PasswordValidators = passwordValidators; if (userValidators != null) @@ -20,9 +27,24 @@ public class AuthorityUserManager private readonly ILogger _logger; public AuthorityManager AuthorityManager { get; } + public IUserRepository UserRepository { get; } + + public IPasswordHasher PasswordHasher { get; } public IEnumerable> PasswordValidators { get; } = []; public IEnumerable> UserValidators { get; } = []; - - + + public async Task ValidatePasswordAsync(TUser user, string password) + { + List errors = []; + foreach (var validator in PasswordValidators) + { + var validatorResult = await validator.ValidatePasswordAsync(this, user, password); + if (!validatorResult.Success) + { + errors.AddRange(validatorResult.Errors); + } + } + return errors.Count > 0 ? ValidationResult.Failed(errors) : ValidationResult.Ok(); + } } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs b/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs index 07590ae..035038d 100644 --- a/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs +++ b/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs @@ -3,7 +3,7 @@ using DotBased.AspNet.Authority.Services; namespace DotBased.AspNet.Authority.Validators; -public interface IPasswordValidator +public interface IPasswordValidator where TUser : class { - public Task ValidatePasswordAsync(AuthorityUserManager userManager, string password); + public Task ValidatePasswordAsync(AuthorityUserManager userManager, TUser user, string password); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs b/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs new file mode 100644 index 0000000..1d5071a --- /dev/null +++ b/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs @@ -0,0 +1,27 @@ +using DotBased.AspNet.Authority.Models.Authority; +using DotBased.AspNet.Authority.Models.Validation; +using DotBased.AspNet.Authority.Services; + +namespace DotBased.AspNet.Authority.Validators; + +public class PasswordEqualsValidator : IPasswordValidator where TUser : class +{ + private const string ValidatorId = "Authority.Validator.Password.Equals"; + private const string ValidationBase = "Authority.Validation.Password"; + public async Task ValidatePasswordAsync(AuthorityUserManager userManager, TUser user, string password) + { + if (user == null || user is not AuthorityUserBase authorityUser) + { + throw new ArgumentException("Invalid user given!", nameof(user)); + } + + List errors = []; + var hashedPassword = await userManager.PasswordHasher.HashPasswordAsync(password); + if (authorityUser.PasswordHash != null && authorityUser.PasswordHash.Equals(hashedPassword, StringComparison.Ordinal)) + { + errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.InUse", "User uses this password already!")); + } + + return errors.Count > 0 ? ValidationResult.Failed(errors) : ValidationResult.Ok(); + } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs b/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs index 1360d3e..6bfcad0 100644 --- a/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs +++ b/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs @@ -8,12 +8,12 @@ namespace DotBased.AspNet.Authority.Validators; /// Validates the password against the options that is configured. /// /// The user model used. -public class PasswordOptionsValidator : IPasswordValidator +public class PasswordOptionsValidator : IPasswordValidator where TUser : class { private const string ValidatorId = "Authority.Validator.Password.Options"; private const string ValidationBase = "Authority.Validation.Password"; - public async Task ValidatePasswordAsync(AuthorityUserManager userManager, string password) + public async Task ValidatePasswordAsync(AuthorityUserManager userManager, TUser user, string password) { if (userManager == null) { From 2d96a259064f54228d8c7ab4ac176bf0c5209e31 Mon Sep 17 00:00:00 2001 From: max Date: Mon, 30 Dec 2024 15:40:52 +0100 Subject: [PATCH 09/24] [CHANGE] Repository manager --- .../Repositories/IAuthorityRepository.cs | 4 ++-- .../Repositories/IGroupRepository.cs | 2 +- .../Repositories/RepositoryManager.cs | 13 +++++++++++++ .../Services/AuthorityManager.cs | 6 +++--- 4 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 DotBased.AspNet.Authority/Repositories/RepositoryManager.cs diff --git a/DotBased.AspNet.Authority/Repositories/IAuthorityRepository.cs b/DotBased.AspNet.Authority/Repositories/IAuthorityRepository.cs index 8c0af84..da76f55 100644 --- a/DotBased.AspNet.Authority/Repositories/IAuthorityRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IAuthorityRepository.cs @@ -2,6 +2,6 @@ namespace DotBased.AspNet.Authority.Repositories; public interface IAuthorityRepository { - public Task GetVersion(); - public Task SetVersion(int version); + public Task GetVersion(); + public Task SetVersion(long version); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs b/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs index 667f839..e3c6369 100644 --- a/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs @@ -1,6 +1,6 @@ namespace DotBased.AspNet.Authority.Repositories; -public interface IGroupRepository where TGroup : class where TId : IEquatable +public interface IGroupRepository where TGroup : class { } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/RepositoryManager.cs b/DotBased.AspNet.Authority/Repositories/RepositoryManager.cs new file mode 100644 index 0000000..952a7b2 --- /dev/null +++ b/DotBased.AspNet.Authority/Repositories/RepositoryManager.cs @@ -0,0 +1,13 @@ +namespace DotBased.AspNet.Authority.Repositories; + +public class RepositoryManager where TUser : class where TGroup : class +{ + public RepositoryManager(IUserRepository userRepository, IGroupRepository groupRepository) + { + UserRepository = userRepository; + GroupRepository = groupRepository; + } + + public IUserRepository UserRepository { get; set; } + public IGroupRepository GroupRepository { get; set; } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Services/AuthorityManager.cs b/DotBased.AspNet.Authority/Services/AuthorityManager.cs index d5e607a..c313772 100644 --- a/DotBased.AspNet.Authority/Services/AuthorityManager.cs +++ b/DotBased.AspNet.Authority/Services/AuthorityManager.cs @@ -83,11 +83,11 @@ public class AuthorityManager _logger.Debug("{HandledPropCount}/{TotalPropCount} protection properties handled!", handledProperties, props.Count); } - public bool IsPropertieProtected(string propertieName) + public bool IsPropertyProtected(string propertyName) { var protectedProperties = GetProtectedProperties(); - var propertieFound = protectedProperties.Where(propInfo => propInfo.Name == propertieName); - return propertieFound.Any(); + var propertyFound = protectedProperties.Where(propInfo => propInfo.Name == propertyName); + return propertyFound.Any(); } public List GetProtectedPropertiesValues(TModel model) From efc82599304e43ac44b3cb781c90ff1771af9f5b Mon Sep 17 00:00:00 2001 From: max Date: Fri, 3 Jan 2025 00:14:12 +0100 Subject: [PATCH 10/24] [CHANGE] Implementing managers. repositories --- .../AuthorityProviderExtensions.cs | 2 +- .../AuthorityGroupManager.cs | 2 +- .../AuthorityManager.cs | 10 +- .../AuthorityRoleManager.cs | 2 +- .../Managers/AuthorityUserManager.cs | 97 +++++++++++++++++++ .../Models/AuthorityResult.cs | 38 ++++++++ .../Models/Options/ListOption.cs | 7 ++ .../Models/Options/UserOptions.cs | 3 +- .../Repositories/IAuthorityRepository.cs | 7 -- .../Repositories/IUserRepository.cs | 10 +- .../Repositories/RepositoryManager.cs | 13 --- .../Services/AuthorityUserManager.cs | 50 ---------- .../Validators/IPasswordValidator.cs | 2 +- .../Validators/IUserValidator.cs | 7 +- .../Validators/PasswordEqualsValidator.cs | 6 +- .../Validators/PasswordOptionsValidator.cs | 2 +- .../Validators/UserValidator.cs | 86 +++++++++++++++- 17 files changed, 252 insertions(+), 92 deletions(-) rename DotBased.AspNet.Authority/{Services => Managers}/AuthorityGroupManager.cs (52%) rename DotBased.AspNet.Authority/{Services => Managers}/AuthorityManager.cs (89%) rename DotBased.AspNet.Authority/{Services => Managers}/AuthorityRoleManager.cs (51%) create mode 100644 DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs create mode 100644 DotBased.AspNet.Authority/Models/AuthorityResult.cs create mode 100644 DotBased.AspNet.Authority/Models/Options/ListOption.cs delete mode 100644 DotBased.AspNet.Authority/Repositories/IAuthorityRepository.cs delete mode 100644 DotBased.AspNet.Authority/Repositories/RepositoryManager.cs delete mode 100644 DotBased.AspNet.Authority/Services/AuthorityUserManager.cs diff --git a/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs index 7789c7b..ab2b8fc 100644 --- a/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs +++ b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs @@ -1,7 +1,7 @@ using DotBased.AspNet.Authority.Crypto; +using DotBased.AspNet.Authority.Managers; using DotBased.AspNet.Authority.Models.Authority; using DotBased.AspNet.Authority.Models.Options; -using DotBased.AspNet.Authority.Services; using DotBased.AspNet.Authority.Validators; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; diff --git a/DotBased.AspNet.Authority/Services/AuthorityGroupManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs similarity index 52% rename from DotBased.AspNet.Authority/Services/AuthorityGroupManager.cs rename to DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs index b8db5ea..a4dffbe 100644 --- a/DotBased.AspNet.Authority/Services/AuthorityGroupManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs @@ -1,4 +1,4 @@ -namespace DotBased.AspNet.Authority.Services; +namespace DotBased.AspNet.Authority.Managers; public class AuthorityGroupManager { diff --git a/DotBased.AspNet.Authority/Services/AuthorityManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityManager.cs similarity index 89% rename from DotBased.AspNet.Authority/Services/AuthorityManager.cs rename to DotBased.AspNet.Authority/Managers/AuthorityManager.cs index c313772..b80e1e3 100644 --- a/DotBased.AspNet.Authority/Services/AuthorityManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityManager.cs @@ -2,24 +2,21 @@ using System.Reflection; using DotBased.AspNet.Authority.Attributes; using DotBased.AspNet.Authority.Crypto; using DotBased.AspNet.Authority.Models.Options; -using DotBased.AspNet.Authority.Repositories; using DotBased.Logging; using Microsoft.Extensions.Options; -namespace DotBased.AspNet.Authority.Services; +namespace DotBased.AspNet.Authority.Managers; public class AuthorityManager { public AuthorityManager( IOptions options, IServiceProvider services, - IAuthorityRepository repository, ICryptographer cryptographer) { _logger = LogService.RegisterLogger(); Options = options.Value ?? new AuthorityOptions(); Services = services; - Repository = repository; Cryptographer = cryptographer; } @@ -27,7 +24,6 @@ public class AuthorityManager public IServiceProvider Services { get; } public AuthorityOptions Options { get; } - public IAuthorityRepository Repository { get; } public ICryptographer Cryptographer { get; } @@ -38,7 +34,7 @@ public class AuthorityManager /// Protect or unprotect the properties with the /// /// The data model - /// True for protection false for unprotection. + /// True for protect false for unprotect. /// The class with the properties to protect. public async Task HandlePropertyProtection(TModel data, bool protection) { @@ -74,7 +70,7 @@ public class AuthorityManager if (cryptString == null) { - _logger.Warning("{Protection} failed for property {PropName}", protection ? "Encyption" : "Decyption", property.Name); + _logger.Warning("{Protection} failed for property {PropName}", protection ? "Encryption" : "Decryption", property.Name); continue; } property.SetValue(data, cryptString); diff --git a/DotBased.AspNet.Authority/Services/AuthorityRoleManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs similarity index 51% rename from DotBased.AspNet.Authority/Services/AuthorityRoleManager.cs rename to DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs index d3b8e7d..c41b6d2 100644 --- a/DotBased.AspNet.Authority/Services/AuthorityRoleManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs @@ -1,4 +1,4 @@ -namespace DotBased.AspNet.Authority.Services; +namespace DotBased.AspNet.Authority.Managers; public class AuthorityRoleManager { diff --git a/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs new file mode 100644 index 0000000..b5a9003 --- /dev/null +++ b/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs @@ -0,0 +1,97 @@ +using DotBased.AspNet.Authority.Crypto; +using DotBased.AspNet.Authority.Models; +using DotBased.AspNet.Authority.Models.Authority; +using DotBased.AspNet.Authority.Models.Validation; +using DotBased.AspNet.Authority.Repositories; +using DotBased.AspNet.Authority.Validators; +using DotBased.Logging; + +namespace DotBased.AspNet.Authority.Managers; + +public class AuthorityUserManager where TUser : class +{ + public AuthorityUserManager( + AuthorityManager manager, + IUserRepository userRepository, + IPasswordHasher passwordHasher, + IEnumerable>? passwordValidators, + IEnumerable>? userValidators) + { + _logger = LogService.RegisterLogger>(); + AuthorityManager = manager; + UserRepository = userRepository; + PasswordHasher = passwordHasher; + if (passwordValidators != null) + PasswordValidators = passwordValidators; + if (userValidators != null) + UserValidators = userValidators; + } + + private readonly ILogger _logger; + public AuthorityManager AuthorityManager { get; } + public IUserRepository UserRepository { get; } + + public IPasswordHasher PasswordHasher { get; } + + public IEnumerable> PasswordValidators { get; } = []; + public IEnumerable> UserValidators { get; } = []; + + public async Task ValidatePasswordAsync(TUser user, string password) + { + List errors = []; + foreach (var validator in PasswordValidators) + { + var validatorResult = await validator.ValidatePasswordAsync(this, user, password); + if (!validatorResult.Success) + { + errors.AddRange(validatorResult.Errors); + } + } + return errors.Count > 0 ? ValidationResult.Failed(errors) : ValidationResult.Ok(); + } + + public async Task ValidateUserAsync(TUser user) + { + List errors = []; + foreach (var userValidator in UserValidators) + { + var validationResult = await userValidator.ValidateUserAsync(this, user); + if (!validationResult.Success) + { + errors.AddRange(validationResult.Errors); + } + } + return errors.Count > 0 ? ValidationResult.Failed(errors) : ValidationResult.Ok(); + } + + public async Task> CreateUserAsync(TUser userModel, string password) + { + if (userModel is not AuthorityUserBase userBase) + { + return AuthorityResult.Error($"Given user is not of base type {nameof(AuthorityUserBase)}!"); + } + + var userValidation = await ValidateUserAsync(userModel); + var passwordValidation = await ValidatePasswordAsync(userModel, password); + if (!userValidation.Success || !passwordValidation.Success) + { + List errors = []; + errors.AddRange(userValidation.Errors); + errors.AddRange(passwordValidation.Errors); + return AuthorityResult.Failed(errors, ResultFailReason.Validation); + } + + var version = AuthorityManager.GenerateVersion(); + userBase.Version = version; + var securityVersion = AuthorityManager.GenerateVersion(); + userBase.SecurityVersion = securityVersion; + var hashedPassword = await PasswordHasher.HashPasswordAsync(password); + userBase.PasswordHash = hashedPassword; + + var userCreationResult = await UserRepository.CreateUserAsync(userModel); + + return userCreationResult != null + ? AuthorityResult.Ok(userCreationResult) + : AuthorityResult.Error("Failed to create user in repository!"); + } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/AuthorityResult.cs b/DotBased.AspNet.Authority/Models/AuthorityResult.cs new file mode 100644 index 0000000..f6c6d35 --- /dev/null +++ b/DotBased.AspNet.Authority/Models/AuthorityResult.cs @@ -0,0 +1,38 @@ +using DotBased.AspNet.Authority.Models.Validation; + +namespace DotBased.AspNet.Authority.Models; + +public class AuthorityResult +{ + public AuthorityResult(bool success, string errorMessage = "", TResultValue? value = default, ResultFailReason reason = ResultFailReason.None, List? errors = null) + { + Success = success; + ErrorMessage = errorMessage; + Value = value; + Reason = reason; + ValidationErrors = errors; + } + + public bool Success { get; } + public string ErrorMessage { get; } + public TResultValue? Value { get; } + public ResultFailReason Reason { get; } + public List? ValidationErrors { get; } + + + public static AuthorityResult Ok(TResultValue? value) => new AuthorityResult(true, value:value); + + public static AuthorityResult Error(string errorMessage, ResultFailReason reason = ResultFailReason.Error) => + new AuthorityResult(false, errorMessage, reason:reason); + + public static AuthorityResult Failed(List errors, ResultFailReason reason = ResultFailReason.None) + => new AuthorityResult(false, errors:errors, reason:reason); +} + +public enum ResultFailReason +{ + None, + Unknown, + Validation, + Error +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Options/ListOption.cs b/DotBased.AspNet.Authority/Models/Options/ListOption.cs new file mode 100644 index 0000000..3c43bf2 --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Options/ListOption.cs @@ -0,0 +1,7 @@ +namespace DotBased.AspNet.Authority.Models.Options; + +public enum ListOption +{ + Blacklist, + Whitelist +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Options/UserOptions.cs b/DotBased.AspNet.Authority/Models/Options/UserOptions.cs index 54011e7..c483bab 100644 --- a/DotBased.AspNet.Authority/Models/Options/UserOptions.cs +++ b/DotBased.AspNet.Authority/Models/Options/UserOptions.cs @@ -4,7 +4,8 @@ public class UserOptions { public bool EnableRegister { get; set; } public bool RequireUniqueEmail { get; set; } - public string AllowedCharacters { get; set; } = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@"; + public string UserNameCharacters { get; set; } = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@"; + public ListOption UserNameCharacterListType { get; set; } = ListOption.Whitelist; public List UserNameBlackList { get; set; } = ["admin", "administrator", "dev", "developer"]; public StringComparer UserNameBlackListComparer { get; set; } = StringComparer.OrdinalIgnoreCase; diff --git a/DotBased.AspNet.Authority/Repositories/IAuthorityRepository.cs b/DotBased.AspNet.Authority/Repositories/IAuthorityRepository.cs deleted file mode 100644 index da76f55..0000000 --- a/DotBased.AspNet.Authority/Repositories/IAuthorityRepository.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace DotBased.AspNet.Authority.Repositories; - -public interface IAuthorityRepository -{ - public Task GetVersion(); - public Task SetVersion(long version); -} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/IUserRepository.cs b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs index 0aa9893..8385b26 100644 --- a/DotBased.AspNet.Authority/Repositories/IUserRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs @@ -4,6 +4,12 @@ public interface IUserRepository where TUser : class { public Task GetUserByIdAsync(string id); public Task GetUserIdAsync(TUser user); - public Task SetVersion(TUser user, long version); - public Task SetSecurityVersion(TUser user, long version); + public Task GetUserByEmailAsync(string email); + public Task SetVersionAsync(TUser user, long version); + public Task GetVersionAsync(TUser user); + public Task SetSecurityVersionAsync(TUser user, long version); + public Task GetSecurityVersionAsync(TUser user); + public Task CreateUserAsync(TUser user); + public Task UpdateUserAsync(TUser user); + public Task DeleteUserAsync(TUser user); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/RepositoryManager.cs b/DotBased.AspNet.Authority/Repositories/RepositoryManager.cs deleted file mode 100644 index 952a7b2..0000000 --- a/DotBased.AspNet.Authority/Repositories/RepositoryManager.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace DotBased.AspNet.Authority.Repositories; - -public class RepositoryManager where TUser : class where TGroup : class -{ - public RepositoryManager(IUserRepository userRepository, IGroupRepository groupRepository) - { - UserRepository = userRepository; - GroupRepository = groupRepository; - } - - public IUserRepository UserRepository { get; set; } - public IGroupRepository GroupRepository { get; set; } -} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Services/AuthorityUserManager.cs b/DotBased.AspNet.Authority/Services/AuthorityUserManager.cs deleted file mode 100644 index a874760..0000000 --- a/DotBased.AspNet.Authority/Services/AuthorityUserManager.cs +++ /dev/null @@ -1,50 +0,0 @@ -using DotBased.AspNet.Authority.Crypto; -using DotBased.AspNet.Authority.Models.Validation; -using DotBased.AspNet.Authority.Repositories; -using DotBased.AspNet.Authority.Validators; -using DotBased.Logging; - -namespace DotBased.AspNet.Authority.Services; - -public class AuthorityUserManager where TUser : class -{ - public AuthorityUserManager( - AuthorityManager manager, - IUserRepository userRepository, - IPasswordHasher passwordHasher, - IEnumerable>? passwordValidators, - IEnumerable>? userValidators) - { - _logger = LogService.RegisterLogger>(); - AuthorityManager = manager; - UserRepository = userRepository; - PasswordHasher = passwordHasher; - if (passwordValidators != null) - PasswordValidators = passwordValidators; - if (userValidators != null) - UserValidators = userValidators; - } - - private readonly ILogger _logger; - public AuthorityManager AuthorityManager { get; } - public IUserRepository UserRepository { get; } - - public IPasswordHasher PasswordHasher { get; } - - public IEnumerable> PasswordValidators { get; } = []; - public IEnumerable> UserValidators { get; } = []; - - public async Task ValidatePasswordAsync(TUser user, string password) - { - List errors = []; - foreach (var validator in PasswordValidators) - { - var validatorResult = await validator.ValidatePasswordAsync(this, user, password); - if (!validatorResult.Success) - { - errors.AddRange(validatorResult.Errors); - } - } - return errors.Count > 0 ? ValidationResult.Failed(errors) : ValidationResult.Ok(); - } -} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs b/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs index 035038d..1f6304f 100644 --- a/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs +++ b/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs @@ -1,5 +1,5 @@ +using DotBased.AspNet.Authority.Managers; using DotBased.AspNet.Authority.Models.Validation; -using DotBased.AspNet.Authority.Services; namespace DotBased.AspNet.Authority.Validators; diff --git a/DotBased.AspNet.Authority/Validators/IUserValidator.cs b/DotBased.AspNet.Authority/Validators/IUserValidator.cs index 96511d6..a2b3f69 100644 --- a/DotBased.AspNet.Authority/Validators/IUserValidator.cs +++ b/DotBased.AspNet.Authority/Validators/IUserValidator.cs @@ -1,6 +1,9 @@ +using DotBased.AspNet.Authority.Managers; +using DotBased.AspNet.Authority.Models.Validation; + namespace DotBased.AspNet.Authority.Validators; -public interface IUserValidator +public interface IUserValidator where TUser : class { - + public Task ValidateUserAsync(AuthorityUserManager manager, TUser user); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs b/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs index 1d5071a..b91de4a 100644 --- a/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs +++ b/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs @@ -1,6 +1,6 @@ +using DotBased.AspNet.Authority.Managers; using DotBased.AspNet.Authority.Models.Authority; using DotBased.AspNet.Authority.Models.Validation; -using DotBased.AspNet.Authority.Services; namespace DotBased.AspNet.Authority.Validators; @@ -10,9 +10,9 @@ public class PasswordEqualsValidator : IPasswordValidator where TU private const string ValidationBase = "Authority.Validation.Password"; public async Task ValidatePasswordAsync(AuthorityUserManager userManager, TUser user, string password) { - if (user == null || user is not AuthorityUserBase authorityUser) + if (user is not AuthorityUserBase authorityUser) { - throw new ArgumentException("Invalid user given!", nameof(user)); + throw new ArgumentException($"User is not type of {nameof(AuthorityUserBase)}!", nameof(user)); } List errors = []; diff --git a/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs b/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs index 6bfcad0..65de9b2 100644 --- a/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs +++ b/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs @@ -1,5 +1,5 @@ +using DotBased.AspNet.Authority.Managers; using DotBased.AspNet.Authority.Models.Validation; -using DotBased.AspNet.Authority.Services; using DotBased.Extensions; namespace DotBased.AspNet.Authority.Validators; diff --git a/DotBased.AspNet.Authority/Validators/UserValidator.cs b/DotBased.AspNet.Authority/Validators/UserValidator.cs index 219bccb..e23183b 100644 --- a/DotBased.AspNet.Authority/Validators/UserValidator.cs +++ b/DotBased.AspNet.Authority/Validators/UserValidator.cs @@ -1,6 +1,88 @@ +using DotBased.AspNet.Authority.Managers; +using DotBased.AspNet.Authority.Models.Authority; +using DotBased.AspNet.Authority.Models.Options; +using DotBased.AspNet.Authority.Models.Validation; + namespace DotBased.AspNet.Authority.Validators; -public class UserValidator : IUserValidator +public class UserValidator : IUserValidator where TUser : class { - + private const string ValidatorId = "Authority.Validator.User"; + private const string ValidationBase = "Authority.Validation.User"; + + public async Task ValidateUserAsync(AuthorityUserManager manager, TUser user) + { + List errors = []; + + var userOptions = manager.AuthorityManager.Options.User; + + if (user is not AuthorityUserBase userBase) + { + errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.NotAuthorityUser", + $"Given user model is not an type of {nameof(AuthorityUserBase)}")); + return ValidationResult.Failed(errors); + } + + if (userOptions.RequireUniqueEmail) + { + if (string.IsNullOrWhiteSpace(userBase.EmailAddress)) + { + errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.NoEmail", + $"Option {nameof(UserOptions.RequireUniqueEmail)} is set to true but given user does not have an email address!")); + } + else + { + var userEmailResult = await manager.UserRepository.GetUserByEmailAsync(userBase.EmailAddress); + if (userEmailResult != null) + { + errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.EmailExists", + "Given email has already registered an account!")); + } + } + } + + if (!string.IsNullOrWhiteSpace(userBase.UserName)) + { + if (userOptions.UserNameBlackList.Count != 0 && userOptions.UserNameBlackList.Contains(userBase.UserName, userOptions.UserNameBlackListComparer)) + { + errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.Blacklisted", "Given username is not allowed (blacklisted)")); + } + + if (!string.IsNullOrWhiteSpace(userOptions.UserNameCharacters)) + { + List chars = []; + if (userOptions.UserNameCharacterListType == ListOption.Whitelist) + { + chars.AddRange(userBase.UserName.Where(userNameChar => !userOptions.UserNameCharacters.Contains(userNameChar))); + } + if (userOptions.UserNameCharacterListType == ListOption.Blacklist) + { + chars.AddRange(userBase.UserName.Where(userNameChar => userOptions.UserNameCharacters.Contains(userNameChar))); + } + + if (chars.Count <= 0) return errors.Count > 0 ? ValidationResult.Failed(errors) : ValidationResult.Ok(); + var errorCode = ""; + var description = ""; + switch (userOptions.UserNameCharacterListType) + { + case ListOption.Whitelist: + errorCode = "CharactersNotOnWhitelist"; + description = $"Found characters in username that were not on the whitelist! Chars: [{string.Join(',', chars)}]"; + break; + case ListOption.Blacklist: + errorCode = "CharactersOnBlacklist"; + description = $"Found characters in username that are on the blacklist! Chars: [{string.Join(',', chars)}]"; + break; + } + + errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.UserName.{errorCode}", description)); + } + } + else + { + errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.InvalidUserName", "No username given!")); + } + + return errors.Count > 0 ? ValidationResult.Failed(errors) : ValidationResult.Ok(); + } } \ No newline at end of file From 12efc92ac4605a8c68783f217d64c1f33bf08dd1 Mon Sep 17 00:00:00 2001 From: max Date: Sat, 4 Jan 2025 00:52:04 +0100 Subject: [PATCH 11/24] [CHANGE] Implementation updates --- .../Managers/AuthorityUserManager.cs | 48 +++++++++++++++++-- .../Repositories/IUserRepository.cs | 21 ++++---- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs index b5a9003..f72af18 100644 --- a/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs @@ -64,6 +64,34 @@ public class AuthorityUserManager where TUser : class return errors.Count > 0 ? ValidationResult.Failed(errors) : ValidationResult.Ok(); } + public async Task> SearchUsersAsync(string query, int maxResults = 20, int offset = 0) + { + var searchResult = await UserRepository.GetUsersAsync(query, maxResults, offset); + return searchResult.Item1 == null ? ListResult.Failed("No results!") : ListResult.Ok(searchResult.Item1, searchResult.Item2); + } + + public async Task> UpdatePasswordAsync(TUser user, string password) + { + if (user is not AuthorityUserBase userBase) + { + return AuthorityResult.Error($"Given user is not of base type {nameof(AuthorityUserBase)}!"); + } + + var passwordValidation = await ValidatePasswordAsync(user, password); + if (!passwordValidation.Success) + { + List errors = []; + errors.AddRange(passwordValidation.Errors); + return AuthorityResult.Failed(errors, ResultFailReason.Validation); + } + + userBase.PasswordHash = await PasswordHasher.HashPasswordAsync(password); + userBase.SecurityVersion = AuthorityManager.GenerateVersion(); + + var updateResult = await UserRepository.UpdateUserAsync(user); + return updateResult == null ? AuthorityResult.Error("Failed to save updates!") : AuthorityResult.Ok(updateResult); + } + public async Task> CreateUserAsync(TUser userModel, string password) { if (userModel is not AuthorityUserBase userBase) @@ -81,10 +109,8 @@ public class AuthorityUserManager where TUser : class return AuthorityResult.Failed(errors, ResultFailReason.Validation); } - var version = AuthorityManager.GenerateVersion(); - userBase.Version = version; - var securityVersion = AuthorityManager.GenerateVersion(); - userBase.SecurityVersion = securityVersion; + userBase.Version = AuthorityManager.GenerateVersion(); + userBase.SecurityVersion = AuthorityManager.GenerateVersion(); var hashedPassword = await PasswordHasher.HashPasswordAsync(password); userBase.PasswordHash = hashedPassword; @@ -94,4 +120,18 @@ public class AuthorityUserManager where TUser : class ? AuthorityResult.Ok(userCreationResult) : AuthorityResult.Error("Failed to create user in repository!"); } + + public async Task> UpdateUserAsync(TUser model) + { + var updateResult = await UserRepository.UpdateUserAsync(model); + return updateResult != null ? Result.Ok(updateResult) : Result.Failed("Failed to update user in repository!"); + } + + public async Task DeleteUserAsync(TUser model) + { + var deleteResult = await UserRepository.DeleteUserAsync(model); + return deleteResult; + } + + } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/IUserRepository.cs b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs index 8385b26..46e833a 100644 --- a/DotBased.AspNet.Authority/Repositories/IUserRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs @@ -2,14 +2,15 @@ namespace DotBased.AspNet.Authority.Repositories; public interface IUserRepository where TUser : class { - public Task GetUserByIdAsync(string id); - public Task GetUserIdAsync(TUser user); - public Task GetUserByEmailAsync(string email); - public Task SetVersionAsync(TUser user, long version); - public Task GetVersionAsync(TUser user); - public Task SetSecurityVersionAsync(TUser user, long version); - public Task GetSecurityVersionAsync(TUser user); - public Task CreateUserAsync(TUser user); - public Task UpdateUserAsync(TUser user); - public Task DeleteUserAsync(TUser user); + public Task GetUserByIdAsync(string id, CancellationToken? cancellationToken = null); + public Task GetUserIdAsync(TUser user, CancellationToken? cancellationToken = null); + public Task?, int>> GetUsersAsync(string query, int maxResults = 20, int offset = 0, CancellationToken? cancellationToken = null); + public Task GetUserByEmailAsync(string email, CancellationToken? cancellationToken = null); + public Task SetVersionAsync(TUser user, long version, CancellationToken? cancellationToken = null); + public Task GetVersionAsync(TUser user, CancellationToken? cancellationToken = null); + public Task SetSecurityVersionAsync(TUser user, long version, CancellationToken? cancellationToken = null); + public Task GetSecurityVersionAsync(TUser user, CancellationToken? cancellationToken = null); + public Task CreateUserAsync(TUser user, CancellationToken? cancellationToken = null); + public Task UpdateUserAsync(TUser user, CancellationToken? cancellationToken = null); + public Task DeleteUserAsync(TUser user, CancellationToken? cancellationToken = null); } \ No newline at end of file From 90cd0a2828cd5802077b305b2ad206abf06f7686 Mon Sep 17 00:00:00 2001 From: max Date: Wed, 8 Jan 2025 15:55:00 +0100 Subject: [PATCH 12/24] [CHANGE] Removed generics and using base classes --- .../AuthorityProviderExtensions.cs | 17 ++--- .../Managers/AuthorityGroupManager.cs | 2 +- .../Managers/AuthorityRoleManager.cs | 2 +- .../Managers/AuthorityUserManager.cs | 66 ++++++++----------- .../Models/Authority/AuthorityUser.cs | 14 ++-- .../Repositories/IAttributeRepository.cs | 2 +- .../Repositories/IGroupRepository.cs | 2 +- .../Repositories/IRoleRepository.cs | 2 +- .../Repositories/IUserRepository.cs | 26 ++++---- .../Validators/IPasswordValidator.cs | 5 +- .../Validators/IUserValidator.cs | 5 +- .../Validators/PasswordEqualsValidator.cs | 11 +--- .../Validators/PasswordOptionsValidator.cs | 5 +- .../Validators/UserValidator.cs | 23 +++---- 14 files changed, 77 insertions(+), 105 deletions(-) diff --git a/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs index ab2b8fc..88c02f2 100644 --- a/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs +++ b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs @@ -1,6 +1,5 @@ using DotBased.AspNet.Authority.Crypto; using DotBased.AspNet.Authority.Managers; -using DotBased.AspNet.Authority.Models.Authority; using DotBased.AspNet.Authority.Models.Options; using DotBased.AspNet.Authority.Validators; using Microsoft.Extensions.DependencyInjection; @@ -11,10 +10,6 @@ namespace DotBased.AspNet.Authority; public static class AuthorityProviderExtensions { public static AuthorityBuilder AddAuthority(this IServiceCollection services, Action? optionsAction = null) - => services.AddAuthority(optionsAction); - - public static AuthorityBuilder AddAuthority(this IServiceCollection services, Action? optionsAction = null) - where TUser : class where TGroup : class where TRole : class { if (optionsAction != null) { @@ -24,16 +19,16 @@ public static class AuthorityProviderExtensions services.TryAddScoped(); services.TryAddScoped(); - services.TryAddScoped, PasswordOptionsValidator>(); - services.TryAddScoped, PasswordEqualsValidator>(); - services.TryAddScoped, UserValidator>(); + services.TryAddScoped(); + services.TryAddScoped(); + services.TryAddScoped(); /*services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped();*/ services.TryAddScoped(); - services.TryAddScoped>(); - services.TryAddScoped>(); - services.TryAddScoped>(); + services.TryAddScoped(); + services.TryAddScoped(); + services.TryAddScoped(); return new AuthorityBuilder(services); } diff --git a/DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs index a4dffbe..7da7e73 100644 --- a/DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs @@ -1,6 +1,6 @@ namespace DotBased.AspNet.Authority.Managers; -public class AuthorityGroupManager +public class AuthorityGroupManager { } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs index c41b6d2..420b639 100644 --- a/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs @@ -1,6 +1,6 @@ namespace DotBased.AspNet.Authority.Managers; -public class AuthorityRoleManager +public class AuthorityRoleManager { } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs index f72af18..6cc0974 100644 --- a/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs @@ -8,16 +8,16 @@ using DotBased.Logging; namespace DotBased.AspNet.Authority.Managers; -public class AuthorityUserManager where TUser : class +public class AuthorityUserManager { public AuthorityUserManager( AuthorityManager manager, - IUserRepository userRepository, + IUserRepository userRepository, IPasswordHasher passwordHasher, - IEnumerable>? passwordValidators, - IEnumerable>? userValidators) + IEnumerable? passwordValidators, + IEnumerable? userValidators) { - _logger = LogService.RegisterLogger>(); + _logger = LogService.RegisterLogger(); AuthorityManager = manager; UserRepository = userRepository; PasswordHasher = passwordHasher; @@ -29,14 +29,14 @@ public class AuthorityUserManager where TUser : class private readonly ILogger _logger; public AuthorityManager AuthorityManager { get; } - public IUserRepository UserRepository { get; } + public IUserRepository UserRepository { get; } public IPasswordHasher PasswordHasher { get; } - public IEnumerable> PasswordValidators { get; } = []; - public IEnumerable> UserValidators { get; } = []; + public IEnumerable PasswordValidators { get; } = []; + public IEnumerable UserValidators { get; } = []; - public async Task ValidatePasswordAsync(TUser user, string password) + public async Task ValidatePasswordAsync(AuthorityUser user, string password) { List errors = []; foreach (var validator in PasswordValidators) @@ -50,7 +50,7 @@ public class AuthorityUserManager where TUser : class return errors.Count > 0 ? ValidationResult.Failed(errors) : ValidationResult.Ok(); } - public async Task ValidateUserAsync(TUser user) + public async Task ValidateUserAsync(AuthorityUser user) { List errors = []; foreach (var userValidator in UserValidators) @@ -64,41 +64,31 @@ public class AuthorityUserManager where TUser : class return errors.Count > 0 ? ValidationResult.Failed(errors) : ValidationResult.Ok(); } - public async Task> SearchUsersAsync(string query, int maxResults = 20, int offset = 0) + public async Task> SearchUsersAsync(string query, int maxResults = 20, int offset = 0) { - var searchResult = await UserRepository.GetUsersAsync(query, maxResults, offset); - return searchResult.Item1 == null ? ListResult.Failed("No results!") : ListResult.Ok(searchResult.Item1, searchResult.Item2); + var searchResult = await UserRepository.GetAuthorityUsersAsync(query, maxResults, offset); + return searchResult.Item1 == null ? ListResult.Failed("No results!") : ListResult.Ok(searchResult.Item1, searchResult.Item2); } - public async Task> UpdatePasswordAsync(TUser user, string password) + public async Task> UpdatePasswordAsync(AuthorityUser user, string password) { - if (user is not AuthorityUserBase userBase) - { - return AuthorityResult.Error($"Given user is not of base type {nameof(AuthorityUserBase)}!"); - } - var passwordValidation = await ValidatePasswordAsync(user, password); if (!passwordValidation.Success) { List errors = []; errors.AddRange(passwordValidation.Errors); - return AuthorityResult.Failed(errors, ResultFailReason.Validation); + return AuthorityResult.Failed(errors, ResultFailReason.Validation); } - userBase.PasswordHash = await PasswordHasher.HashPasswordAsync(password); - userBase.SecurityVersion = AuthorityManager.GenerateVersion(); + user.PasswordHash = await PasswordHasher.HashPasswordAsync(password); + user.SecurityVersion = AuthorityManager.GenerateVersion(); var updateResult = await UserRepository.UpdateUserAsync(user); - return updateResult == null ? AuthorityResult.Error("Failed to save updates!") : AuthorityResult.Ok(updateResult); + return updateResult == null ? AuthorityResult.Error("Failed to save updates!") : AuthorityResult.Ok(updateResult); } - public async Task> CreateUserAsync(TUser userModel, string password) + public async Task> CreateUserAsync(AuthorityUser userModel, string password) { - if (userModel is not AuthorityUserBase userBase) - { - return AuthorityResult.Error($"Given user is not of base type {nameof(AuthorityUserBase)}!"); - } - var userValidation = await ValidateUserAsync(userModel); var passwordValidation = await ValidatePasswordAsync(userModel, password); if (!userValidation.Success || !passwordValidation.Success) @@ -106,28 +96,28 @@ public class AuthorityUserManager where TUser : class List errors = []; errors.AddRange(userValidation.Errors); errors.AddRange(passwordValidation.Errors); - return AuthorityResult.Failed(errors, ResultFailReason.Validation); + return AuthorityResult.Failed(errors, ResultFailReason.Validation); } - userBase.Version = AuthorityManager.GenerateVersion(); - userBase.SecurityVersion = AuthorityManager.GenerateVersion(); + userModel.Version = AuthorityManager.GenerateVersion(); + userModel.SecurityVersion = AuthorityManager.GenerateVersion(); var hashedPassword = await PasswordHasher.HashPasswordAsync(password); - userBase.PasswordHash = hashedPassword; + userModel.PasswordHash = hashedPassword; var userCreationResult = await UserRepository.CreateUserAsync(userModel); return userCreationResult != null - ? AuthorityResult.Ok(userCreationResult) - : AuthorityResult.Error("Failed to create user in repository!"); + ? AuthorityResult.Ok(userCreationResult) + : AuthorityResult.Error("Failed to create user in repository!"); } - public async Task> UpdateUserAsync(TUser model) + public async Task> UpdateUserAsync(AuthorityUser model) { var updateResult = await UserRepository.UpdateUserAsync(model); - return updateResult != null ? Result.Ok(updateResult) : Result.Failed("Failed to update user in repository!"); + return updateResult != null ? Result.Ok(updateResult) : Result.Failed("Failed to update user in repository!"); } - public async Task DeleteUserAsync(TUser model) + public async Task DeleteUserAsync(AuthorityUser model) { var deleteResult = await UserRepository.DeleteUserAsync(model); return deleteResult; diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs index 7f7f87a..9f52b58 100644 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs @@ -2,7 +2,7 @@ using DotBased.AspNet.Authority.Attributes; namespace DotBased.AspNet.Authority.Models.Authority; -public class AuthorityUser : AuthorityUser +public class AuthorityUser { public AuthorityUser(string userName) : this() { @@ -14,15 +14,9 @@ public class AuthorityUser : AuthorityUser Id = Guid.NewGuid(); CreatedDate = DateTime.Now; } -} - -public abstract class AuthorityUser : AuthorityUserBase where TKey : IEquatable -{ - public TKey Id { get; set; } -} - -public abstract class AuthorityUserBase -{ + + public Guid Id { get; set; } + public bool Enabled { get; set; } public bool Confirmed { get; set; } diff --git a/DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs b/DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs index 92272f3..7e22f88 100644 --- a/DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs @@ -1,6 +1,6 @@ namespace DotBased.AspNet.Authority.Repositories; -public interface IAttributeRepository where TAttribute : class where TId : IEquatable +public interface IAttributeRepository { } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs b/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs index e3c6369..f16b7d2 100644 --- a/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs @@ -1,6 +1,6 @@ namespace DotBased.AspNet.Authority.Repositories; -public interface IGroupRepository where TGroup : class +public interface IGroupRepository { } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs b/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs index e3c9b4c..436076b 100644 --- a/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs @@ -1,6 +1,6 @@ namespace DotBased.AspNet.Authority.Repositories; -public interface IRoleRepository where TRole : class where TId : IEquatable +public interface IRoleRepository { } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/IUserRepository.cs b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs index 46e833a..f86cdb5 100644 --- a/DotBased.AspNet.Authority/Repositories/IUserRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs @@ -1,16 +1,18 @@ +using DotBased.AspNet.Authority.Models.Authority; + namespace DotBased.AspNet.Authority.Repositories; -public interface IUserRepository where TUser : class +public interface IUserRepository { - public Task GetUserByIdAsync(string id, CancellationToken? cancellationToken = null); - public Task GetUserIdAsync(TUser user, CancellationToken? cancellationToken = null); - public Task?, int>> GetUsersAsync(string query, int maxResults = 20, int offset = 0, CancellationToken? cancellationToken = null); - public Task GetUserByEmailAsync(string email, CancellationToken? cancellationToken = null); - public Task SetVersionAsync(TUser user, long version, CancellationToken? cancellationToken = null); - public Task GetVersionAsync(TUser user, CancellationToken? cancellationToken = null); - public Task SetSecurityVersionAsync(TUser user, long version, CancellationToken? cancellationToken = null); - public Task GetSecurityVersionAsync(TUser user, CancellationToken? cancellationToken = null); - public Task CreateUserAsync(TUser user, CancellationToken? cancellationToken = null); - public Task UpdateUserAsync(TUser user, CancellationToken? cancellationToken = null); - public Task DeleteUserAsync(TUser user, CancellationToken? cancellationToken = null); + public Task GetAuthorityUserByIdAsync(string id, CancellationToken? cancellationToken = null); + public Task GetAuthorityUserIdAsync(AuthorityUser user, CancellationToken? cancellationToken = null); + public Task?, int>> GetAuthorityUsersAsync(string query, int maxResults = 20, int offset = 0, CancellationToken? cancellationToken = null); + public Task GetAuthorityUserByEmailAsync(string email, CancellationToken? cancellationToken = null); + public Task SetVersionAsync(AuthorityUser user, long version, CancellationToken? cancellationToken = null); + public Task GetVersionAsync(AuthorityUser user, CancellationToken? cancellationToken = null); + public Task SetSecurityVersionAsync(AuthorityUser user, long version, CancellationToken? cancellationToken = null); + public Task GetSecurityVersionAsync(AuthorityUser user, CancellationToken? cancellationToken = null); + public Task CreateUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null); + public Task UpdateUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null); + public Task DeleteUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs b/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs index 1f6304f..ff7c770 100644 --- a/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs +++ b/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs @@ -1,9 +1,10 @@ using DotBased.AspNet.Authority.Managers; +using DotBased.AspNet.Authority.Models.Authority; using DotBased.AspNet.Authority.Models.Validation; namespace DotBased.AspNet.Authority.Validators; -public interface IPasswordValidator where TUser : class +public interface IPasswordValidator { - public Task ValidatePasswordAsync(AuthorityUserManager userManager, TUser user, string password); + public Task ValidatePasswordAsync(AuthorityUserManager userManager, AuthorityUser user, string password); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/IUserValidator.cs b/DotBased.AspNet.Authority/Validators/IUserValidator.cs index a2b3f69..da07405 100644 --- a/DotBased.AspNet.Authority/Validators/IUserValidator.cs +++ b/DotBased.AspNet.Authority/Validators/IUserValidator.cs @@ -1,9 +1,10 @@ using DotBased.AspNet.Authority.Managers; +using DotBased.AspNet.Authority.Models.Authority; using DotBased.AspNet.Authority.Models.Validation; namespace DotBased.AspNet.Authority.Validators; -public interface IUserValidator where TUser : class +public interface IUserValidator { - public Task ValidateUserAsync(AuthorityUserManager manager, TUser user); + public Task ValidateUserAsync(AuthorityUserManager manager, AuthorityUser user); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs b/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs index b91de4a..a95c55d 100644 --- a/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs +++ b/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs @@ -4,20 +4,15 @@ using DotBased.AspNet.Authority.Models.Validation; namespace DotBased.AspNet.Authority.Validators; -public class PasswordEqualsValidator : IPasswordValidator where TUser : class +public class PasswordEqualsValidator : IPasswordValidator { private const string ValidatorId = "Authority.Validator.Password.Equals"; private const string ValidationBase = "Authority.Validation.Password"; - public async Task ValidatePasswordAsync(AuthorityUserManager userManager, TUser user, string password) + public async Task ValidatePasswordAsync(AuthorityUserManager userManager, AuthorityUser user, string password) { - if (user is not AuthorityUserBase authorityUser) - { - throw new ArgumentException($"User is not type of {nameof(AuthorityUserBase)}!", nameof(user)); - } - List errors = []; var hashedPassword = await userManager.PasswordHasher.HashPasswordAsync(password); - if (authorityUser.PasswordHash != null && authorityUser.PasswordHash.Equals(hashedPassword, StringComparison.Ordinal)) + if (user.PasswordHash != null && user.PasswordHash.Equals(hashedPassword, StringComparison.Ordinal)) { errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.InUse", "User uses this password already!")); } diff --git a/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs b/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs index 65de9b2..bd38b25 100644 --- a/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs +++ b/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs @@ -1,4 +1,5 @@ using DotBased.AspNet.Authority.Managers; +using DotBased.AspNet.Authority.Models.Authority; using DotBased.AspNet.Authority.Models.Validation; using DotBased.Extensions; @@ -8,12 +9,12 @@ namespace DotBased.AspNet.Authority.Validators; /// Validates the password against the options that is configured. /// /// The user model used. -public class PasswordOptionsValidator : IPasswordValidator where TUser : class +public class PasswordOptionsValidator : IPasswordValidator { private const string ValidatorId = "Authority.Validator.Password.Options"; private const string ValidationBase = "Authority.Validation.Password"; - public async Task ValidatePasswordAsync(AuthorityUserManager userManager, TUser user, string password) + public async Task ValidatePasswordAsync(AuthorityUserManager userManager, AuthorityUser user, string password) { if (userManager == null) { diff --git a/DotBased.AspNet.Authority/Validators/UserValidator.cs b/DotBased.AspNet.Authority/Validators/UserValidator.cs index e23183b..317327f 100644 --- a/DotBased.AspNet.Authority/Validators/UserValidator.cs +++ b/DotBased.AspNet.Authority/Validators/UserValidator.cs @@ -5,34 +5,27 @@ using DotBased.AspNet.Authority.Models.Validation; namespace DotBased.AspNet.Authority.Validators; -public class UserValidator : IUserValidator where TUser : class +public class UserValidator : IUserValidator { private const string ValidatorId = "Authority.Validator.User"; private const string ValidationBase = "Authority.Validation.User"; - public async Task ValidateUserAsync(AuthorityUserManager manager, TUser user) + public async Task ValidateUserAsync(AuthorityUserManager manager, AuthorityUser user) { List errors = []; var userOptions = manager.AuthorityManager.Options.User; - if (user is not AuthorityUserBase userBase) - { - errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.NotAuthorityUser", - $"Given user model is not an type of {nameof(AuthorityUserBase)}")); - return ValidationResult.Failed(errors); - } - if (userOptions.RequireUniqueEmail) { - if (string.IsNullOrWhiteSpace(userBase.EmailAddress)) + if (string.IsNullOrWhiteSpace(user.EmailAddress)) { errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.NoEmail", $"Option {nameof(UserOptions.RequireUniqueEmail)} is set to true but given user does not have an email address!")); } else { - var userEmailResult = await manager.UserRepository.GetUserByEmailAsync(userBase.EmailAddress); + var userEmailResult = await manager.UserRepository.GetAuthorityUserByEmailAsync(user.EmailAddress); if (userEmailResult != null) { errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.EmailExists", @@ -41,9 +34,9 @@ public class UserValidator : IUserValidator where TUser : class } } - if (!string.IsNullOrWhiteSpace(userBase.UserName)) + if (!string.IsNullOrWhiteSpace(user.UserName)) { - if (userOptions.UserNameBlackList.Count != 0 && userOptions.UserNameBlackList.Contains(userBase.UserName, userOptions.UserNameBlackListComparer)) + if (userOptions.UserNameBlackList.Count != 0 && userOptions.UserNameBlackList.Contains(user.UserName, userOptions.UserNameBlackListComparer)) { errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.Blacklisted", "Given username is not allowed (blacklisted)")); } @@ -53,11 +46,11 @@ public class UserValidator : IUserValidator where TUser : class List chars = []; if (userOptions.UserNameCharacterListType == ListOption.Whitelist) { - chars.AddRange(userBase.UserName.Where(userNameChar => !userOptions.UserNameCharacters.Contains(userNameChar))); + chars.AddRange(user.UserName.Where(userNameChar => !userOptions.UserNameCharacters.Contains(userNameChar))); } if (userOptions.UserNameCharacterListType == ListOption.Blacklist) { - chars.AddRange(userBase.UserName.Where(userNameChar => userOptions.UserNameCharacters.Contains(userNameChar))); + chars.AddRange(user.UserName.Where(userNameChar => userOptions.UserNameCharacters.Contains(userNameChar))); } if (chars.Count <= 0) return errors.Count > 0 ? ValidationResult.Failed(errors) : ValidationResult.Ok(); From 28fcd74acf04ee20fbfcb84c24fd4ed2928645f4 Mon Sep 17 00:00:00 2001 From: max Date: Wed, 8 Jan 2025 16:06:25 +0100 Subject: [PATCH 13/24] [REFACTOR] Split manager class, refactored/cleaned classes --- .../AuthorityProviderExtensions.cs | 3 -- .../Managers/AuthorityGroupManager.cs | 2 +- .../Managers/AuthorityManager.cs | 26 ++++++++----- .../Managers/AuthorityRoleManager.cs | 2 +- .../Managers/AuthorityUserManager.cs | 38 ++----------------- .../Validators/IPasswordValidator.cs | 2 +- .../Validators/IUserValidator.cs | 2 +- .../Validators/PasswordEqualsValidator.cs | 2 +- .../Validators/PasswordOptionsValidator.cs | 5 +-- .../Validators/UserValidator.cs | 4 +- 10 files changed, 30 insertions(+), 56 deletions(-) diff --git a/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs index 88c02f2..9018d7a 100644 --- a/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs +++ b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs @@ -26,9 +26,6 @@ public static class AuthorityProviderExtensions services.TryAddScoped(); services.TryAddScoped();*/ services.TryAddScoped(); - services.TryAddScoped(); - services.TryAddScoped(); - services.TryAddScoped(); return new AuthorityBuilder(services); } diff --git a/DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs index 7da7e73..22e70cb 100644 --- a/DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs @@ -1,6 +1,6 @@ namespace DotBased.AspNet.Authority.Managers; -public class AuthorityGroupManager +public partial class AuthorityManager { } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Managers/AuthorityManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityManager.cs index b80e1e3..4b78e03 100644 --- a/DotBased.AspNet.Authority/Managers/AuthorityManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityManager.cs @@ -2,22 +2,28 @@ using System.Reflection; using DotBased.AspNet.Authority.Attributes; using DotBased.AspNet.Authority.Crypto; using DotBased.AspNet.Authority.Models.Options; +using DotBased.AspNet.Authority.Repositories; +using DotBased.AspNet.Authority.Validators; using DotBased.Logging; using Microsoft.Extensions.Options; namespace DotBased.AspNet.Authority.Managers; -public class AuthorityManager +public partial class AuthorityManager { public AuthorityManager( IOptions options, IServiceProvider services, - ICryptographer cryptographer) + ICryptographer cryptographer, + IUserRepository userRepository, + IPasswordHasher passwordHasher) { _logger = LogService.RegisterLogger(); - Options = options.Value ?? new AuthorityOptions(); + Options = options.Value; Services = services; Cryptographer = cryptographer; + UserRepository = userRepository; + PasswordHasher = passwordHasher; } private readonly ILogger _logger; @@ -25,6 +31,13 @@ public class AuthorityManager public IServiceProvider Services { get; } public AuthorityOptions Options { get; } public ICryptographer Cryptographer { get; } + + public IUserRepository UserRepository { get; } + + public IPasswordHasher PasswordHasher { get; } + + public IEnumerable PasswordValidators { get; } = []; + public IEnumerable UserValidators { get; } = []; public long GenerateVersion() => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); @@ -38,12 +51,7 @@ public class AuthorityManager /// The class with the properties to protect. public async Task HandlePropertyProtection(TModel data, bool protection) { - var props = GetProtectedPropertiesValues(data); - if (Cryptographer == null) - { - _logger.Warning("No cryptographer specified! Cannot encrypt/decrypt properties."); - return; - } + var props = GetProtectedPropertiesValues(data); if (props.Count == 0) { return; diff --git a/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs index 420b639..22e70cb 100644 --- a/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs @@ -1,6 +1,6 @@ namespace DotBased.AspNet.Authority.Managers; -public class AuthorityRoleManager +public partial class AuthorityManager { } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs index 6cc0974..94feb6f 100644 --- a/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs @@ -1,41 +1,11 @@ -using DotBased.AspNet.Authority.Crypto; using DotBased.AspNet.Authority.Models; using DotBased.AspNet.Authority.Models.Authority; using DotBased.AspNet.Authority.Models.Validation; -using DotBased.AspNet.Authority.Repositories; -using DotBased.AspNet.Authority.Validators; -using DotBased.Logging; namespace DotBased.AspNet.Authority.Managers; -public class AuthorityUserManager +public partial class AuthorityManager { - public AuthorityUserManager( - AuthorityManager manager, - IUserRepository userRepository, - IPasswordHasher passwordHasher, - IEnumerable? passwordValidators, - IEnumerable? userValidators) - { - _logger = LogService.RegisterLogger(); - AuthorityManager = manager; - UserRepository = userRepository; - PasswordHasher = passwordHasher; - if (passwordValidators != null) - PasswordValidators = passwordValidators; - if (userValidators != null) - UserValidators = userValidators; - } - - private readonly ILogger _logger; - public AuthorityManager AuthorityManager { get; } - public IUserRepository UserRepository { get; } - - public IPasswordHasher PasswordHasher { get; } - - public IEnumerable PasswordValidators { get; } = []; - public IEnumerable UserValidators { get; } = []; - public async Task ValidatePasswordAsync(AuthorityUser user, string password) { List errors = []; @@ -81,7 +51,7 @@ public class AuthorityUserManager } user.PasswordHash = await PasswordHasher.HashPasswordAsync(password); - user.SecurityVersion = AuthorityManager.GenerateVersion(); + user.SecurityVersion = GenerateVersion(); var updateResult = await UserRepository.UpdateUserAsync(user); return updateResult == null ? AuthorityResult.Error("Failed to save updates!") : AuthorityResult.Ok(updateResult); @@ -99,8 +69,8 @@ public class AuthorityUserManager return AuthorityResult.Failed(errors, ResultFailReason.Validation); } - userModel.Version = AuthorityManager.GenerateVersion(); - userModel.SecurityVersion = AuthorityManager.GenerateVersion(); + userModel.Version = GenerateVersion(); + userModel.SecurityVersion = GenerateVersion(); var hashedPassword = await PasswordHasher.HashPasswordAsync(password); userModel.PasswordHash = hashedPassword; diff --git a/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs b/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs index ff7c770..1208a21 100644 --- a/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs +++ b/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs @@ -6,5 +6,5 @@ namespace DotBased.AspNet.Authority.Validators; public interface IPasswordValidator { - public Task ValidatePasswordAsync(AuthorityUserManager userManager, AuthorityUser user, string password); + public Task ValidatePasswordAsync(AuthorityManager manager, AuthorityUser user, string password); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/IUserValidator.cs b/DotBased.AspNet.Authority/Validators/IUserValidator.cs index da07405..ef07650 100644 --- a/DotBased.AspNet.Authority/Validators/IUserValidator.cs +++ b/DotBased.AspNet.Authority/Validators/IUserValidator.cs @@ -6,5 +6,5 @@ namespace DotBased.AspNet.Authority.Validators; public interface IUserValidator { - public Task ValidateUserAsync(AuthorityUserManager manager, AuthorityUser user); + public Task ValidateUserAsync(AuthorityManager manager, AuthorityUser user); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs b/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs index a95c55d..8278e13 100644 --- a/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs +++ b/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs @@ -8,7 +8,7 @@ public class PasswordEqualsValidator : IPasswordValidator { private const string ValidatorId = "Authority.Validator.Password.Equals"; private const string ValidationBase = "Authority.Validation.Password"; - public async Task ValidatePasswordAsync(AuthorityUserManager userManager, AuthorityUser user, string password) + public async Task ValidatePasswordAsync(AuthorityManager userManager, AuthorityUser user, string password) { List errors = []; var hashedPassword = await userManager.PasswordHasher.HashPasswordAsync(password); diff --git a/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs b/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs index bd38b25..41cab40 100644 --- a/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs +++ b/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs @@ -8,19 +8,18 @@ namespace DotBased.AspNet.Authority.Validators; /// /// Validates the password against the options that is configured. /// -/// The user model used. public class PasswordOptionsValidator : IPasswordValidator { private const string ValidatorId = "Authority.Validator.Password.Options"; private const string ValidationBase = "Authority.Validation.Password"; - public async Task ValidatePasswordAsync(AuthorityUserManager userManager, AuthorityUser user, string password) + public async Task ValidatePasswordAsync(AuthorityManager userManager, AuthorityUser user, string password) { if (userManager == null) { throw new ArgumentNullException(nameof(userManager), "User manager is not provided!"); } - var passwordOptions = userManager.AuthorityManager.Options.Password; + var passwordOptions = userManager.Options.Password; var errors = new List(); if (password.IsNullOrEmpty() || password.Length < passwordOptions.RequiredLength) diff --git a/DotBased.AspNet.Authority/Validators/UserValidator.cs b/DotBased.AspNet.Authority/Validators/UserValidator.cs index 317327f..1b914bc 100644 --- a/DotBased.AspNet.Authority/Validators/UserValidator.cs +++ b/DotBased.AspNet.Authority/Validators/UserValidator.cs @@ -10,11 +10,11 @@ public class UserValidator : IUserValidator private const string ValidatorId = "Authority.Validator.User"; private const string ValidationBase = "Authority.Validation.User"; - public async Task ValidateUserAsync(AuthorityUserManager manager, AuthorityUser user) + public async Task ValidateUserAsync(AuthorityManager manager, AuthorityUser user) { List errors = []; - var userOptions = manager.AuthorityManager.Options.User; + var userOptions = manager.Options.User; if (userOptions.RequireUniqueEmail) { From fd733b7238d09c18e4550251c77cb0f9c739dc7f Mon Sep 17 00:00:00 2001 From: max Date: Wed, 8 Jan 2025 16:22:59 +0100 Subject: [PATCH 14/24] [ADD] Add cancellation tokens to async functions --- .../Managers/AuthorityManager.cs | 3 +++ .../Managers/AuthorityRoleManager.cs | 22 ++++++++++++++++++- .../Managers/AuthorityUserManager.cs | 20 ++++++++--------- .../Models/Authority/AuthorityRole.cs | 18 ++------------- 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/DotBased.AspNet.Authority/Managers/AuthorityManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityManager.cs index 4b78e03..50548b1 100644 --- a/DotBased.AspNet.Authority/Managers/AuthorityManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityManager.cs @@ -16,6 +16,7 @@ public partial class AuthorityManager IServiceProvider services, ICryptographer cryptographer, IUserRepository userRepository, + IRoleRepository roleRepository, IPasswordHasher passwordHasher) { _logger = LogService.RegisterLogger(); @@ -23,6 +24,7 @@ public partial class AuthorityManager Services = services; Cryptographer = cryptographer; UserRepository = userRepository; + RoleRepository = roleRepository; PasswordHasher = passwordHasher; } @@ -33,6 +35,7 @@ public partial class AuthorityManager public ICryptographer Cryptographer { get; } public IUserRepository UserRepository { get; } + public IRoleRepository RoleRepository { get; } public IPasswordHasher PasswordHasher { get; } diff --git a/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs index 22e70cb..3500cb4 100644 --- a/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs @@ -1,6 +1,26 @@ +using DotBased.AspNet.Authority.Models.Authority; + namespace DotBased.AspNet.Authority.Managers; public partial class AuthorityManager { - + public async Task> CreateRoleAsync(AuthorityRole role, CancellationToken? cancellationToken = null) + { + return Result.Failed("Not implemented!"); + } + + public async Task DeleteRoleAsync(AuthorityRole role, CancellationToken? cancellationToken = null) + { + return Result.Failed("Not implemented!"); + } + + public async Task> UpdateRoleAsync(AuthorityRole role, CancellationToken? cancellationToken = null) + { + return Result.Failed("Not implemented!"); + } + + public async Task AddUserToRole(AuthorityUser user, AuthorityRole role, CancellationToken? cancellationToken = null) + { + + } } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs index 94feb6f..5b07cfc 100644 --- a/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs @@ -34,13 +34,13 @@ public partial class AuthorityManager return errors.Count > 0 ? ValidationResult.Failed(errors) : ValidationResult.Ok(); } - public async Task> SearchUsersAsync(string query, int maxResults = 20, int offset = 0) + public async Task> SearchUsersAsync(string query, int maxResults = 20, int offset = 0, CancellationToken? cancellationToken = null) { - var searchResult = await UserRepository.GetAuthorityUsersAsync(query, maxResults, offset); + var searchResult = await UserRepository.GetAuthorityUsersAsync(query, maxResults, offset, cancellationToken); return searchResult.Item1 == null ? ListResult.Failed("No results!") : ListResult.Ok(searchResult.Item1, searchResult.Item2); } - public async Task> UpdatePasswordAsync(AuthorityUser user, string password) + public async Task> UpdatePasswordAsync(AuthorityUser user, string password, CancellationToken? cancellationToken = null) { var passwordValidation = await ValidatePasswordAsync(user, password); if (!passwordValidation.Success) @@ -53,11 +53,11 @@ public partial class AuthorityManager user.PasswordHash = await PasswordHasher.HashPasswordAsync(password); user.SecurityVersion = GenerateVersion(); - var updateResult = await UserRepository.UpdateUserAsync(user); + var updateResult = await UserRepository.UpdateUserAsync(user, cancellationToken); return updateResult == null ? AuthorityResult.Error("Failed to save updates!") : AuthorityResult.Ok(updateResult); } - public async Task> CreateUserAsync(AuthorityUser userModel, string password) + public async Task> CreateUserAsync(AuthorityUser userModel, string password, CancellationToken? cancellationToken = null) { var userValidation = await ValidateUserAsync(userModel); var passwordValidation = await ValidatePasswordAsync(userModel, password); @@ -74,22 +74,22 @@ public partial class AuthorityManager var hashedPassword = await PasswordHasher.HashPasswordAsync(password); userModel.PasswordHash = hashedPassword; - var userCreationResult = await UserRepository.CreateUserAsync(userModel); + var userCreationResult = await UserRepository.CreateUserAsync(userModel, cancellationToken); return userCreationResult != null ? AuthorityResult.Ok(userCreationResult) : AuthorityResult.Error("Failed to create user in repository!"); } - public async Task> UpdateUserAsync(AuthorityUser model) + public async Task> UpdateUserAsync(AuthorityUser model, CancellationToken? cancellationToken = null) { - var updateResult = await UserRepository.UpdateUserAsync(model); + var updateResult = await UserRepository.UpdateUserAsync(model, cancellationToken); return updateResult != null ? Result.Ok(updateResult) : Result.Failed("Failed to update user in repository!"); } - public async Task DeleteUserAsync(AuthorityUser model) + public async Task DeleteUserAsync(AuthorityUser model, CancellationToken? cancellationToken = null) { - var deleteResult = await UserRepository.DeleteUserAsync(model); + var deleteResult = await UserRepository.DeleteUserAsync(model, cancellationToken); return deleteResult; } diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs index 90ccee5..4fc2600 100644 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs @@ -1,22 +1,8 @@ namespace DotBased.AspNet.Authority.Models.Authority; -public class AuthorityRole : AuthorityRole +public abstract class AuthorityRole { - public AuthorityRole(string name) : this() - { - Name = name; - } - - public AuthorityRole() - { - Id = Guid.NewGuid(); - CreatedDate = DateTime.Now; - } -} - -public abstract class AuthorityRole where TKey : IEquatable -{ - public TKey Id { get; set; } + public Guid Id { get; set; } public string? Name { get; set; } From 3ccd3106c14ac735150e65399ccfdda320d62eb5 Mon Sep 17 00:00:00 2001 From: max Date: Wed, 8 Jan 2025 16:30:51 +0100 Subject: [PATCH 15/24] [CHANGE] Updated models --- .../Managers/AuthorityRoleManager.cs | 10 +++++++++- .../Models/Authority/AuthorityGroup.cs | 9 +++------ .../Models/Authority/AuthorityRole.cs | 11 +++++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs index 3500cb4..52f09a5 100644 --- a/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs @@ -19,8 +19,16 @@ public partial class AuthorityManager return Result.Failed("Not implemented!"); } - public async Task AddUserToRole(AuthorityUser user, AuthorityRole role, CancellationToken? cancellationToken = null) + public async Task AddRoleToUserAsync(AuthorityUser user, AuthorityRole role, CancellationToken? cancellationToken = null) { } + + public async Task RemoveRoleFromUserAsync(AuthorityRole role, AuthorityUser user, CancellationToken? cancellationToken = null) + { + } + + public async Task AddRoleToGroupAsync(AuthorityRole role, AuthorityGroup group, CancellationToken? cancellationToken = null) + { + } } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs index df010e8..c5474df 100644 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs @@ -1,6 +1,6 @@ namespace DotBased.AspNet.Authority.Models.Authority; -public class AuthorityGroup : AuthorityGroup +public class AuthorityGroup { public AuthorityGroup(string name) : this() { @@ -12,11 +12,8 @@ public class AuthorityGroup : AuthorityGroup Id = Guid.NewGuid(); CreatedDate = DateTime.Now; } -} - -public abstract class AuthorityGroup where TKey : IEquatable -{ - public TKey Id { get; set; } + + public Guid Id { get; set; } public string? Name { get; set; } diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs index 4fc2600..1e22cb5 100644 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs @@ -2,6 +2,17 @@ namespace DotBased.AspNet.Authority.Models.Authority; public abstract class AuthorityRole { + public AuthorityRole(string name) : this() + { + Name = name; + } + + public AuthorityRole() + { + Id = Guid.NewGuid(); + CreatedDate = DateTime.Now; + } + public Guid Id { get; set; } public string? Name { get; set; } From c27890a31fa8ffb2f0f0c921f3dd18d925cdc7c2 Mon Sep 17 00:00:00 2001 From: max Date: Mon, 27 Jan 2025 01:21:09 +0100 Subject: [PATCH 16/24] [CHANGE] Building data structure --- Blazor.Wasm/App.razor | 0 Blazor.Wasm/Blazor.Wasm.csproj | 0 Blazor.Wasm/Layout/MainLayout.razor | 0 Blazor.Wasm/Layout/MainLayout.razor.css | 0 Blazor.Wasm/Layout/NavMenu.razor | 0 Blazor.Wasm/Layout/NavMenu.razor.css | 0 Blazor.Wasm/Pages/Counter.razor | 0 Blazor.Wasm/Pages/Home.razor | 0 Blazor.Wasm/Pages/Weather.razor | 0 Blazor.Wasm/Program.cs | 0 Blazor.Wasm/Properties/launchSettings.json | 0 Blazor.Wasm/_Imports.razor | 0 Blazor.Wasm/wwwroot/css/app.css | 0 .../wwwroot/css/bootstrap/bootstrap.min.css | 0 .../css/bootstrap/bootstrap.min.css.map | 0 Blazor.Wasm/wwwroot/favicon.png | Bin Blazor.Wasm/wwwroot/icon-192.png | Bin Blazor.Wasm/wwwroot/index.html | 0 Blazor.Wasm/wwwroot/sample-data/weather.json | 0 DotBased.ASP.Auth/AuthDataCache.cs | 0 DotBased.ASP.Auth/AuthenticationService.cs | 0 DotBased.ASP.Auth/BasedAuthConfiguration.cs | 0 DotBased.ASP.Auth/BasedAuthDefaults.cs | 0 .../BasedServerAuthenticationStateProvider.cs | 0 .../Domains/Auth/AuthenticationStateModel.cs | 0 .../Domains/Auth/PermissionModel.cs | 0 DotBased.ASP.Auth/Domains/Auth/RoleModel.cs | 0 .../Domains/Identity/GroupItemModel.cs | 0 .../Domains/Identity/GroupModel.cs | 0 .../Domains/Identity/UserItemModel.cs | 0 .../Domains/Identity/UserModel.cs | 0 DotBased.ASP.Auth/Domains/LoginModel.cs | 0 DotBased.ASP.Auth/Domains/RegisterModel.cs | 0 DotBased.ASP.Auth/DotBased.ASP.Auth.csproj | 0 .../DotBasedAuthDependencyInjection.cs | 0 DotBased.ASP.Auth/IAuthDataRepository.cs | 0 DotBased.ASP.Auth/ISessionStateProvider.cs | 0 DotBased.ASP.Auth/MemoryAuthDataRepository.cs | 0 .../Models/Configuration/AuthConfiguration.cs | 0 .../Configuration/CacheConfiguration.cs | 0 .../Configuration/LockoutConfiguration.cs | 0 .../Configuration/PasswordConfiguration.cs | 0 .../Configuration/ProviderConfiguration.cs | 0 .../Configuration/RepositoryConfiguration.cs | 0 .../Models/Configuration/UserConfiguration.cs | 0 DotBased.ASP.Auth/SecurityManager.cs | 0 DotBased.ASP.Auth/SecurityService.cs | 0 .../Attributes/ProtectAttribute.cs | 0 DotBased.AspNet.Authority/AuthorityBuilder.cs | 0 .../AuthorityDefaults.cs | 0 .../AuthorityProviderExtensions.cs | 0 .../Crypto/Cryptographer.cs | 0 .../Crypto/ICryptographer.cs | 0 .../Crypto/IPasswordHasher.cs | 0 .../Crypto/PasswordHasher.cs | 0 .../DotBased.AspNet.Authority.csproj | 0 .../Managers/AuthorityGroupManager.cs | 6 +- .../Managers/AuthorityManager.cs | 43 ++-- .../Managers/AuthorityRoleManager.cs | 33 +++- .../Managers/AuthorityUserManager.cs | 0 .../Models/Authority/AuthorityAttribute.cs | 0 .../Models/Authority/AuthorityGroup.cs | 0 .../Models/Authority/AuthorityRole.cs | 16 +- .../Models/Authority/AuthorityUser.cs | 16 +- .../Models/AuthorityResult.cs | 0 .../Models/Options/AuthorityOptions.cs | 0 .../Models/Options/ListOption.cs | 0 .../Models/Options/LockdownOptions.cs | 0 .../Models/Options/LockoutOptions.cs | 0 .../Models/Options/PasswordOptions.cs | 0 .../Models/Options/ProviderOptions.cs | 0 .../Models/Options/RepositoryOptions.cs | 0 .../Models/Options/SignInOptions.cs | 0 .../Models/Options/UserOptions.cs | 0 .../Models/Validation/ValidationError.cs | 0 .../Models/Validation/ValidationResult.cs | 0 .../Repositories/IAttributeRepository.cs | 0 .../Repositories/IGroupRepository.cs | 0 .../Repositories/IRoleRepository.cs | 0 .../Repositories/IUserRepository.cs | 0 .../Validators/IPasswordValidator.cs | 0 .../Validators/IUserValidator.cs | 0 .../Validators/PasswordEqualsValidator.cs | 0 .../Validators/PasswordOptionsValidator.cs | 0 .../Validators/UserValidator.cs | 0 .../Verifiers/IEmailVerifier.cs | 0 .../Verifiers/IPhoneNumberVerifier.cs | 0 .../Verifiers/IUserVerifier.cs | 0 DotBased.Data/DotBased.Data.csproj | 12 ++ DotBased.Logging.MEL/BasedLogger.cs | 0 DotBased.Logging.MEL/BasedLoggerProvider.cs | 0 .../DotBased.Logging.MEL.csproj | 0 .../LoggerBuilderExtensions.cs | 0 DotBased.Logging.Serilog/BasedSerilog.cs | 0 .../BasedSerilogEnricher.cs | 0 DotBased.sln | 7 + DotBased/Objects/DbObjectAttribute.cs | 0 DotBased/Objects/IObjectAttribute.cs | 0 DotBased/Objects/ObjectAttribute.cs | 0 TestWebApi/Program.cs | 0 TestWebApi/Properties/launchSettings.json | 0 TestWebApi/SeedAuthorityData.cs | 0 TestWebApi/TestWebApi.csproj | 0 TestWebApi/TestWebApi.http | 0 TestWebApi/appsettings.Development.json | 0 TestWebApi/appsettings.json | 0 obs_DotBased/.obsidian/app.json | 1 + obs_DotBased/.obsidian/appearance.json | 1 + obs_DotBased/.obsidian/core-plugins.json | 30 +++ obs_DotBased/.obsidian/graph.json | 22 +++ obs_DotBased/.obsidian/workspace.json | 186 ++++++++++++++++++ .../DotBased.Authority/Data diagram.canvas | 22 +++ .../Models/AuthorityAttribute.md | 2 + .../Models/AuthorityGroup.md | 2 + .../Models/AuthorityRole.md | 1 + .../Models/AuthorityUser.md | 1 + .../Repositories/UserRepository.md | 6 + .../AspNet/DotBased.Authority/Repository.md | 0 118 files changed, 356 insertions(+), 51 deletions(-) mode change 100644 => 100755 Blazor.Wasm/App.razor mode change 100644 => 100755 Blazor.Wasm/Blazor.Wasm.csproj mode change 100644 => 100755 Blazor.Wasm/Layout/MainLayout.razor mode change 100644 => 100755 Blazor.Wasm/Layout/MainLayout.razor.css mode change 100644 => 100755 Blazor.Wasm/Layout/NavMenu.razor mode change 100644 => 100755 Blazor.Wasm/Layout/NavMenu.razor.css mode change 100644 => 100755 Blazor.Wasm/Pages/Counter.razor mode change 100644 => 100755 Blazor.Wasm/Pages/Home.razor mode change 100644 => 100755 Blazor.Wasm/Pages/Weather.razor mode change 100644 => 100755 Blazor.Wasm/Program.cs mode change 100644 => 100755 Blazor.Wasm/Properties/launchSettings.json mode change 100644 => 100755 Blazor.Wasm/_Imports.razor mode change 100644 => 100755 Blazor.Wasm/wwwroot/css/app.css mode change 100644 => 100755 Blazor.Wasm/wwwroot/css/bootstrap/bootstrap.min.css mode change 100644 => 100755 Blazor.Wasm/wwwroot/css/bootstrap/bootstrap.min.css.map mode change 100644 => 100755 Blazor.Wasm/wwwroot/favicon.png mode change 100644 => 100755 Blazor.Wasm/wwwroot/icon-192.png mode change 100644 => 100755 Blazor.Wasm/wwwroot/index.html mode change 100644 => 100755 Blazor.Wasm/wwwroot/sample-data/weather.json mode change 100644 => 100755 DotBased.ASP.Auth/AuthDataCache.cs mode change 100644 => 100755 DotBased.ASP.Auth/AuthenticationService.cs mode change 100644 => 100755 DotBased.ASP.Auth/BasedAuthConfiguration.cs mode change 100644 => 100755 DotBased.ASP.Auth/BasedAuthDefaults.cs mode change 100644 => 100755 DotBased.ASP.Auth/BasedServerAuthenticationStateProvider.cs mode change 100644 => 100755 DotBased.ASP.Auth/Domains/Auth/AuthenticationStateModel.cs mode change 100644 => 100755 DotBased.ASP.Auth/Domains/Auth/PermissionModel.cs mode change 100644 => 100755 DotBased.ASP.Auth/Domains/Auth/RoleModel.cs mode change 100644 => 100755 DotBased.ASP.Auth/Domains/Identity/GroupItemModel.cs mode change 100644 => 100755 DotBased.ASP.Auth/Domains/Identity/GroupModel.cs mode change 100644 => 100755 DotBased.ASP.Auth/Domains/Identity/UserItemModel.cs mode change 100644 => 100755 DotBased.ASP.Auth/Domains/Identity/UserModel.cs mode change 100644 => 100755 DotBased.ASP.Auth/Domains/LoginModel.cs mode change 100644 => 100755 DotBased.ASP.Auth/Domains/RegisterModel.cs mode change 100644 => 100755 DotBased.ASP.Auth/DotBased.ASP.Auth.csproj mode change 100644 => 100755 DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs mode change 100644 => 100755 DotBased.ASP.Auth/IAuthDataRepository.cs mode change 100644 => 100755 DotBased.ASP.Auth/ISessionStateProvider.cs mode change 100644 => 100755 DotBased.ASP.Auth/MemoryAuthDataRepository.cs mode change 100644 => 100755 DotBased.ASP.Auth/Models/Configuration/AuthConfiguration.cs mode change 100644 => 100755 DotBased.ASP.Auth/Models/Configuration/CacheConfiguration.cs mode change 100644 => 100755 DotBased.ASP.Auth/Models/Configuration/LockoutConfiguration.cs mode change 100644 => 100755 DotBased.ASP.Auth/Models/Configuration/PasswordConfiguration.cs mode change 100644 => 100755 DotBased.ASP.Auth/Models/Configuration/ProviderConfiguration.cs mode change 100644 => 100755 DotBased.ASP.Auth/Models/Configuration/RepositoryConfiguration.cs mode change 100644 => 100755 DotBased.ASP.Auth/Models/Configuration/UserConfiguration.cs mode change 100644 => 100755 DotBased.ASP.Auth/SecurityManager.cs mode change 100644 => 100755 DotBased.ASP.Auth/SecurityService.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs mode change 100644 => 100755 DotBased.AspNet.Authority/AuthorityBuilder.cs mode change 100644 => 100755 DotBased.AspNet.Authority/AuthorityDefaults.cs mode change 100644 => 100755 DotBased.AspNet.Authority/AuthorityProviderExtensions.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Crypto/Cryptographer.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Crypto/ICryptographer.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Crypto/IPasswordHasher.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Crypto/PasswordHasher.cs mode change 100644 => 100755 DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj mode change 100644 => 100755 DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Managers/AuthorityManager.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Models/AuthorityResult.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Models/Options/AuthorityOptions.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Models/Options/ListOption.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Models/Options/LockdownOptions.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Models/Options/LockoutOptions.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Models/Options/PasswordOptions.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Models/Options/ProviderOptions.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Models/Options/RepositoryOptions.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Models/Options/SignInOptions.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Models/Options/UserOptions.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Models/Validation/ValidationError.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Models/Validation/ValidationResult.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Repositories/IGroupRepository.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Repositories/IRoleRepository.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Repositories/IUserRepository.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Validators/IPasswordValidator.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Validators/IUserValidator.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Validators/UserValidator.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Verifiers/IEmailVerifier.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Verifiers/IPhoneNumberVerifier.cs mode change 100644 => 100755 DotBased.AspNet.Authority/Verifiers/IUserVerifier.cs create mode 100644 DotBased.Data/DotBased.Data.csproj mode change 100644 => 100755 DotBased.Logging.MEL/BasedLogger.cs mode change 100644 => 100755 DotBased.Logging.MEL/BasedLoggerProvider.cs mode change 100644 => 100755 DotBased.Logging.MEL/DotBased.Logging.MEL.csproj mode change 100644 => 100755 DotBased.Logging.MEL/LoggerBuilderExtensions.cs mode change 100644 => 100755 DotBased.Logging.Serilog/BasedSerilog.cs mode change 100644 => 100755 DotBased.Logging.Serilog/BasedSerilogEnricher.cs mode change 100644 => 100755 DotBased/Objects/DbObjectAttribute.cs mode change 100644 => 100755 DotBased/Objects/IObjectAttribute.cs mode change 100644 => 100755 DotBased/Objects/ObjectAttribute.cs mode change 100644 => 100755 TestWebApi/Program.cs mode change 100644 => 100755 TestWebApi/Properties/launchSettings.json mode change 100644 => 100755 TestWebApi/SeedAuthorityData.cs mode change 100644 => 100755 TestWebApi/TestWebApi.csproj mode change 100644 => 100755 TestWebApi/TestWebApi.http mode change 100644 => 100755 TestWebApi/appsettings.Development.json mode change 100644 => 100755 TestWebApi/appsettings.json create mode 100644 obs_DotBased/.obsidian/app.json create mode 100644 obs_DotBased/.obsidian/appearance.json create mode 100644 obs_DotBased/.obsidian/core-plugins.json create mode 100644 obs_DotBased/.obsidian/graph.json create mode 100644 obs_DotBased/.obsidian/workspace.json create mode 100644 obs_DotBased/Modules/AspNet/DotBased.Authority/Data diagram.canvas create mode 100644 obs_DotBased/Modules/AspNet/DotBased.Authority/Models/AuthorityAttribute.md create mode 100644 obs_DotBased/Modules/AspNet/DotBased.Authority/Models/AuthorityGroup.md create mode 100644 obs_DotBased/Modules/AspNet/DotBased.Authority/Models/AuthorityRole.md create mode 100644 obs_DotBased/Modules/AspNet/DotBased.Authority/Models/AuthorityUser.md create mode 100644 obs_DotBased/Modules/AspNet/DotBased.Authority/Repositories/UserRepository.md create mode 100644 obs_DotBased/Modules/AspNet/DotBased.Authority/Repository.md diff --git a/Blazor.Wasm/App.razor b/Blazor.Wasm/App.razor old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/Blazor.Wasm.csproj b/Blazor.Wasm/Blazor.Wasm.csproj old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/Layout/MainLayout.razor b/Blazor.Wasm/Layout/MainLayout.razor old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/Layout/MainLayout.razor.css b/Blazor.Wasm/Layout/MainLayout.razor.css old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/Layout/NavMenu.razor b/Blazor.Wasm/Layout/NavMenu.razor old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/Layout/NavMenu.razor.css b/Blazor.Wasm/Layout/NavMenu.razor.css old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/Pages/Counter.razor b/Blazor.Wasm/Pages/Counter.razor old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/Pages/Home.razor b/Blazor.Wasm/Pages/Home.razor old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/Pages/Weather.razor b/Blazor.Wasm/Pages/Weather.razor old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/Program.cs b/Blazor.Wasm/Program.cs old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/Properties/launchSettings.json b/Blazor.Wasm/Properties/launchSettings.json old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/_Imports.razor b/Blazor.Wasm/_Imports.razor old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/wwwroot/css/app.css b/Blazor.Wasm/wwwroot/css/app.css old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/wwwroot/css/bootstrap/bootstrap.min.css b/Blazor.Wasm/wwwroot/css/bootstrap/bootstrap.min.css old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/wwwroot/css/bootstrap/bootstrap.min.css.map b/Blazor.Wasm/wwwroot/css/bootstrap/bootstrap.min.css.map old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/wwwroot/favicon.png b/Blazor.Wasm/wwwroot/favicon.png old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/wwwroot/icon-192.png b/Blazor.Wasm/wwwroot/icon-192.png old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/wwwroot/index.html b/Blazor.Wasm/wwwroot/index.html old mode 100644 new mode 100755 diff --git a/Blazor.Wasm/wwwroot/sample-data/weather.json b/Blazor.Wasm/wwwroot/sample-data/weather.json old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/AuthDataCache.cs b/DotBased.ASP.Auth/AuthDataCache.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/AuthenticationService.cs b/DotBased.ASP.Auth/AuthenticationService.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/BasedAuthConfiguration.cs b/DotBased.ASP.Auth/BasedAuthConfiguration.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/BasedAuthDefaults.cs b/DotBased.ASP.Auth/BasedAuthDefaults.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/BasedServerAuthenticationStateProvider.cs b/DotBased.ASP.Auth/BasedServerAuthenticationStateProvider.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/Domains/Auth/AuthenticationStateModel.cs b/DotBased.ASP.Auth/Domains/Auth/AuthenticationStateModel.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/Domains/Auth/PermissionModel.cs b/DotBased.ASP.Auth/Domains/Auth/PermissionModel.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/Domains/Auth/RoleModel.cs b/DotBased.ASP.Auth/Domains/Auth/RoleModel.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/Domains/Identity/GroupItemModel.cs b/DotBased.ASP.Auth/Domains/Identity/GroupItemModel.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/Domains/Identity/GroupModel.cs b/DotBased.ASP.Auth/Domains/Identity/GroupModel.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/Domains/Identity/UserItemModel.cs b/DotBased.ASP.Auth/Domains/Identity/UserItemModel.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/Domains/Identity/UserModel.cs b/DotBased.ASP.Auth/Domains/Identity/UserModel.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/Domains/LoginModel.cs b/DotBased.ASP.Auth/Domains/LoginModel.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/Domains/RegisterModel.cs b/DotBased.ASP.Auth/Domains/RegisterModel.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/DotBased.ASP.Auth.csproj b/DotBased.ASP.Auth/DotBased.ASP.Auth.csproj old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs b/DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/IAuthDataRepository.cs b/DotBased.ASP.Auth/IAuthDataRepository.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/ISessionStateProvider.cs b/DotBased.ASP.Auth/ISessionStateProvider.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/MemoryAuthDataRepository.cs b/DotBased.ASP.Auth/MemoryAuthDataRepository.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/Models/Configuration/AuthConfiguration.cs b/DotBased.ASP.Auth/Models/Configuration/AuthConfiguration.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/Models/Configuration/CacheConfiguration.cs b/DotBased.ASP.Auth/Models/Configuration/CacheConfiguration.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/Models/Configuration/LockoutConfiguration.cs b/DotBased.ASP.Auth/Models/Configuration/LockoutConfiguration.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/Models/Configuration/PasswordConfiguration.cs b/DotBased.ASP.Auth/Models/Configuration/PasswordConfiguration.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/Models/Configuration/ProviderConfiguration.cs b/DotBased.ASP.Auth/Models/Configuration/ProviderConfiguration.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/Models/Configuration/RepositoryConfiguration.cs b/DotBased.ASP.Auth/Models/Configuration/RepositoryConfiguration.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/Models/Configuration/UserConfiguration.cs b/DotBased.ASP.Auth/Models/Configuration/UserConfiguration.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/SecurityManager.cs b/DotBased.ASP.Auth/SecurityManager.cs old mode 100644 new mode 100755 diff --git a/DotBased.ASP.Auth/SecurityService.cs b/DotBased.ASP.Auth/SecurityService.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs b/DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/AuthorityBuilder.cs b/DotBased.AspNet.Authority/AuthorityBuilder.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/AuthorityDefaults.cs b/DotBased.AspNet.Authority/AuthorityDefaults.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Crypto/Cryptographer.cs b/DotBased.AspNet.Authority/Crypto/Cryptographer.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Crypto/ICryptographer.cs b/DotBased.AspNet.Authority/Crypto/ICryptographer.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Crypto/IPasswordHasher.cs b/DotBased.AspNet.Authority/Crypto/IPasswordHasher.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Crypto/PasswordHasher.cs b/DotBased.AspNet.Authority/Crypto/PasswordHasher.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj b/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs old mode 100644 new mode 100755 index 22e70cb..a91e65d --- a/DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs @@ -2,5 +2,9 @@ namespace DotBased.AspNet.Authority.Managers; public partial class AuthorityManager { - + /* + * - Validate User & Group + * - Check if user is already in group (if already in group return) + * - Add to UsersGroups table + */ } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Managers/AuthorityManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityManager.cs old mode 100644 new mode 100755 index 50548b1..a9f83f9 --- a/DotBased.AspNet.Authority/Managers/AuthorityManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityManager.cs @@ -9,36 +9,25 @@ using Microsoft.Extensions.Options; namespace DotBased.AspNet.Authority.Managers; -public partial class AuthorityManager +public partial class AuthorityManager( + IOptions options, + IServiceProvider services, + ICryptographer cryptographer, + IUserRepository userRepository, + IRoleRepository roleRepository, + IPasswordHasher passwordHasher) { - public AuthorityManager( - IOptions options, - IServiceProvider services, - ICryptographer cryptographer, - IUserRepository userRepository, - IRoleRepository roleRepository, - IPasswordHasher passwordHasher) - { - _logger = LogService.RegisterLogger(); - Options = options.Value; - Services = services; - Cryptographer = cryptographer; - UserRepository = userRepository; - RoleRepository = roleRepository; - PasswordHasher = passwordHasher; - } + private readonly ILogger _logger = LogService.RegisterLogger(); - private readonly ILogger _logger; + public IServiceProvider Services { get; } = services; + public AuthorityOptions Options { get; } = options.Value; + public ICryptographer Cryptographer { get; } = cryptographer; + + public IUserRepository UserRepository { get; } = userRepository; + public IRoleRepository RoleRepository { get; } = roleRepository; + + public IPasswordHasher PasswordHasher { get; } = passwordHasher; - public IServiceProvider Services { get; } - public AuthorityOptions Options { get; } - public ICryptographer Cryptographer { get; } - - public IUserRepository UserRepository { get; } - public IRoleRepository RoleRepository { get; } - - public IPasswordHasher PasswordHasher { get; } - public IEnumerable PasswordValidators { get; } = []; public IEnumerable UserValidators { get; } = []; diff --git a/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs old mode 100644 new mode 100755 index 52f09a5..2b62e98 --- a/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs @@ -19,9 +19,22 @@ public partial class AuthorityManager return Result.Failed("Not implemented!"); } + public async Task> GetRolesAsync(int limit = 20, int offset = 0, string search = "", CancellationToken? cancellationToken = null) + { + /* + * Search by role name & id + * Order by name, created date, creator? (paging) + */ + return ListResult.Failed("Not implemented!"); + } + public async Task AddRoleToUserAsync(AuthorityUser user, AuthorityRole role, CancellationToken? cancellationToken = null) { - + /* + - Validate User & Role + - Check if role is already in linked to user (if user already has the role, return) + - Add to UsersRoles table + */ } public async Task RemoveRoleFromUserAsync(AuthorityRole role, AuthorityUser user, CancellationToken? cancellationToken = null) @@ -31,4 +44,22 @@ public partial class AuthorityManager public async Task AddRoleToGroupAsync(AuthorityRole role, AuthorityGroup group, CancellationToken? cancellationToken = null) { } + + /// + /// Get all roles (including group roles) that the user has. + /// + /// The user to get the roles from + /// + public async Task> GetUserRolesAsync(AuthorityUser user, CancellationToken? cancellationToken = null) + { + /* + * - Validate user + * - Get user groups (id) + * - Get roles contained from user + * - Get roles contained from groups (if any) + * - Order by (for paging) + */ + + return ListResult.Failed("Not implemented!"); + } } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs old mode 100644 new mode 100755 index 1e22cb5..f1ef51a --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs @@ -1,25 +1,19 @@ namespace DotBased.AspNet.Authority.Models.Authority; -public abstract class AuthorityRole +public abstract class AuthorityRole() { public AuthorityRole(string name) : this() { Name = name; } - - public AuthorityRole() - { - Id = Guid.NewGuid(); - CreatedDate = DateTime.Now; - } - - public Guid Id { get; set; } - + + public Guid Id { get; set; } = Guid.NewGuid(); + public string? Name { get; set; } public long Version { get; set; } - public DateTime CreatedDate { get; set; } + public DateTime CreatedDate { get; set; } = DateTime.Now; public override string ToString() => Name ?? string.Empty; } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs old mode 100644 new mode 100755 index 9f52b58..bc90356 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs @@ -2,21 +2,15 @@ using DotBased.AspNet.Authority.Attributes; namespace DotBased.AspNet.Authority.Models.Authority; -public class AuthorityUser +public class AuthorityUser() { public AuthorityUser(string userName) : this() { UserName = userName; } - - public AuthorityUser() - { - Id = Guid.NewGuid(); - CreatedDate = DateTime.Now; - } - - public Guid Id { get; set; } - + + public Guid Id { get; set; } = Guid.NewGuid(); + public bool Enabled { get; set; } public bool Confirmed { get; set; } @@ -29,7 +23,7 @@ public class AuthorityUser public string? PasswordHash { get; set; } - public DateTime CreatedDate { get; set; } + public DateTime CreatedDate { get; set; } = DateTime.Now; public bool TwoFactorEnabled { get; set; } diff --git a/DotBased.AspNet.Authority/Models/AuthorityResult.cs b/DotBased.AspNet.Authority/Models/AuthorityResult.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Models/Options/AuthorityOptions.cs b/DotBased.AspNet.Authority/Models/Options/AuthorityOptions.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Models/Options/ListOption.cs b/DotBased.AspNet.Authority/Models/Options/ListOption.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Models/Options/LockdownOptions.cs b/DotBased.AspNet.Authority/Models/Options/LockdownOptions.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Models/Options/LockoutOptions.cs b/DotBased.AspNet.Authority/Models/Options/LockoutOptions.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Models/Options/PasswordOptions.cs b/DotBased.AspNet.Authority/Models/Options/PasswordOptions.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Models/Options/ProviderOptions.cs b/DotBased.AspNet.Authority/Models/Options/ProviderOptions.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Models/Options/RepositoryOptions.cs b/DotBased.AspNet.Authority/Models/Options/RepositoryOptions.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Models/Options/SignInOptions.cs b/DotBased.AspNet.Authority/Models/Options/SignInOptions.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Models/Options/UserOptions.cs b/DotBased.AspNet.Authority/Models/Options/UserOptions.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Models/Validation/ValidationError.cs b/DotBased.AspNet.Authority/Models/Validation/ValidationError.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Models/Validation/ValidationResult.cs b/DotBased.AspNet.Authority/Models/Validation/ValidationResult.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs b/DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs b/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs b/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Repositories/IUserRepository.cs b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs b/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Validators/IUserValidator.cs b/DotBased.AspNet.Authority/Validators/IUserValidator.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs b/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs b/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Validators/UserValidator.cs b/DotBased.AspNet.Authority/Validators/UserValidator.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Verifiers/IEmailVerifier.cs b/DotBased.AspNet.Authority/Verifiers/IEmailVerifier.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Verifiers/IPhoneNumberVerifier.cs b/DotBased.AspNet.Authority/Verifiers/IPhoneNumberVerifier.cs old mode 100644 new mode 100755 diff --git a/DotBased.AspNet.Authority/Verifiers/IUserVerifier.cs b/DotBased.AspNet.Authority/Verifiers/IUserVerifier.cs old mode 100644 new mode 100755 diff --git a/DotBased.Data/DotBased.Data.csproj b/DotBased.Data/DotBased.Data.csproj new file mode 100644 index 0000000..f5d3ce2 --- /dev/null +++ b/DotBased.Data/DotBased.Data.csproj @@ -0,0 +1,12 @@ + + + + netstandard2.1 + enable + + + + + + + diff --git a/DotBased.Logging.MEL/BasedLogger.cs b/DotBased.Logging.MEL/BasedLogger.cs old mode 100644 new mode 100755 diff --git a/DotBased.Logging.MEL/BasedLoggerProvider.cs b/DotBased.Logging.MEL/BasedLoggerProvider.cs old mode 100644 new mode 100755 diff --git a/DotBased.Logging.MEL/DotBased.Logging.MEL.csproj b/DotBased.Logging.MEL/DotBased.Logging.MEL.csproj old mode 100644 new mode 100755 diff --git a/DotBased.Logging.MEL/LoggerBuilderExtensions.cs b/DotBased.Logging.MEL/LoggerBuilderExtensions.cs old mode 100644 new mode 100755 diff --git a/DotBased.Logging.Serilog/BasedSerilog.cs b/DotBased.Logging.Serilog/BasedSerilog.cs old mode 100644 new mode 100755 diff --git a/DotBased.Logging.Serilog/BasedSerilogEnricher.cs b/DotBased.Logging.Serilog/BasedSerilogEnricher.cs old mode 100644 new mode 100755 diff --git a/DotBased.sln b/DotBased.sln index 0275d48..b867aa7 100755 --- a/DotBased.sln +++ b/DotBased.sln @@ -22,6 +22,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AspNet", "AspNet", "{624E7B EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotBased.AspNet.Authority", "DotBased.AspNet.Authority\DotBased.AspNet.Authority.csproj", "{A3ADC9AF-39B7-4EC4-8022-946118A8C322}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotBased.Data", "DotBased.Data\DotBased.Data.csproj", "{2DF9FEEF-5A60-4B41-9B5F-F883DCE33EF4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -60,6 +62,10 @@ Global {A3ADC9AF-39B7-4EC4-8022-946118A8C322}.Debug|Any CPU.Build.0 = Debug|Any CPU {A3ADC9AF-39B7-4EC4-8022-946118A8C322}.Release|Any CPU.ActiveCfg = Release|Any CPU {A3ADC9AF-39B7-4EC4-8022-946118A8C322}.Release|Any CPU.Build.0 = Release|Any CPU + {2DF9FEEF-5A60-4B41-9B5F-F883DCE33EF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2DF9FEEF-5A60-4B41-9B5F-F883DCE33EF4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2DF9FEEF-5A60-4B41-9B5F-F883DCE33EF4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2DF9FEEF-5A60-4B41-9B5F-F883DCE33EF4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {EBBDAF9A-BFC7-4BDC-8C51-0501B59A1DDC} = {2156FB93-C252-4B33-8A0C-73C82FABB163} @@ -69,5 +75,6 @@ Global {AC8343A5-7953-4E1D-A926-406BE4D7E819} = {DBDB4538-85D4-45AC-9E0A-A684467AEABA} {624E7B11-8A18-46E5-AB1F-6AF6097F9D4D} = {2156FB93-C252-4B33-8A0C-73C82FABB163} {A3ADC9AF-39B7-4EC4-8022-946118A8C322} = {624E7B11-8A18-46E5-AB1F-6AF6097F9D4D} + {2DF9FEEF-5A60-4B41-9B5F-F883DCE33EF4} = {2156FB93-C252-4B33-8A0C-73C82FABB163} EndGlobalSection EndGlobal diff --git a/DotBased/Objects/DbObjectAttribute.cs b/DotBased/Objects/DbObjectAttribute.cs old mode 100644 new mode 100755 diff --git a/DotBased/Objects/IObjectAttribute.cs b/DotBased/Objects/IObjectAttribute.cs old mode 100644 new mode 100755 diff --git a/DotBased/Objects/ObjectAttribute.cs b/DotBased/Objects/ObjectAttribute.cs old mode 100644 new mode 100755 diff --git a/TestWebApi/Program.cs b/TestWebApi/Program.cs old mode 100644 new mode 100755 diff --git a/TestWebApi/Properties/launchSettings.json b/TestWebApi/Properties/launchSettings.json old mode 100644 new mode 100755 diff --git a/TestWebApi/SeedAuthorityData.cs b/TestWebApi/SeedAuthorityData.cs old mode 100644 new mode 100755 diff --git a/TestWebApi/TestWebApi.csproj b/TestWebApi/TestWebApi.csproj old mode 100644 new mode 100755 diff --git a/TestWebApi/TestWebApi.http b/TestWebApi/TestWebApi.http old mode 100644 new mode 100755 diff --git a/TestWebApi/appsettings.Development.json b/TestWebApi/appsettings.Development.json old mode 100644 new mode 100755 diff --git a/TestWebApi/appsettings.json b/TestWebApi/appsettings.json old mode 100644 new mode 100755 diff --git a/obs_DotBased/.obsidian/app.json b/obs_DotBased/.obsidian/app.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/obs_DotBased/.obsidian/app.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/obs_DotBased/.obsidian/appearance.json b/obs_DotBased/.obsidian/appearance.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/obs_DotBased/.obsidian/appearance.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/obs_DotBased/.obsidian/core-plugins.json b/obs_DotBased/.obsidian/core-plugins.json new file mode 100644 index 0000000..436f43c --- /dev/null +++ b/obs_DotBased/.obsidian/core-plugins.json @@ -0,0 +1,30 @@ +{ + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "properties": false, + "page-preview": true, + "daily-notes": true, + "templates": true, + "note-composer": true, + "command-palette": true, + "slash-command": false, + "editor-status": true, + "bookmarks": true, + "markdown-importer": false, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": false, + "audio-recorder": false, + "workspaces": false, + "file-recovery": true, + "publish": false, + "sync": false +} \ No newline at end of file diff --git a/obs_DotBased/.obsidian/graph.json b/obs_DotBased/.obsidian/graph.json new file mode 100644 index 0000000..42a46ec --- /dev/null +++ b/obs_DotBased/.obsidian/graph.json @@ -0,0 +1,22 @@ +{ + "collapse-filter": true, + "search": "", + "showTags": false, + "showAttachments": false, + "hideUnresolved": false, + "showOrphans": true, + "collapse-color-groups": true, + "colorGroups": [], + "collapse-display": true, + "showArrow": false, + "textFadeMultiplier": 0, + "nodeSizeMultiplier": 1, + "lineSizeMultiplier": 1, + "collapse-forces": true, + "centerStrength": 0.518713248970312, + "repelStrength": 10, + "linkStrength": 1, + "linkDistance": 250, + "scale": 1, + "close": true +} \ No newline at end of file diff --git a/obs_DotBased/.obsidian/workspace.json b/obs_DotBased/.obsidian/workspace.json new file mode 100644 index 0000000..7819a34 --- /dev/null +++ b/obs_DotBased/.obsidian/workspace.json @@ -0,0 +1,186 @@ +{ + "main": { + "id": "036516b320d15f05", + "type": "split", + "children": [ + { + "id": "68f3abbbf106a47b", + "type": "tabs", + "children": [ + { + "id": "9629cc68ecd8963f", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "Modules/AspNet/DotBased.Authority/Repositories/UserRepository.md", + "mode": "source", + "source": false + }, + "icon": "lucide-file", + "title": "UserRepository" + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "97d2b7eaa5b0817b", + "type": "split", + "children": [ + { + "id": "4ab704d9f07468d7", + "type": "tabs", + "children": [ + { + "id": "2dcd0dffd753993c", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical" + }, + "icon": "lucide-folder-closed", + "title": "Files" + } + }, + { + "id": "4a5d90d4f8e6e8b9", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + }, + "icon": "lucide-search", + "title": "Search" + } + }, + { + "id": "26ebe543611d3a9b", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {}, + "icon": "lucide-bookmark", + "title": "Bookmarks" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300 + }, + "right": { + "id": "37b39260b7304344", + "type": "split", + "children": [ + { + "id": "233acbe146cb7e31", + "type": "tabs", + "children": [ + { + "id": "61d17c9ee7d66c50", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "Modules/AspNet/DotBased.Authority/Repositories/UserRepository.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-coming-in", + "title": "Backlinks for UserRepository" + } + }, + { + "id": "9edca2501ca8fc8d", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "Modules/AspNet/DotBased.Authority/Repositories/UserRepository.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-going-out", + "title": "Outgoing links from UserRepository" + } + }, + { + "id": "a0b5dc7ca04cc4f8", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + }, + "icon": "lucide-tags", + "title": "Tags" + } + }, + { + "id": "25f7df275652ff82", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "Modules/AspNet/DotBased.Authority/Repositories/UserRepository.md" + }, + "icon": "lucide-list", + "title": "Outline of UserRepository" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Open quick switcher": false, + "graph:Open graph view": false, + "canvas:Create new canvas": false, + "daily-notes:Open today's daily note": false, + "templates:Insert template": false, + "command-palette:Open command palette": false + } + }, + "active": "9629cc68ecd8963f", + "lastOpenFiles": [ + "Modules/AspNet/DotBased.Authority/Repository.md", + "Modules/AspNet/DotBased.Authority/Repositories/UserRepository.md", + "Modules/AspNet/DotBased.Authority/Repositories", + "Modules/AspNet/DotBased.Authority/Data diagram.canvas", + "Modules/AspNet/DotBased.Authority/Models/AuthorityAttribute.md", + "Modules/AspNet/DotBased.Authority/Models/AuthorityUser.md", + "Modules/AspNet/DotBased.Authority/Models/AuthorityRole.md", + "Modules/AspNet/DotBased.Authority/Models/AuthorityGroup.md", + "Modules/AspNet/DotBased.Authority/Models", + "Untitled.canvas", + "Modules/AspNet/DotBased.Authority.md", + "Modules/AspNet/DotBased.Authority", + "DotBased.md", + "Modules/Untitled", + "Modules/DotBased.Data", + "Modules/AspNet", + "Modules", + "Welcome.md" + ] +} \ No newline at end of file diff --git a/obs_DotBased/Modules/AspNet/DotBased.Authority/Data diagram.canvas b/obs_DotBased/Modules/AspNet/DotBased.Authority/Data diagram.canvas new file mode 100644 index 0000000..f1b2fb6 --- /dev/null +++ b/obs_DotBased/Modules/AspNet/DotBased.Authority/Data diagram.canvas @@ -0,0 +1,22 @@ +{ + "nodes":[ + {"id":"d06f84f5cb83d5b3","type":"file","file":"Modules/AspNet/DotBased.Authority/Models/AuthorityUser.md","x":-660,"y":-740,"width":400,"height":400}, + {"id":"20469f32c2b97d54","type":"file","file":"Modules/AspNet/DotBased.Authority/Models/AuthorityGroup.md","x":-180,"y":-740,"width":400,"height":400}, + {"id":"54394f02266f386b","x":-180,"y":-1480,"width":400,"height":400,"type":"file","file":"Modules/AspNet/DotBased.Authority/Models/AuthorityAttribute.md"}, + {"id":"e38f59f13e522e10","x":-340,"y":-190,"width":250,"height":60,"color":"5","type":"text","text":"#### UsersGroups"}, + {"id":"cfabf37254bf9b94","x":-340,"y":-40,"width":250,"height":60,"color":"5","type":"text","text":"#### UserRoles"}, + {"id":"d306221d0fc3815f","x":300,"y":-740,"width":400,"height":400,"type":"file","file":"Modules/AspNet/DotBased.Authority/Models/AuthorityRole.md"}, + {"id":"3c9bc17b3a311b43","x":140,"y":-190,"width":250,"height":60,"color":"5","type":"text","text":"#### GroupsRoles"} + ], + "edges":[ + {"id":"591458f8b454aec0","fromNode":"d06f84f5cb83d5b3","fromSide":"bottom","toNode":"e38f59f13e522e10","toSide":"left"}, + {"id":"a5758a4bbb7fe559","fromNode":"20469f32c2b97d54","fromSide":"bottom","toNode":"e38f59f13e522e10","toSide":"right"}, + {"id":"86bf0eb2ae0fcfdc","fromNode":"d06f84f5cb83d5b3","fromSide":"bottom","toNode":"cfabf37254bf9b94","toSide":"left"}, + {"id":"64a58417d746183f","fromNode":"d306221d0fc3815f","fromSide":"bottom","toNode":"cfabf37254bf9b94","toSide":"right"}, + {"id":"e5825aa1415a34c4","fromNode":"20469f32c2b97d54","fromSide":"bottom","toNode":"3c9bc17b3a311b43","toSide":"left"}, + {"id":"0aa9af60a44a83dd","fromNode":"d306221d0fc3815f","fromSide":"bottom","toNode":"3c9bc17b3a311b43","toSide":"right"}, + {"id":"1958b5d5b82c10d7","fromNode":"54394f02266f386b","fromSide":"bottom","toNode":"d06f84f5cb83d5b3","toSide":"top"}, + {"id":"c71f1f3fc3e239f3","fromNode":"54394f02266f386b","fromSide":"bottom","toNode":"20469f32c2b97d54","toSide":"top"}, + {"id":"bd026c0a356725a5","fromNode":"54394f02266f386b","fromSide":"bottom","toNode":"d306221d0fc3815f","toSide":"top"} + ] +} \ No newline at end of file diff --git a/obs_DotBased/Modules/AspNet/DotBased.Authority/Models/AuthorityAttribute.md b/obs_DotBased/Modules/AspNet/DotBased.Authority/Models/AuthorityAttribute.md new file mode 100644 index 0000000..a3fa43b --- /dev/null +++ b/obs_DotBased/Modules/AspNet/DotBased.Authority/Models/AuthorityAttribute.md @@ -0,0 +1,2 @@ +Attributes to store some extra metadata. +One attribute per table entry. \ No newline at end of file diff --git a/obs_DotBased/Modules/AspNet/DotBased.Authority/Models/AuthorityGroup.md b/obs_DotBased/Modules/AspNet/DotBased.Authority/Models/AuthorityGroup.md new file mode 100644 index 0000000..0c4ca5a --- /dev/null +++ b/obs_DotBased/Modules/AspNet/DotBased.Authority/Models/AuthorityGroup.md @@ -0,0 +1,2 @@ +Groups can have multiple [[AuthorityUser]]s and holds [[AuthorityRole]]s . +Can be extended with extra data from [[AuthorityAttribute]]s \ No newline at end of file diff --git a/obs_DotBased/Modules/AspNet/DotBased.Authority/Models/AuthorityRole.md b/obs_DotBased/Modules/AspNet/DotBased.Authority/Models/AuthorityRole.md new file mode 100644 index 0000000..f2bf9f3 --- /dev/null +++ b/obs_DotBased/Modules/AspNet/DotBased.Authority/Models/AuthorityRole.md @@ -0,0 +1 @@ +Roles used for permissions and specific [[AuthorityAttribute]]s. Can get added to [[AuthorityUser]] and [[AuthorityRole]] \ No newline at end of file diff --git a/obs_DotBased/Modules/AspNet/DotBased.Authority/Models/AuthorityUser.md b/obs_DotBased/Modules/AspNet/DotBased.Authority/Models/AuthorityUser.md new file mode 100644 index 0000000..2a576e0 --- /dev/null +++ b/obs_DotBased/Modules/AspNet/DotBased.Authority/Models/AuthorityUser.md @@ -0,0 +1 @@ +An user can be in multiple [[AuthorityGroup]]s, can have own [[AuthorityRole]]s and [[AuthorityAttribute]]s. \ No newline at end of file diff --git a/obs_DotBased/Modules/AspNet/DotBased.Authority/Repositories/UserRepository.md b/obs_DotBased/Modules/AspNet/DotBased.Authority/Repositories/UserRepository.md new file mode 100644 index 0000000..62f8751 --- /dev/null +++ b/obs_DotBased/Modules/AspNet/DotBased.Authority/Repositories/UserRepository.md @@ -0,0 +1,6 @@ +Handles the db for user models. + +## GetUsers (list) +- Search +- Paging (limit, offset) +- Returns useritem \ No newline at end of file diff --git a/obs_DotBased/Modules/AspNet/DotBased.Authority/Repository.md b/obs_DotBased/Modules/AspNet/DotBased.Authority/Repository.md new file mode 100644 index 0000000..e69de29 From e914023c5a545b69415b050586cb57347bcdfca9 Mon Sep 17 00:00:00 2001 From: max Date: Mon, 27 Jan 2025 23:15:23 +0100 Subject: [PATCH 17/24] [ADD] Added EF Core project for authority db --- .../AuthorityContext.cs | 12 ++++++++++++ .../DotBased.AspNet.Authority.EFCore.csproj | 17 +++++++++++++++++ .../Repositories/RoleRepository.cs | 12 ++++++++++++ .../Repositories/IRoleRepository.cs | 4 +++- DotBased.sln | 7 +++++++ DotBased/Result.cs | 16 +++++++++++++--- 6 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 DotBased.AspNet.Authority.EFCore/AuthorityContext.cs create mode 100644 DotBased.AspNet.Authority.EFCore/DotBased.AspNet.Authority.EFCore.csproj create mode 100644 DotBased.AspNet.Authority.EFCore/Repositories/RoleRepository.cs diff --git a/DotBased.AspNet.Authority.EFCore/AuthorityContext.cs b/DotBased.AspNet.Authority.EFCore/AuthorityContext.cs new file mode 100644 index 0000000..fc470a6 --- /dev/null +++ b/DotBased.AspNet.Authority.EFCore/AuthorityContext.cs @@ -0,0 +1,12 @@ +using DotBased.AspNet.Authority.Models.Authority; +using Microsoft.EntityFrameworkCore; + +namespace DotBased.AspNet.Authority.EFCore; + +public class AuthorityContext : DbContext +{ + public DbSet Attributes { get; set; } + public DbSet Groups { get; set; } + public DbSet Roles { get; set; } + public DbSet Users { get; set; } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority.EFCore/DotBased.AspNet.Authority.EFCore.csproj b/DotBased.AspNet.Authority.EFCore/DotBased.AspNet.Authority.EFCore.csproj new file mode 100644 index 0000000..56e1802 --- /dev/null +++ b/DotBased.AspNet.Authority.EFCore/DotBased.AspNet.Authority.EFCore.csproj @@ -0,0 +1,17 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + diff --git a/DotBased.AspNet.Authority.EFCore/Repositories/RoleRepository.cs b/DotBased.AspNet.Authority.EFCore/Repositories/RoleRepository.cs new file mode 100644 index 0000000..6a1b1d8 --- /dev/null +++ b/DotBased.AspNet.Authority.EFCore/Repositories/RoleRepository.cs @@ -0,0 +1,12 @@ +using DotBased.AspNet.Authority.Models.Authority; +using DotBased.AspNet.Authority.Repositories; + +namespace DotBased.AspNet.Authority.EFCore.Repositories; + +public class RoleRepository : IRoleRepository +{ + public Task> GetRolesAsync(int limit = 20, int offset = 0, string search = "", CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs b/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs index 436076b..69ab90d 100755 --- a/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs @@ -1,6 +1,8 @@ +using DotBased.AspNet.Authority.Models.Authority; + namespace DotBased.AspNet.Authority.Repositories; public interface IRoleRepository { - + public Task> GetRolesAsync(int limit = 20, int offset = 0, string search = "", CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/DotBased.sln b/DotBased.sln index b867aa7..bdaa262 100755 --- a/DotBased.sln +++ b/DotBased.sln @@ -24,6 +24,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotBased.AspNet.Authority", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotBased.Data", "DotBased.Data\DotBased.Data.csproj", "{2DF9FEEF-5A60-4B41-9B5F-F883DCE33EF4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotBased.AspNet.Authority.EFCore", "DotBased.AspNet.Authority.EFCore\DotBased.AspNet.Authority.EFCore.csproj", "{F1F3F60B-911F-4036-8A2B-CEC18A8F59DD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -66,6 +68,10 @@ Global {2DF9FEEF-5A60-4B41-9B5F-F883DCE33EF4}.Debug|Any CPU.Build.0 = Debug|Any CPU {2DF9FEEF-5A60-4B41-9B5F-F883DCE33EF4}.Release|Any CPU.ActiveCfg = Release|Any CPU {2DF9FEEF-5A60-4B41-9B5F-F883DCE33EF4}.Release|Any CPU.Build.0 = Release|Any CPU + {F1F3F60B-911F-4036-8A2B-CEC18A8F59DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1F3F60B-911F-4036-8A2B-CEC18A8F59DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1F3F60B-911F-4036-8A2B-CEC18A8F59DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1F3F60B-911F-4036-8A2B-CEC18A8F59DD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {EBBDAF9A-BFC7-4BDC-8C51-0501B59A1DDC} = {2156FB93-C252-4B33-8A0C-73C82FABB163} @@ -76,5 +82,6 @@ Global {624E7B11-8A18-46E5-AB1F-6AF6097F9D4D} = {2156FB93-C252-4B33-8A0C-73C82FABB163} {A3ADC9AF-39B7-4EC4-8022-946118A8C322} = {624E7B11-8A18-46E5-AB1F-6AF6097F9D4D} {2DF9FEEF-5A60-4B41-9B5F-F883DCE33EF4} = {2156FB93-C252-4B33-8A0C-73C82FABB163} + {F1F3F60B-911F-4036-8A2B-CEC18A8F59DD} = {624E7B11-8A18-46E5-AB1F-6AF6097F9D4D} EndGlobalSection EndGlobal diff --git a/DotBased/Result.cs b/DotBased/Result.cs index 49e8ba6..e188b77 100755 --- a/DotBased/Result.cs +++ b/DotBased/Result.cs @@ -47,7 +47,7 @@ public class Result : Result public class ListResult : Result { - public ListResult(bool success, string message, int totalCount, IEnumerable? items, Exception? exception) : base(success, message, exception) + public ListResult(bool success, string message, int totalCount, IEnumerable? items, int limit = -1, int offset = -1, Exception? exception = null) : base(success, message, exception) { Items = items != null ? new List(items) : new List(); TotalCount = totalCount; @@ -69,9 +69,19 @@ public class ListResult : Result /// public int TotalCount { get; } + /// + /// The limit this result contains + /// + public int Limit { get; } + + /// + /// The offset this result has the items from. + /// + public int Offset { get; } + public static ListResult Ok(IEnumerable items, int totalCount = -1) => - new(true, string.Empty, totalCount, items, null); + new(true, string.Empty, totalCount, items); public new static ListResult Failed(string message, Exception? exception = null) => - new(false, message, -1, null, exception); + new(false, message, -1, null); } \ No newline at end of file From 5b4509cac30418e78c889c65c2177de0c98095af Mon Sep 17 00:00:00 2001 From: max Date: Sat, 1 Feb 2025 01:02:27 +0100 Subject: [PATCH 18/24] [ADD] Added sqlite to test project, created di for ef core context. Reworked repositories to use result class. --- DotBased.AspNet.Authority.EFCore/DI.cs | 13 ++++++++ .../Repositories/AttributeRepository.cs | 33 +++++++++++++++++++ .../Repositories/GroupRepository.cs | 32 ++++++++++++++++++ .../Repositories/RoleRepository.cs | 22 ++++++++++++- .../Managers/AuthorityUserManager.cs | 18 +++++----- .../Authority/AuthorityAttributeItem.cs | 6 ++++ .../Models/Authority/AuthorityGroupItem.cs | 6 ++++ .../Models/Authority/AuthorityRoleItem.cs | 6 ++++ .../Models/Authority/AuthorityUserItem.cs | 6 ++++ .../Models/AuthorityResult.cs | 17 ++++++---- .../Repositories/IAttributeRepository.cs | 8 ++++- .../Repositories/IGroupRepository.cs | 8 ++++- .../Repositories/IRoleRepository.cs | 6 +++- .../Repositories/IUserRepository.cs | 21 ++++++------ TestWebApi/Program.cs | 7 +++- TestWebApi/TestWebApi.csproj | 2 ++ obs_DotBased/.obsidian/workspace.json | 18 ++++------ 17 files changed, 185 insertions(+), 44 deletions(-) create mode 100644 DotBased.AspNet.Authority.EFCore/DI.cs create mode 100644 DotBased.AspNet.Authority.EFCore/Repositories/AttributeRepository.cs create mode 100644 DotBased.AspNet.Authority.EFCore/Repositories/GroupRepository.cs create mode 100644 DotBased.AspNet.Authority/Models/Authority/AuthorityAttributeItem.cs create mode 100644 DotBased.AspNet.Authority/Models/Authority/AuthorityGroupItem.cs create mode 100644 DotBased.AspNet.Authority/Models/Authority/AuthorityRoleItem.cs create mode 100644 DotBased.AspNet.Authority/Models/Authority/AuthorityUserItem.cs diff --git a/DotBased.AspNet.Authority.EFCore/DI.cs b/DotBased.AspNet.Authority.EFCore/DI.cs new file mode 100644 index 0000000..f5baba7 --- /dev/null +++ b/DotBased.AspNet.Authority.EFCore/DI.cs @@ -0,0 +1,13 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace DotBased.AspNet.Authority.EFCore; + +public static class DI +{ + public static IServiceCollection AddAuthorityContext(this IServiceCollection services, Action options) + { + services.AddDbContextFactory(options); + return services; + } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority.EFCore/Repositories/AttributeRepository.cs b/DotBased.AspNet.Authority.EFCore/Repositories/AttributeRepository.cs new file mode 100644 index 0000000..438e75d --- /dev/null +++ b/DotBased.AspNet.Authority.EFCore/Repositories/AttributeRepository.cs @@ -0,0 +1,33 @@ +using DotBased.AspNet.Authority.Models.Authority; +using DotBased.AspNet.Authority.Repositories; + +namespace DotBased.AspNet.Authority.EFCore.Repositories; + +public class AttributeRepository : IAttributeRepository +{ + public Task> GetAttributesAsync(int limit = 20, int offset = 0, string search = "", + CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetAttributeByIdAsync(string id, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> CreateAttributeAsync(AuthorityAttribute attribute, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> UpdateAttributeAsync(AuthorityAttribute attribute, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task DeleteAttributeAsync(AuthorityAttribute attribute, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority.EFCore/Repositories/GroupRepository.cs b/DotBased.AspNet.Authority.EFCore/Repositories/GroupRepository.cs new file mode 100644 index 0000000..a6fb010 --- /dev/null +++ b/DotBased.AspNet.Authority.EFCore/Repositories/GroupRepository.cs @@ -0,0 +1,32 @@ +using DotBased.AspNet.Authority.Models.Authority; +using DotBased.AspNet.Authority.Repositories; + +namespace DotBased.AspNet.Authority.EFCore.Repositories; + +public class GroupRepository : IGroupRepository +{ + public Task> GetGroupsAsync(int limit = 20, int offset = 0, string search = "", CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetGroupByIdAsync(string id, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> CreateGroupAsync(AuthorityGroup group, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> UpdateGroupAsync(AuthorityGroup group, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task DeleteGroupAsync(AuthorityGroup group, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority.EFCore/Repositories/RoleRepository.cs b/DotBased.AspNet.Authority.EFCore/Repositories/RoleRepository.cs index 6a1b1d8..bd15ed7 100644 --- a/DotBased.AspNet.Authority.EFCore/Repositories/RoleRepository.cs +++ b/DotBased.AspNet.Authority.EFCore/Repositories/RoleRepository.cs @@ -5,7 +5,27 @@ namespace DotBased.AspNet.Authority.EFCore.Repositories; public class RoleRepository : IRoleRepository { - public Task> GetRolesAsync(int limit = 20, int offset = 0, string search = "", CancellationToken cancellationToken = default) + public Task> GetRolesAsync(int limit = 20, int offset = 0, string search = "", CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> GetRoleByIdAsync(string id, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> CreateRoleAsync(AuthorityRole role, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task> UpdateRoleAsync(AuthorityRole role, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public Task DeleteRoleAsync(AuthorityRole role, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } diff --git a/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs index 5b07cfc..363c49c 100755 --- a/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs @@ -34,10 +34,10 @@ public partial class AuthorityManager return errors.Count > 0 ? ValidationResult.Failed(errors) : ValidationResult.Ok(); } - public async Task> SearchUsersAsync(string query, int maxResults = 20, int offset = 0, CancellationToken? cancellationToken = null) + public async Task> SearchUsersAsync(string query, int maxResults = 20, int offset = 0, CancellationToken? cancellationToken = null) { - var searchResult = await UserRepository.GetAuthorityUsersAsync(query, maxResults, offset, cancellationToken); - return searchResult.Item1 == null ? ListResult.Failed("No results!") : ListResult.Ok(searchResult.Item1, searchResult.Item2); + var result = await UserRepository.GetAuthorityUsersAsync(maxResults, offset, query, cancellationToken); + return result; } public async Task> UpdatePasswordAsync(AuthorityUser user, string password, CancellationToken? cancellationToken = null) @@ -54,7 +54,7 @@ public partial class AuthorityManager user.SecurityVersion = GenerateVersion(); var updateResult = await UserRepository.UpdateUserAsync(user, cancellationToken); - return updateResult == null ? AuthorityResult.Error("Failed to save updates!") : AuthorityResult.Ok(updateResult); + return AuthorityResult.FromResult(updateResult); } public async Task> CreateUserAsync(AuthorityUser userModel, string password, CancellationToken? cancellationToken = null) @@ -75,19 +75,17 @@ public partial class AuthorityManager userModel.PasswordHash = hashedPassword; var userCreationResult = await UserRepository.CreateUserAsync(userModel, cancellationToken); - - return userCreationResult != null - ? AuthorityResult.Ok(userCreationResult) - : AuthorityResult.Error("Failed to create user in repository!"); + + return AuthorityResult.FromResult(userCreationResult); } public async Task> UpdateUserAsync(AuthorityUser model, CancellationToken? cancellationToken = null) { var updateResult = await UserRepository.UpdateUserAsync(model, cancellationToken); - return updateResult != null ? Result.Ok(updateResult) : Result.Failed("Failed to update user in repository!"); + return updateResult; } - public async Task DeleteUserAsync(AuthorityUser model, CancellationToken? cancellationToken = null) + public async Task DeleteUserAsync(AuthorityUser model, CancellationToken? cancellationToken = null) { var deleteResult = await UserRepository.DeleteUserAsync(model, cancellationToken); return deleteResult; diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityAttributeItem.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityAttributeItem.cs new file mode 100644 index 0000000..44a0211 --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityAttributeItem.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Models.Authority; + +public class AuthorityAttributeItem +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityGroupItem.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityGroupItem.cs new file mode 100644 index 0000000..2b9bbcc --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityGroupItem.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Models.Authority; + +public class AuthorityGroupItem +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityRoleItem.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityRoleItem.cs new file mode 100644 index 0000000..e285d5a --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityRoleItem.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Models.Authority; + +public class AuthorityRoleItem +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityUserItem.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityUserItem.cs new file mode 100644 index 0000000..6afe594 --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityUserItem.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Models.Authority; + +public class AuthorityUserItem +{ + +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/AuthorityResult.cs b/DotBased.AspNet.Authority/Models/AuthorityResult.cs index f6c6d35..3cb3340 100755 --- a/DotBased.AspNet.Authority/Models/AuthorityResult.cs +++ b/DotBased.AspNet.Authority/Models/AuthorityResult.cs @@ -2,20 +2,23 @@ using DotBased.AspNet.Authority.Models.Validation; namespace DotBased.AspNet.Authority.Models; -public class AuthorityResult +public class AuthorityResult : Result { - public AuthorityResult(bool success, string errorMessage = "", TResultValue? value = default, ResultFailReason reason = ResultFailReason.None, List? errors = null) + public static AuthorityResult FromResult(Result result) => new AuthorityResult(result); + + public AuthorityResult(Result result) : base(result) + { + Reason = ResultFailReason.Unknown; + } + + public AuthorityResult(bool success, string errorMessage = "", TResultValue? value = default, ResultFailReason reason = ResultFailReason.None, List? errors = null) : base(success, errorMessage, value, null) { Success = success; - ErrorMessage = errorMessage; + Message = errorMessage; Value = value; Reason = reason; ValidationErrors = errors; } - - public bool Success { get; } - public string ErrorMessage { get; } - public TResultValue? Value { get; } public ResultFailReason Reason { get; } public List? ValidationErrors { get; } diff --git a/DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs b/DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs index 7e22f88..7fcca43 100755 --- a/DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs @@ -1,6 +1,12 @@ +using DotBased.AspNet.Authority.Models.Authority; + namespace DotBased.AspNet.Authority.Repositories; public interface IAttributeRepository { - + public Task> GetAttributesAsync(int limit = 20, int offset = 0, string search = "", CancellationToken cancellationToken = default); + public Task> GetAttributeByIdAsync(string id, CancellationToken cancellationToken = default); + public Task> CreateAttributeAsync(AuthorityAttribute attribute, CancellationToken cancellationToken = default); + public Task> UpdateAttributeAsync(AuthorityAttribute attribute, CancellationToken cancellationToken = default); + public Task DeleteAttributeAsync(AuthorityAttribute attribute, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs b/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs index f16b7d2..f8eb385 100755 --- a/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs @@ -1,6 +1,12 @@ +using DotBased.AspNet.Authority.Models.Authority; + namespace DotBased.AspNet.Authority.Repositories; public interface IGroupRepository { - + public Task> GetGroupsAsync(int limit = 20, int offset = 0, string search = "", CancellationToken cancellationToken = default); + public Task> GetGroupByIdAsync(string id, CancellationToken cancellationToken = default); + public Task> CreateGroupAsync(AuthorityGroup group, CancellationToken cancellationToken = default); + public Task> UpdateGroupAsync(AuthorityGroup group, CancellationToken cancellationToken = default); + public Task DeleteGroupAsync(AuthorityGroup group, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs b/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs index 69ab90d..83cec83 100755 --- a/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs @@ -4,5 +4,9 @@ namespace DotBased.AspNet.Authority.Repositories; public interface IRoleRepository { - public Task> GetRolesAsync(int limit = 20, int offset = 0, string search = "", CancellationToken cancellationToken = default); + public Task> GetRolesAsync(int limit = 20, int offset = 0, string search = "", CancellationToken cancellationToken = default); + public Task> GetRoleByIdAsync(string id, CancellationToken cancellationToken = default); + public Task> CreateRoleAsync(AuthorityRole role, CancellationToken cancellationToken = default); + public Task> UpdateRoleAsync(AuthorityRole role, CancellationToken cancellationToken = default); + public Task DeleteRoleAsync(AuthorityRole role, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/IUserRepository.cs b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs index f86cdb5..ca15430 100755 --- a/DotBased.AspNet.Authority/Repositories/IUserRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs @@ -4,15 +4,14 @@ namespace DotBased.AspNet.Authority.Repositories; public interface IUserRepository { - public Task GetAuthorityUserByIdAsync(string id, CancellationToken? cancellationToken = null); - public Task GetAuthorityUserIdAsync(AuthorityUser user, CancellationToken? cancellationToken = null); - public Task?, int>> GetAuthorityUsersAsync(string query, int maxResults = 20, int offset = 0, CancellationToken? cancellationToken = null); - public Task GetAuthorityUserByEmailAsync(string email, CancellationToken? cancellationToken = null); - public Task SetVersionAsync(AuthorityUser user, long version, CancellationToken? cancellationToken = null); - public Task GetVersionAsync(AuthorityUser user, CancellationToken? cancellationToken = null); - public Task SetSecurityVersionAsync(AuthorityUser user, long version, CancellationToken? cancellationToken = null); - public Task GetSecurityVersionAsync(AuthorityUser user, CancellationToken? cancellationToken = null); - public Task CreateUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null); - public Task UpdateUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null); - public Task DeleteUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null); + public Task> GetAuthorityUsersAsync(int limit = 20, int offset = 0, string search = "", CancellationToken? cancellationToken = null); + public Task> GetAuthorityUserByIdAsync(string id, CancellationToken? cancellationToken = null); + public Task> CreateUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null); + public Task> UpdateUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null); + public Task DeleteUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null); + public Task> GetAuthorityUserByEmailAsync(string email, CancellationToken? cancellationToken = null); + public Task SetVersionAsync(AuthorityUser user, long version, CancellationToken? cancellationToken = null); + public Task> GetVersionAsync(AuthorityUser user, CancellationToken? cancellationToken = null); + public Task SetSecurityVersionAsync(AuthorityUser user, long version, CancellationToken? cancellationToken = null); + public Task> GetSecurityVersionAsync(AuthorityUser user, CancellationToken? cancellationToken = null); } \ No newline at end of file diff --git a/TestWebApi/Program.cs b/TestWebApi/Program.cs index 323ca0c..1c24460 100755 --- a/TestWebApi/Program.cs +++ b/TestWebApi/Program.cs @@ -1,7 +1,9 @@ using DotBased.AspNet.Authority; +using DotBased.AspNet.Authority.EFCore; using DotBased.Logging; using DotBased.Logging.MEL; using DotBased.Logging.Serilog; +using Microsoft.EntityFrameworkCore; using Serilog; using TestWebApi; using ILogger = Serilog.ILogger; @@ -19,7 +21,10 @@ LogService.AddLogAdapter(new BasedSerilogAdapter(serilogLogger)); builder.Logging.ClearProviders(); builder.Logging.AddDotBasedLoggerProvider(LogService.Options); - +builder.Services.AddAuthorityContext(options => +{ + options.UseSqlite("Data Source=dev-dotbased.db"); +}); builder.Services.AddAuthority(options => { diff --git a/TestWebApi/TestWebApi.csproj b/TestWebApi/TestWebApi.csproj index ef97892..6340445 100755 --- a/TestWebApi/TestWebApi.csproj +++ b/TestWebApi/TestWebApi.csproj @@ -8,11 +8,13 @@ + + diff --git a/obs_DotBased/.obsidian/workspace.json b/obs_DotBased/.obsidian/workspace.json index 7819a34..9e458b2 100644 --- a/obs_DotBased/.obsidian/workspace.json +++ b/obs_DotBased/.obsidian/workspace.json @@ -4,21 +4,17 @@ "type": "split", "children": [ { - "id": "68f3abbbf106a47b", + "id": "89533e49f06550fb", "type": "tabs", "children": [ { - "id": "9629cc68ecd8963f", + "id": "65943ca25b411f17", "type": "leaf", "state": { - "type": "markdown", - "state": { - "file": "Modules/AspNet/DotBased.Authority/Repositories/UserRepository.md", - "mode": "source", - "source": false - }, + "type": "empty", + "state": {}, "icon": "lucide-file", - "title": "UserRepository" + "title": "New tab" } } ] @@ -162,10 +158,10 @@ "command-palette:Open command palette": false } }, - "active": "9629cc68ecd8963f", + "active": "65943ca25b411f17", "lastOpenFiles": [ - "Modules/AspNet/DotBased.Authority/Repository.md", "Modules/AspNet/DotBased.Authority/Repositories/UserRepository.md", + "Modules/AspNet/DotBased.Authority/Repository.md", "Modules/AspNet/DotBased.Authority/Repositories", "Modules/AspNet/DotBased.Authority/Data diagram.canvas", "Modules/AspNet/DotBased.Authority/Models/AuthorityAttribute.md", From 0f6b2fec88bb311b9ef5d4d6390c760b62901910 Mon Sep 17 00:00:00 2001 From: max Date: Sun, 2 Feb 2025 01:06:36 +0100 Subject: [PATCH 19/24] DBContext & repository --- .../AuthorityContext.cs | 9 +++ DotBased.AspNet.Authority.EFCore/DI.cs | 6 ++ .../DotBased.AspNet.Authority.EFCore.csproj | 1 + .../Repositories/UserRepository.cs | 58 +++++++++++++++++++ .../Repositories/IUserRepository.cs | 2 +- .../Validators/UserValidator.cs | 2 +- 6 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 DotBased.AspNet.Authority.EFCore/Repositories/UserRepository.cs diff --git a/DotBased.AspNet.Authority.EFCore/AuthorityContext.cs b/DotBased.AspNet.Authority.EFCore/AuthorityContext.cs index fc470a6..5463940 100644 --- a/DotBased.AspNet.Authority.EFCore/AuthorityContext.cs +++ b/DotBased.AspNet.Authority.EFCore/AuthorityContext.cs @@ -9,4 +9,13 @@ public class AuthorityContext : DbContext public DbSet Groups { get; set; } public DbSet Roles { get; set; } public DbSet Users { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().ToTable("authority_attributes"); + modelBuilder.Entity().ToTable("authority_groups"); + modelBuilder.Entity().ToTable("authority_roles"); + modelBuilder.Entity().ToTable("authority_users"); + base.OnModelCreating(modelBuilder); + } } \ No newline at end of file diff --git a/DotBased.AspNet.Authority.EFCore/DI.cs b/DotBased.AspNet.Authority.EFCore/DI.cs index f5baba7..999df24 100644 --- a/DotBased.AspNet.Authority.EFCore/DI.cs +++ b/DotBased.AspNet.Authority.EFCore/DI.cs @@ -1,3 +1,5 @@ +using DotBased.AspNet.Authority.EFCore.Repositories; +using DotBased.AspNet.Authority.Repositories; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; @@ -8,6 +10,10 @@ public static class DI public static IServiceCollection AddAuthorityContext(this IServiceCollection services, Action options) { services.AddDbContextFactory(options); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); return services; } } \ No newline at end of file diff --git a/DotBased.AspNet.Authority.EFCore/DotBased.AspNet.Authority.EFCore.csproj b/DotBased.AspNet.Authority.EFCore/DotBased.AspNet.Authority.EFCore.csproj index 56e1802..64df6fe 100644 --- a/DotBased.AspNet.Authority.EFCore/DotBased.AspNet.Authority.EFCore.csproj +++ b/DotBased.AspNet.Authority.EFCore/DotBased.AspNet.Authority.EFCore.csproj @@ -12,6 +12,7 @@ + diff --git a/DotBased.AspNet.Authority.EFCore/Repositories/UserRepository.cs b/DotBased.AspNet.Authority.EFCore/Repositories/UserRepository.cs new file mode 100644 index 0000000..9e1542f --- /dev/null +++ b/DotBased.AspNet.Authority.EFCore/Repositories/UserRepository.cs @@ -0,0 +1,58 @@ +using DotBased.AspNet.Authority.Models.Authority; +using DotBased.AspNet.Authority.Repositories; + +namespace DotBased.AspNet.Authority.EFCore.Repositories; + +public class UserRepository : IUserRepository +{ + public Task> GetAuthorityUsersAsync(int limit = 20, int offset = 0, string search = "", + CancellationToken? cancellationToken = null) + { + throw new NotImplementedException(); + } + + public Task> GetAuthorityUserByIdAsync(string id, CancellationToken? cancellationToken = null) + { + throw new NotImplementedException(); + } + + public Task> CreateUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null) + { + throw new NotImplementedException(); + } + + public Task> UpdateUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null) + { + throw new NotImplementedException(); + } + + public Task DeleteUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null) + { + throw new NotImplementedException(); + } + + public Task> GetUserByEmailAsync(string email, CancellationToken? cancellationToken = null) + { + throw new NotImplementedException(); + } + + public Task SetVersionAsync(AuthorityUser user, long version, CancellationToken? cancellationToken = null) + { + throw new NotImplementedException(); + } + + public Task> GetVersionAsync(AuthorityUser user, CancellationToken? cancellationToken = null) + { + throw new NotImplementedException(); + } + + public Task SetSecurityVersionAsync(AuthorityUser user, long version, CancellationToken? cancellationToken = null) + { + throw new NotImplementedException(); + } + + public Task> GetSecurityVersionAsync(AuthorityUser user, CancellationToken? cancellationToken = null) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/IUserRepository.cs b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs index ca15430..c84448a 100755 --- a/DotBased.AspNet.Authority/Repositories/IUserRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs @@ -9,7 +9,7 @@ public interface IUserRepository public Task> CreateUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null); public Task> UpdateUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null); public Task DeleteUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null); - public Task> GetAuthorityUserByEmailAsync(string email, CancellationToken? cancellationToken = null); + public Task> GetUserByEmailAsync(string email, CancellationToken? cancellationToken = null); public Task SetVersionAsync(AuthorityUser user, long version, CancellationToken? cancellationToken = null); public Task> GetVersionAsync(AuthorityUser user, CancellationToken? cancellationToken = null); public Task SetSecurityVersionAsync(AuthorityUser user, long version, CancellationToken? cancellationToken = null); diff --git a/DotBased.AspNet.Authority/Validators/UserValidator.cs b/DotBased.AspNet.Authority/Validators/UserValidator.cs index 1b914bc..cb60dce 100755 --- a/DotBased.AspNet.Authority/Validators/UserValidator.cs +++ b/DotBased.AspNet.Authority/Validators/UserValidator.cs @@ -25,7 +25,7 @@ public class UserValidator : IUserValidator } else { - var userEmailResult = await manager.UserRepository.GetAuthorityUserByEmailAsync(user.EmailAddress); + var userEmailResult = await manager.UserRepository.GetUserByEmailAsync(user.EmailAddress); if (userEmailResult != null) { errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.EmailExists", From 2938e1311f7e80a4e9f942da72101d3a3984d0f5 Mon Sep 17 00:00:00 2001 From: max Date: Sun, 2 Feb 2025 23:33:00 +0100 Subject: [PATCH 20/24] [DB] DbContext relations --- .../AuthorityContext.cs | 32 ++++++++++++++++--- .../DotBased.AspNet.Authority.EFCore.csproj | 4 +++ DotBased.AspNet.Authority.EFCore/README.md | 23 +++++++++++++ .../DotBased.AspNet.Authority.csproj | 1 + .../Models/Authority/AuthorityAttribute.cs | 20 ++++-------- .../Models/Authority/AuthorityGroup.cs | 1 + .../Models/Authority/AuthorityRole.cs | 2 +- .../Models/Authority/AuthorityUser.cs | 2 ++ TestWebApi/Program.cs | 2 +- TestWebApi/TestWebApi.csproj | 6 +++- 10 files changed, 71 insertions(+), 22 deletions(-) create mode 100644 DotBased.AspNet.Authority.EFCore/README.md diff --git a/DotBased.AspNet.Authority.EFCore/AuthorityContext.cs b/DotBased.AspNet.Authority.EFCore/AuthorityContext.cs index 5463940..6a0f813 100644 --- a/DotBased.AspNet.Authority.EFCore/AuthorityContext.cs +++ b/DotBased.AspNet.Authority.EFCore/AuthorityContext.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore; namespace DotBased.AspNet.Authority.EFCore; -public class AuthorityContext : DbContext +public class AuthorityContext(DbContextOptions options) : DbContext(options) { public DbSet Attributes { get; set; } public DbSet Groups { get; set; } @@ -12,10 +12,32 @@ public class AuthorityContext : DbContext protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity().ToTable("authority_attributes"); - modelBuilder.Entity().ToTable("authority_groups"); - modelBuilder.Entity().ToTable("authority_roles"); - modelBuilder.Entity().ToTable("authority_users"); + modelBuilder.Entity(attributeEntity => + { + attributeEntity.ToTable("authority_attributes"); + attributeEntity.HasKey(a => new { a.BoundId, a.AttributeKey }); + }); + + modelBuilder.Entity(groupEntity => + { + groupEntity.ToTable("authority_groups"); + groupEntity.HasKey(x => x.Id); + groupEntity.HasMany(g => g.Attributes).WithOne().HasForeignKey(a => a.BoundId).OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity(roleEntity => + { + roleEntity.ToTable("authority_roles"); + roleEntity.HasKey(x => x.Id); + }); + + modelBuilder.Entity(userEntity => + { + userEntity.ToTable("authority_users"); + userEntity.HasKey(x => x.Id); + userEntity.HasMany(u => u.Attributes).WithOne().HasForeignKey(a => a.BoundId).OnDelete(DeleteBehavior.Cascade); + }); + base.OnModelCreating(modelBuilder); } } \ No newline at end of file diff --git a/DotBased.AspNet.Authority.EFCore/DotBased.AspNet.Authority.EFCore.csproj b/DotBased.AspNet.Authority.EFCore/DotBased.AspNet.Authority.EFCore.csproj index 64df6fe..ec7e8bd 100644 --- a/DotBased.AspNet.Authority.EFCore/DotBased.AspNet.Authority.EFCore.csproj +++ b/DotBased.AspNet.Authority.EFCore/DotBased.AspNet.Authority.EFCore.csproj @@ -12,6 +12,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/DotBased.AspNet.Authority.EFCore/README.md b/DotBased.AspNet.Authority.EFCore/README.md new file mode 100644 index 0000000..f15acc9 --- /dev/null +++ b/DotBased.AspNet.Authority.EFCore/README.md @@ -0,0 +1,23 @@ +# EF Core database + +## Add migration project +```csharp +options.UseSqlite("Data Source=dev-dotbased.db", c => c.MigrationsAssembly("PROJECT-NAME")); +``` + +## EF Tool + +Add migration +```shell +dotnet ef migrations add MIGRATION-NAME --project PROJECT-NAME +``` + +Remove migrations +```shell +dotnet ef migrations remove --project PROJECT-NAME +``` + +Update database +```shell +dotnet ef database update --project PROJECT-NAME +``` diff --git a/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj b/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj index 53d8b22..b0a8901 100755 --- a/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj +++ b/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj @@ -17,6 +17,7 @@ + diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs index 5bc4d6e..27c08ca 100755 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs @@ -1,26 +1,18 @@ namespace DotBased.AspNet.Authority.Models.Authority; -public class AuthorityAttribute +public class AuthorityAttribute(string attributeKey, Guid bound) { - public AuthorityAttribute(string attributeKey, string bound) + public AuthorityAttribute() : this(string.Empty, Guid.NewGuid()) { - AttributeKey = attributeKey; - BoundId = bound; } - public AuthorityAttribute() - { - AttributeKey = string.Empty; - BoundId = string.Empty; - } - - public string AttributeKey { get; set; } // ClaimType/Authority.attribute.enabled + public Guid BoundId { get; set; } = bound; - public string BoundId { get; set; } // Bound to User, Group, Role id + public string AttributeKey { get; set; } = attributeKey; - public object? AttributeValue { get; set; } + public string AttributeValue { get; set; } = string.Empty; - public string? Type { get; set; } // AspNet.Claim.Role/Property/Data.JSON, Data.Raw, Data.Base64 etc. + public string? Type { get; set; } public long Version { get; set; } } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs index c5474df..05f85c9 100755 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs @@ -20,4 +20,5 @@ public class AuthorityGroup public long Version { get; set; } public DateTime CreatedDate { get; set; } + public ICollection Attributes { get; set; } = []; } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs index f1ef51a..1e215aa 100755 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs @@ -1,6 +1,6 @@ namespace DotBased.AspNet.Authority.Models.Authority; -public abstract class AuthorityRole() +public class AuthorityRole() { public AuthorityRole(string name) : this() { diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs index bc90356..03214ca 100755 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs @@ -41,5 +41,7 @@ public class AuthorityUser() public bool PhoneNumberConfirmed { get; set; } + public ICollection Attributes { get; set; } = []; + public override string ToString() => UserName ?? EmailAddress ?? string.Empty; } \ No newline at end of file diff --git a/TestWebApi/Program.cs b/TestWebApi/Program.cs index 1c24460..4c03d50 100755 --- a/TestWebApi/Program.cs +++ b/TestWebApi/Program.cs @@ -23,7 +23,7 @@ builder.Logging.ClearProviders(); builder.Logging.AddDotBasedLoggerProvider(LogService.Options); builder.Services.AddAuthorityContext(options => { - options.UseSqlite("Data Source=dev-dotbased.db"); + options.UseSqlite("Data Source=dev-dotbased.db", c => c.MigrationsAssembly("TestWebApi")); }); builder.Services.AddAuthority(options => { diff --git a/TestWebApi/TestWebApi.csproj b/TestWebApi/TestWebApi.csproj index 6340445..673fe48 100755 --- a/TestWebApi/TestWebApi.csproj +++ b/TestWebApi/TestWebApi.csproj @@ -8,7 +8,11 @@ - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + From 65d625a30dc579e496616d4ba1e53f0ac3397732 Mon Sep 17 00:00:00 2001 From: max Date: Mon, 10 Feb 2025 02:11:35 +0100 Subject: [PATCH 21/24] [ADD] Join tables. Added attributes to role. --- .../AuthorityContext.cs | 24 +++++++++++++++++++ .../Models/RoleGroup.cs | 7 ++++++ .../Models/RoleUser.cs | 7 ++++++ .../Models/UserGroup.cs | 7 ++++++ .../Models/Authority/AuthorityGroup.cs | 16 ++++--------- .../Models/Authority/AuthorityRole.cs | 2 ++ 6 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 DotBased.AspNet.Authority.EFCore/Models/RoleGroup.cs create mode 100644 DotBased.AspNet.Authority.EFCore/Models/RoleUser.cs create mode 100644 DotBased.AspNet.Authority.EFCore/Models/UserGroup.cs diff --git a/DotBased.AspNet.Authority.EFCore/AuthorityContext.cs b/DotBased.AspNet.Authority.EFCore/AuthorityContext.cs index 6a0f813..57dbaa8 100644 --- a/DotBased.AspNet.Authority.EFCore/AuthorityContext.cs +++ b/DotBased.AspNet.Authority.EFCore/AuthorityContext.cs @@ -1,3 +1,4 @@ +using DotBased.AspNet.Authority.EFCore.Models; using DotBased.AspNet.Authority.Models.Authority; using Microsoft.EntityFrameworkCore; @@ -9,6 +10,10 @@ public class AuthorityContext(DbContextOptions options) : DbCo public DbSet Groups { get; set; } public DbSet Roles { get; set; } public DbSet Users { get; set; } + + public DbSet RoleGroup { get; set; } + public DbSet RoleUser { get; set; } + public DbSet UserGroup { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -29,6 +34,7 @@ public class AuthorityContext(DbContextOptions options) : DbCo { roleEntity.ToTable("authority_roles"); roleEntity.HasKey(x => x.Id); + roleEntity.HasMany(r => r.Attributes).WithOne().HasForeignKey(a => a.BoundId).OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(userEntity => @@ -37,6 +43,24 @@ public class AuthorityContext(DbContextOptions options) : DbCo userEntity.HasKey(x => x.Id); userEntity.HasMany(u => u.Attributes).WithOne().HasForeignKey(a => a.BoundId).OnDelete(DeleteBehavior.Cascade); }); + + modelBuilder.Entity(rgEntity => + { + rgEntity.ToTable("role_group"); + rgEntity.HasKey(rg => new { rg.RoleId, rg.GroupId }); + }); + + modelBuilder.Entity(ruEntity => + { + ruEntity.ToTable("role_user"); + ruEntity.HasKey(ru => new { ru.RoleId, ru.UserId }); + }); + + modelBuilder.Entity(ugEntity => + { + ugEntity.ToTable("user_group"); + ugEntity.HasKey(ug => new { ug.UserId, ug.GroupId }); + }); base.OnModelCreating(modelBuilder); } diff --git a/DotBased.AspNet.Authority.EFCore/Models/RoleGroup.cs b/DotBased.AspNet.Authority.EFCore/Models/RoleGroup.cs new file mode 100644 index 0000000..1afe9a7 --- /dev/null +++ b/DotBased.AspNet.Authority.EFCore/Models/RoleGroup.cs @@ -0,0 +1,7 @@ +namespace DotBased.AspNet.Authority.EFCore.Models; + +public class RoleGroup +{ + public Guid RoleId { get; set; } + public Guid GroupId { get; set; } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority.EFCore/Models/RoleUser.cs b/DotBased.AspNet.Authority.EFCore/Models/RoleUser.cs new file mode 100644 index 0000000..c4a0917 --- /dev/null +++ b/DotBased.AspNet.Authority.EFCore/Models/RoleUser.cs @@ -0,0 +1,7 @@ +namespace DotBased.AspNet.Authority.EFCore.Models; + +public class RoleUser +{ + public Guid RoleId { get; set; } + public Guid UserId { get; set; } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority.EFCore/Models/UserGroup.cs b/DotBased.AspNet.Authority.EFCore/Models/UserGroup.cs new file mode 100644 index 0000000..648b376 --- /dev/null +++ b/DotBased.AspNet.Authority.EFCore/Models/UserGroup.cs @@ -0,0 +1,7 @@ +namespace DotBased.AspNet.Authority.EFCore.Models; + +public class UserGroup +{ + public Guid UserId { get; set; } + public Guid GroupId { get; set; } +} \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs index 05f85c9..fdcae2f 100755 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs @@ -1,24 +1,18 @@ namespace DotBased.AspNet.Authority.Models.Authority; -public class AuthorityGroup +public class AuthorityGroup() { public AuthorityGroup(string name) : this() { Name = name; } - - public AuthorityGroup() - { - Id = Guid.NewGuid(); - CreatedDate = DateTime.Now; - } - - public Guid Id { get; set; } - + + public Guid Id { get; set; } = Guid.NewGuid(); + public string? Name { get; set; } public long Version { get; set; } - public DateTime CreatedDate { get; set; } + public DateTime CreatedDate { get; set; } = DateTime.Now; public ICollection Attributes { get; set; } = []; } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs index 1e215aa..d3efc45 100755 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs @@ -15,5 +15,7 @@ public class AuthorityRole() public DateTime CreatedDate { get; set; } = DateTime.Now; + public IEnumerable Attributes { get; set; } = []; + public override string ToString() => Name ?? string.Empty; } \ No newline at end of file From eef7cfb2b9f45f2f28d66e12d2010331ee50087f Mon Sep 17 00:00:00 2001 From: max Date: Mon, 10 Feb 2025 02:40:27 +0100 Subject: [PATCH 22/24] [ADD] Creating queries --- .../{DI.cs => Extensions.cs} | 2 +- .../Repositories/UserRepository.cs | 22 +++++++++++++++---- .../Models/Authority/AuthorityUser.cs | 17 ++++++++++++-- .../Models/Authority/AuthorityUserItem.cs | 5 ++++- DotBased/Result.cs | 2 +- 5 files changed, 39 insertions(+), 9 deletions(-) rename DotBased.AspNet.Authority.EFCore/{DI.cs => Extensions.cs} (95%) diff --git a/DotBased.AspNet.Authority.EFCore/DI.cs b/DotBased.AspNet.Authority.EFCore/Extensions.cs similarity index 95% rename from DotBased.AspNet.Authority.EFCore/DI.cs rename to DotBased.AspNet.Authority.EFCore/Extensions.cs index 999df24..d48aa30 100644 --- a/DotBased.AspNet.Authority.EFCore/DI.cs +++ b/DotBased.AspNet.Authority.EFCore/Extensions.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.DependencyInjection; namespace DotBased.AspNet.Authority.EFCore; -public static class DI +public static class Extensions { public static IServiceCollection AddAuthorityContext(this IServiceCollection services, Action options) { diff --git a/DotBased.AspNet.Authority.EFCore/Repositories/UserRepository.cs b/DotBased.AspNet.Authority.EFCore/Repositories/UserRepository.cs index 9e1542f..bcd8165 100644 --- a/DotBased.AspNet.Authority.EFCore/Repositories/UserRepository.cs +++ b/DotBased.AspNet.Authority.EFCore/Repositories/UserRepository.cs @@ -1,14 +1,28 @@ using DotBased.AspNet.Authority.Models.Authority; using DotBased.AspNet.Authority.Repositories; +using Microsoft.EntityFrameworkCore; namespace DotBased.AspNet.Authority.EFCore.Repositories; -public class UserRepository : IUserRepository +public class UserRepository(IDbContextFactory contextFactory) : IUserRepository { - public Task> GetAuthorityUsersAsync(int limit = 20, int offset = 0, string search = "", - CancellationToken? cancellationToken = null) + public async Task> GetAuthorityUsersAsync(int limit = 20, int offset = 0, string search = "", CancellationToken? cancellationToken = null) { - throw new NotImplementedException(); + await using var context = await contextFactory.CreateDbContextAsync(); + var query = context.Users.AsQueryable(); + if (!string.IsNullOrWhiteSpace(search)) + { + query = query.Where(u => $"{u.Id} {u.Name} {u.UserName} {u.EmailAddress} {u.PhoneNumber}".Contains(search, StringComparison.CurrentCultureIgnoreCase)); + } + var totalCount = query.Count(); + var selected = query.Skip(offset).Take(limit).Select(u => new AuthorityUserItem() + { + Id = u.Id, + UserName = u.UserName, + EmailAddress = u.EmailAddress, + PhoneNumber = u.PhoneNumber + }); + return ListResult.Ok(selected, totalCount, limit, offset); } public Task> GetAuthorityUserByIdAsync(string id, CancellationToken? cancellationToken = null) diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs index 03214ca..1d37fa1 100755 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs @@ -1,3 +1,4 @@ +using System.Text; using DotBased.AspNet.Authority.Attributes; namespace DotBased.AspNet.Authority.Models.Authority; @@ -19,7 +20,9 @@ public class AuthorityUser() public DateTime LockedDate { get; set; } - public string? UserName { get; set; } + public string UserName { get; set; } = string.Empty; + + public string Name { get; set; } = string.Empty; public string? PasswordHash { get; set; } @@ -43,5 +46,15 @@ public class AuthorityUser() public ICollection Attributes { get; set; } = []; - public override string ToString() => UserName ?? EmailAddress ?? string.Empty; + public override string ToString() + { + var strBuilder = new StringBuilder(); + strBuilder.Append(!string.IsNullOrWhiteSpace(Name) ? Name : UserName); + + if (string.IsNullOrWhiteSpace(EmailAddress)) return strBuilder.ToString(); + + strBuilder.Append(strBuilder.Length == 0 ? EmailAddress : $" ({EmailAddress})"); + + return strBuilder.ToString(); + } } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Models/Authority/AuthorityUserItem.cs b/DotBased.AspNet.Authority/Models/Authority/AuthorityUserItem.cs index 6afe594..61e116b 100644 --- a/DotBased.AspNet.Authority/Models/Authority/AuthorityUserItem.cs +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityUserItem.cs @@ -2,5 +2,8 @@ namespace DotBased.AspNet.Authority.Models.Authority; public class AuthorityUserItem { - + public Guid Id { get; set; } + public string UserName { get; set; } = string.Empty; + public string? EmailAddress { get; set; } = string.Empty; + public string? PhoneNumber { get; set; } = string.Empty; } \ No newline at end of file diff --git a/DotBased/Result.cs b/DotBased/Result.cs index e188b77..dcf1b28 100755 --- a/DotBased/Result.cs +++ b/DotBased/Result.cs @@ -79,7 +79,7 @@ public class ListResult : Result /// public int Offset { get; } - public static ListResult Ok(IEnumerable items, int totalCount = -1) => + public static ListResult Ok(IEnumerable items, int totalCount = -1, int limit = -1, int offset = -1) => new(true, string.Empty, totalCount, items); public new static ListResult Failed(string message, Exception? exception = null) => From 1f593a364bf32b23e7a872334e4b1f56246c2309 Mon Sep 17 00:00:00 2001 From: max Date: Mon, 10 Feb 2025 16:11:58 +0100 Subject: [PATCH 23/24] [CHANGE] updating queries && parameter update --- .../Repositories/UserRepository.cs | 150 +++++++++++++----- .../Managers/AuthorityUserManager.cs | 10 +- .../Repositories/IUserRepository.cs | 20 +-- DotBased/Result.cs | 5 + 4 files changed, 132 insertions(+), 53 deletions(-) diff --git a/DotBased.AspNet.Authority.EFCore/Repositories/UserRepository.cs b/DotBased.AspNet.Authority.EFCore/Repositories/UserRepository.cs index bcd8165..6a1dcf2 100644 --- a/DotBased.AspNet.Authority.EFCore/Repositories/UserRepository.cs +++ b/DotBased.AspNet.Authority.EFCore/Repositories/UserRepository.cs @@ -6,66 +6,140 @@ namespace DotBased.AspNet.Authority.EFCore.Repositories; public class UserRepository(IDbContextFactory contextFactory) : IUserRepository { - public async Task> GetAuthorityUsersAsync(int limit = 20, int offset = 0, string search = "", CancellationToken? cancellationToken = null) + public async Task> GetAuthorityUsersAsync(int limit = 20, int offset = 0, string search = "", CancellationToken cancellationToken = default) { - await using var context = await contextFactory.CreateDbContextAsync(); - var query = context.Users.AsQueryable(); - if (!string.IsNullOrWhiteSpace(search)) + try { - query = query.Where(u => $"{u.Id} {u.Name} {u.UserName} {u.EmailAddress} {u.PhoneNumber}".Contains(search, StringComparison.CurrentCultureIgnoreCase)); + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + var query = context.Users.AsQueryable(); + if (!string.IsNullOrWhiteSpace(search)) + { + query = query.Where(u => + $"{u.Id} {u.Name} {u.UserName} {u.EmailAddress} {u.PhoneNumber}".Contains(search, + StringComparison.CurrentCultureIgnoreCase)); + } + + var totalCount = query.Count(); + var selected = query.Skip(offset).Take(limit).Select(u => new AuthorityUserItem() + { + Id = u.Id, + UserName = u.UserName, + EmailAddress = u.EmailAddress, + PhoneNumber = u.PhoneNumber + }); + return ListResult.Ok(selected, totalCount, limit, offset); } - var totalCount = query.Count(); - var selected = query.Skip(offset).Take(limit).Select(u => new AuthorityUserItem() + catch (Exception e) { - Id = u.Id, - UserName = u.UserName, - EmailAddress = u.EmailAddress, - PhoneNumber = u.PhoneNumber - }); - return ListResult.Ok(selected, totalCount, limit, offset); + return ListResult.Failed("Failed to get users.", e); + } } - public Task> GetAuthorityUserByIdAsync(string id, CancellationToken? cancellationToken = null) + public async Task> GetAuthorityUserByIdAsync(string id, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + if (!Guid.TryParse(id, out var guid)) + { + return Result.Failed("Invalid id!"); + } + + var user = await context.Users.FirstOrDefaultAsync(u => u.Id == guid, cancellationToken: cancellationToken); + return Result.HandleResult(user, "User not found."); + } + catch (Exception e) + { + return Result.Failed("Failed to get user.", e); + } + } + + public async Task> CreateUserAsync(AuthorityUser user, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + if (user.Id == Guid.Empty) + { + return Result.Failed("Id cannot be empty!"); + } + var entity = context.Users.Add(user); + var saveResult = await context.SaveChangesAsync(cancellationToken); + return saveResult <= 0 ? Result.Failed("Failed to create user!") : Result.Ok(entity.Entity); + } + catch (Exception e) + { + return Result.Failed("Failed to create user.", e); + } + } + + public async Task> UpdateUserAsync(AuthorityUser user, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + var usr = context.Users.FirstOrDefault(u => u.Id == user.Id); + if (usr == null) + { + return Result.Failed("User not found!"); + } + + if (usr.Version != user.Version || usr.SecurityVersion != user.SecurityVersion) + { + return Result.Failed("Version validation failed!"); + } + + var entity = context.Users.Update(user); + var saveResult = await context.SaveChangesAsync(cancellationToken); + return saveResult <= 0 ? Result.Failed("Failed to save updated user!") : Result.Ok(entity.Entity); + } + catch (Exception e) + { + return Result.Failed("Failed to update user!", e); + } + } + + public async Task DeleteUserAsync(AuthorityUser user, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + var usr = context.Users.FirstOrDefault(u => u.Id == user.Id); + if (usr == null) + { + return Result.Failed("User not found!"); + } + context.Users.Remove(usr); + var saveResult = await context.SaveChangesAsync(cancellationToken); + return saveResult <= 0 ? Result.Failed("Failed to delete user!") : Result.Ok(); + } + catch (Exception e) + { + return Result.Failed("Failed to delete user!", e); + } + } + + public Task> GetUserByEmailAsync(string email, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } - public Task> CreateUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null) + public Task SetVersionAsync(AuthorityUser user, long version, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } - public Task> UpdateUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null) + public Task> GetVersionAsync(AuthorityUser user, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } - public Task DeleteUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null) + public Task SetSecurityVersionAsync(AuthorityUser user, long version, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } - public Task> GetUserByEmailAsync(string email, CancellationToken? cancellationToken = null) - { - throw new NotImplementedException(); - } - - public Task SetVersionAsync(AuthorityUser user, long version, CancellationToken? cancellationToken = null) - { - throw new NotImplementedException(); - } - - public Task> GetVersionAsync(AuthorityUser user, CancellationToken? cancellationToken = null) - { - throw new NotImplementedException(); - } - - public Task SetSecurityVersionAsync(AuthorityUser user, long version, CancellationToken? cancellationToken = null) - { - throw new NotImplementedException(); - } - - public Task> GetSecurityVersionAsync(AuthorityUser user, CancellationToken? cancellationToken = null) + public Task> GetSecurityVersionAsync(AuthorityUser user, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } diff --git a/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs index 363c49c..cd31157 100755 --- a/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs +++ b/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs @@ -34,13 +34,13 @@ public partial class AuthorityManager return errors.Count > 0 ? ValidationResult.Failed(errors) : ValidationResult.Ok(); } - public async Task> SearchUsersAsync(string query, int maxResults = 20, int offset = 0, CancellationToken? cancellationToken = null) + public async Task> SearchUsersAsync(string query, int maxResults = 20, int offset = 0, CancellationToken cancellationToken = default) { var result = await UserRepository.GetAuthorityUsersAsync(maxResults, offset, query, cancellationToken); return result; } - public async Task> UpdatePasswordAsync(AuthorityUser user, string password, CancellationToken? cancellationToken = null) + public async Task> UpdatePasswordAsync(AuthorityUser user, string password, CancellationToken cancellationToken = default) { var passwordValidation = await ValidatePasswordAsync(user, password); if (!passwordValidation.Success) @@ -57,7 +57,7 @@ public partial class AuthorityManager return AuthorityResult.FromResult(updateResult); } - public async Task> CreateUserAsync(AuthorityUser userModel, string password, CancellationToken? cancellationToken = null) + public async Task> CreateUserAsync(AuthorityUser userModel, string password, CancellationToken cancellationToken = default) { var userValidation = await ValidateUserAsync(userModel); var passwordValidation = await ValidatePasswordAsync(userModel, password); @@ -79,13 +79,13 @@ public partial class AuthorityManager return AuthorityResult.FromResult(userCreationResult); } - public async Task> UpdateUserAsync(AuthorityUser model, CancellationToken? cancellationToken = null) + public async Task> UpdateUserAsync(AuthorityUser model, CancellationToken cancellationToken = default) { var updateResult = await UserRepository.UpdateUserAsync(model, cancellationToken); return updateResult; } - public async Task DeleteUserAsync(AuthorityUser model, CancellationToken? cancellationToken = null) + public async Task DeleteUserAsync(AuthorityUser model, CancellationToken cancellationToken = default) { var deleteResult = await UserRepository.DeleteUserAsync(model, cancellationToken); return deleteResult; diff --git a/DotBased.AspNet.Authority/Repositories/IUserRepository.cs b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs index c84448a..01c4165 100755 --- a/DotBased.AspNet.Authority/Repositories/IUserRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs @@ -4,14 +4,14 @@ namespace DotBased.AspNet.Authority.Repositories; public interface IUserRepository { - public Task> GetAuthorityUsersAsync(int limit = 20, int offset = 0, string search = "", CancellationToken? cancellationToken = null); - public Task> GetAuthorityUserByIdAsync(string id, CancellationToken? cancellationToken = null); - public Task> CreateUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null); - public Task> UpdateUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null); - public Task DeleteUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null); - public Task> GetUserByEmailAsync(string email, CancellationToken? cancellationToken = null); - public Task SetVersionAsync(AuthorityUser user, long version, CancellationToken? cancellationToken = null); - public Task> GetVersionAsync(AuthorityUser user, CancellationToken? cancellationToken = null); - public Task SetSecurityVersionAsync(AuthorityUser user, long version, CancellationToken? cancellationToken = null); - public Task> GetSecurityVersionAsync(AuthorityUser user, CancellationToken? cancellationToken = null); + public Task> GetAuthorityUsersAsync(int limit = 20, int offset = 0, string search = "", CancellationToken cancellationToken = default); + public Task> GetAuthorityUserByIdAsync(string id, CancellationToken cancellationToken = default); + public Task> CreateUserAsync(AuthorityUser user, CancellationToken cancellationToken = default); + public Task> UpdateUserAsync(AuthorityUser user, CancellationToken cancellationToken = default); + public Task DeleteUserAsync(AuthorityUser user, CancellationToken cancellationToken = default); + public Task> GetUserByEmailAsync(string email, CancellationToken cancellationToken = default); + public Task SetVersionAsync(AuthorityUser user, long version, CancellationToken cancellationToken = default); + public Task> GetVersionAsync(AuthorityUser user, CancellationToken cancellationToken = default); + public Task SetSecurityVersionAsync(AuthorityUser user, long version, CancellationToken cancellationToken = default); + public Task> GetSecurityVersionAsync(AuthorityUser user, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/DotBased/Result.cs b/DotBased/Result.cs index dcf1b28..4669c28 100755 --- a/DotBased/Result.cs +++ b/DotBased/Result.cs @@ -43,6 +43,11 @@ public class Result : Result public new static Result Failed(string message, Exception? exception = null) => new(false, message, default, exception); + + public new static Result HandleResult(TValue? value, string failedMessage, Exception? exception = null) + { + return value == null ? Failed(failedMessage, exception) : Ok(value); + } } public class ListResult : Result From 6c67276dca7ea14bbecc0c0ed075bc987a3d94a0 Mon Sep 17 00:00:00 2001 From: max Date: Mon, 10 Feb 2025 16:25:25 +0100 Subject: [PATCH 24/24] [IMPL] Added base user repository implementation --- .../Repositories/UserRepository.cs | 95 ++++++++++++++++--- .../Repositories/IUserRepository.cs | 2 +- 2 files changed, 84 insertions(+), 13 deletions(-) diff --git a/DotBased.AspNet.Authority.EFCore/Repositories/UserRepository.cs b/DotBased.AspNet.Authority.EFCore/Repositories/UserRepository.cs index 6a1dcf2..599f5d0 100644 --- a/DotBased.AspNet.Authority.EFCore/Repositories/UserRepository.cs +++ b/DotBased.AspNet.Authority.EFCore/Repositories/UserRepository.cs @@ -78,7 +78,7 @@ public class UserRepository(IDbContextFactory contextFactory) try { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - var usr = context.Users.FirstOrDefault(u => u.Id == user.Id); + var usr = await context.Users.FirstOrDefaultAsync(u => u.Id == user.Id, cancellationToken: cancellationToken); if (usr == null) { return Result.Failed("User not found!"); @@ -104,7 +104,7 @@ public class UserRepository(IDbContextFactory contextFactory) try { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - var usr = context.Users.FirstOrDefault(u => u.Id == user.Id); + var usr = await context.Users.FirstOrDefaultAsync(u => u.Id == user.Id, cancellationToken: cancellationToken); if (usr == null) { return Result.Failed("User not found!"); @@ -119,28 +119,99 @@ public class UserRepository(IDbContextFactory contextFactory) } } - public Task> GetUserByEmailAsync(string email, CancellationToken cancellationToken = default) + public async Task> GetUserByEmailAsync(string email, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + var usr = await context.Users.FirstOrDefaultAsync(u => u.EmailAddress == email, cancellationToken: cancellationToken); + return Result.HandleResult(usr, "User not found by given email address."); + } + catch (Exception e) + { + return Result.Failed("An error occured while getting the user.", e); + } } - public Task SetVersionAsync(AuthorityUser user, long version, CancellationToken cancellationToken = default) + public async Task SetVersionAsync(AuthorityUser user, long version, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + var usr = await context.Users.FirstOrDefaultAsync(u => u.Id == user.Id, cancellationToken); + if (usr == null) + { + return Result.Failed("Failed to find user with given id!"); + } + + if (usr.Version != user.Version) + { + return Result.Failed("Stored user version doesn't match current user version!"); + } + + usr.Version = version; + context.Users.Update(usr); + var saveResult = await context.SaveChangesAsync(cancellationToken); + return saveResult <= 0 ? Result.Failed("Failed to update user!") : Result.Ok(); + } + catch (Exception e) + { + return Result.Failed("An error occured while updating the version.", e); + } } - public Task> GetVersionAsync(AuthorityUser user, CancellationToken cancellationToken = default) + public async Task> GetVersionAsync(AuthorityUser user, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + var usrVersion = await context.Users.Where(u => u.Id == user.Id).Select(u => u.Version).FirstOrDefaultAsync(cancellationToken); + return Result.HandleResult(usrVersion, "Failed to get user version!"); + } + catch (Exception e) + { + return Result.Failed("An error occured while getting the user version.", e); + } } - public Task SetSecurityVersionAsync(AuthorityUser user, long version, CancellationToken cancellationToken = default) + public async Task SetSecurityVersionAsync(AuthorityUser user, long securityVersion, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + var usr = await context.Users.FirstOrDefaultAsync(u => u.Id == user.Id, cancellationToken); + if (usr == null) + { + return Result.Failed("Failed to find user with given id!"); + } + + if (usr.SecurityVersion != user.SecurityVersion) + { + return Result.Failed("Stored user version doesn't match current user version!"); + } + + usr.SecurityVersion = securityVersion; + context.Users.Update(usr); + var saveResult = await context.SaveChangesAsync(cancellationToken); + return saveResult <= 0 ? Result.Failed("Failed to update user!") : Result.Ok(); + } + catch (Exception e) + { + return Result.Failed("An error occured while updating the security version.", e); + } } - public Task> GetSecurityVersionAsync(AuthorityUser user, CancellationToken cancellationToken = default) + public async Task> GetSecurityVersionAsync(AuthorityUser user, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + var usrVersion = await context.Users.Where(u => u.Id == user.Id).Select(u => u.SecurityVersion).FirstOrDefaultAsync(cancellationToken); + return Result.HandleResult(usrVersion, "Failed to get user security version!"); + } + catch (Exception e) + { + return Result.Failed("An error occured while getting the user security version.", e); + } } } \ No newline at end of file diff --git a/DotBased.AspNet.Authority/Repositories/IUserRepository.cs b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs index 01c4165..0107a76 100755 --- a/DotBased.AspNet.Authority/Repositories/IUserRepository.cs +++ b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs @@ -12,6 +12,6 @@ public interface IUserRepository public Task> GetUserByEmailAsync(string email, CancellationToken cancellationToken = default); public Task SetVersionAsync(AuthorityUser user, long version, CancellationToken cancellationToken = default); public Task> GetVersionAsync(AuthorityUser user, CancellationToken cancellationToken = default); - public Task SetSecurityVersionAsync(AuthorityUser user, long version, CancellationToken cancellationToken = default); + public Task SetSecurityVersionAsync(AuthorityUser user, long securityVersion, CancellationToken cancellationToken = default); public Task> GetSecurityVersionAsync(AuthorityUser user, CancellationToken cancellationToken = default); } \ No newline at end of file