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 index 59fa9e0..b060926 --- 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.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.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 100755 index 0000000..1d1749e --- /dev/null +++ b/DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs @@ -0,0 +1,10 @@ +namespace DotBased.AspNet.Authority.Attributes; + +/// +/// Indicates to protect the property before saving/loading to the repository. +/// +[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 100755 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 100755 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 100755 index 0000000..9018d7a --- /dev/null +++ b/DotBased.AspNet.Authority/AuthorityProviderExtensions.cs @@ -0,0 +1,57 @@ +using DotBased.AspNet.Authority.Crypto; +using DotBased.AspNet.Authority.Managers; +using DotBased.AspNet.Authority.Models.Options; +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 AddAuthority(this IServiceCollection services, Action? optionsAction = null) + { + if (optionsAction != null) + { + services.AddOptions(); + services.Configure(optionsAction); + } + + services.TryAddScoped(); + services.TryAddScoped(); + services.TryAddScoped(); + services.TryAddScoped(); + services.TryAddScoped(); + /*services.TryAddScoped(); + services.TryAddScoped(); + services.TryAddScoped();*/ + services.TryAddScoped(); + return new AuthorityBuilder(services); + } + + public static AuthorityBuilder AddAuthorityRepository(this AuthorityBuilder authorityBuilder) where TRepository : class + { + return authorityBuilder; + } + + public static AuthorityBuilder MapAuthorityEndpoints(this AuthorityBuilder builder) + { + 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/Crypto/Cryptographer.cs b/DotBased.AspNet.Authority/Crypto/Cryptographer.cs new file mode 100755 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 100755 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 100755 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 100755 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.Auth/DotBased.AspNet.Auth.csproj b/DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj old mode 100644 new mode 100755 similarity index 64% rename from DotBased.AspNet.Auth/DotBased.AspNet.Auth.csproj rename to DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj index 3af943e..53d8b22 --- 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,11 @@ - - ..\..\..\..\..\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/Managers/AuthorityGroupManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs new file mode 100755 index 0000000..a91e65d --- /dev/null +++ b/DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs @@ -0,0 +1,10 @@ +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 new file mode 100755 index 0000000..a9f83f9 --- /dev/null +++ b/DotBased.AspNet.Authority/Managers/AuthorityManager.cs @@ -0,0 +1,97 @@ +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 partial class AuthorityManager( + IOptions options, + IServiceProvider services, + ICryptographer cryptographer, + IUserRepository userRepository, + IRoleRepository roleRepository, + IPasswordHasher passwordHasher) +{ + private readonly ILogger _logger = LogService.RegisterLogger(); + + 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 IEnumerable PasswordValidators { get; } = []; + public IEnumerable UserValidators { get; } = []; + + + public long GenerateVersion() => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + + + /// + /// Protect or unprotect the properties with the + /// + /// The data model + /// True for protect false for unprotect. + /// The class with the properties to protect. + public async Task HandlePropertyProtection(TModel data, bool protection) + { + var props = GetProtectedPropertiesValues(data); + 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 ? "Encryption" : "Decryption", property.Name); + continue; + } + property.SetValue(data, cryptString); + handledProperties++; + } + _logger.Debug("{HandledPropCount}/{TotalPropCount} protection properties handled!", handledProperties, props.Count); + } + + public bool IsPropertyProtected(string propertyName) + { + var protectedProperties = GetProtectedProperties(); + var propertyFound = protectedProperties.Where(propInfo => propInfo.Name == propertyName); + return propertyFound.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/Managers/AuthorityRoleManager.cs b/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs new file mode 100755 index 0000000..2b62e98 --- /dev/null +++ b/DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs @@ -0,0 +1,65 @@ +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> 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) + { + } + + 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 new file mode 100755 index 0000000..5b07cfc --- /dev/null +++ b/DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs @@ -0,0 +1,97 @@ +using DotBased.AspNet.Authority.Models; +using DotBased.AspNet.Authority.Models.Authority; +using DotBased.AspNet.Authority.Models.Validation; + +namespace DotBased.AspNet.Authority.Managers; + +public partial class AuthorityManager +{ + public async Task ValidatePasswordAsync(AuthorityUser 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(AuthorityUser 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> 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); + } + + public async Task> UpdatePasswordAsync(AuthorityUser user, string password, CancellationToken? cancellationToken = null) + { + var passwordValidation = await ValidatePasswordAsync(user, password); + if (!passwordValidation.Success) + { + List errors = []; + errors.AddRange(passwordValidation.Errors); + return AuthorityResult.Failed(errors, ResultFailReason.Validation); + } + + user.PasswordHash = await PasswordHasher.HashPasswordAsync(password); + user.SecurityVersion = GenerateVersion(); + + 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, CancellationToken? cancellationToken = null) + { + 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); + } + + userModel.Version = GenerateVersion(); + userModel.SecurityVersion = GenerateVersion(); + var hashedPassword = await PasswordHasher.HashPasswordAsync(password); + userModel.PasswordHash = hashedPassword; + + 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, CancellationToken? cancellationToken = null) + { + 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, CancellationToken? cancellationToken = null) + { + var deleteResult = await UserRepository.DeleteUserAsync(model, cancellationToken); + return deleteResult; + } + + +} \ 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 100755 index 0000000..5bc4d6e --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs @@ -0,0 +1,26 @@ +namespace DotBased.AspNet.Authority.Models.Authority; + +public class AuthorityAttribute +{ + public AuthorityAttribute(string attributeKey, string bound) + { + AttributeKey = attributeKey; + BoundId = bound; + } + + public AuthorityAttribute() + { + AttributeKey = string.Empty; + BoundId = string.Empty; + } + + public string AttributeKey { get; set; } // ClaimType/Authority.attribute.enabled + + public string BoundId { get; set; } // Bound to User, Group, Role id + + public object? 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 100755 index 0000000..c5474df --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs @@ -0,0 +1,23 @@ +namespace DotBased.AspNet.Authority.Models.Authority; + +public class AuthorityGroup +{ + public AuthorityGroup(string name) : this() + { + Name = name; + } + + public AuthorityGroup() + { + Id = Guid.NewGuid(); + CreatedDate = DateTime.Now; + } + + public Guid 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 100755 index 0000000..f1ef51a --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs @@ -0,0 +1,19 @@ +namespace DotBased.AspNet.Authority.Models.Authority; + +public abstract class AuthorityRole() +{ + public AuthorityRole(string name) : this() + { + Name = name; + } + + public Guid Id { get; set; } = Guid.NewGuid(); + + public string? Name { get; set; } + + public long Version { 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 new file mode 100755 index 0000000..bc90356 --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs @@ -0,0 +1,45 @@ +using DotBased.AspNet.Authority.Attributes; + +namespace DotBased.AspNet.Authority.Models.Authority; + +public class AuthorityUser() +{ + public AuthorityUser(string userName) : this() + { + UserName = userName; + } + + public Guid Id { get; set; } = Guid.NewGuid(); + + public bool Enabled { get; set; } + + public bool Confirmed { 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; } = DateTime.Now; + + 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/AuthorityResult.cs b/DotBased.AspNet.Authority/Models/AuthorityResult.cs new file mode 100755 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/AuthorityOptions.cs b/DotBased.AspNet.Authority/Models/Options/AuthorityOptions.cs new file mode 100755 index 0000000..6ab3e04 --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Options/AuthorityOptions.cs @@ -0,0 +1,11 @@ +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 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/ListOption.cs b/DotBased.AspNet.Authority/Models/Options/ListOption.cs new file mode 100755 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/LockdownOptions.cs b/DotBased.AspNet.Authority/Models/Options/LockdownOptions.cs new file mode 100755 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 100755 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 100755 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 100755 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/RepositoryOptions.cs b/DotBased.AspNet.Authority/Models/Options/RepositoryOptions.cs new file mode 100755 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/Options/SignInOptions.cs b/DotBased.AspNet.Authority/Models/Options/SignInOptions.cs new file mode 100755 index 0000000..8c142db --- /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 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/Models/Options/UserOptions.cs b/DotBased.AspNet.Authority/Models/Options/UserOptions.cs new file mode 100755 index 0000000..c483bab --- /dev/null +++ b/DotBased.AspNet.Authority/Models/Options/UserOptions.cs @@ -0,0 +1,12 @@ +namespace DotBased.AspNet.Authority.Models.Options; + +public class UserOptions +{ + public bool EnableRegister { get; set; } + public bool RequireUniqueEmail { get; set; } + 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; +} \ 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 100755 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 100755 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/IAttributeRepository.cs b/DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs new file mode 100755 index 0000000..7e22f88 --- /dev/null +++ b/DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Repositories; + +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 new file mode 100755 index 0000000..f16b7d2 --- /dev/null +++ b/DotBased.AspNet.Authority/Repositories/IGroupRepository.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Repositories; + +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 new file mode 100755 index 0000000..436076b --- /dev/null +++ b/DotBased.AspNet.Authority/Repositories/IRoleRepository.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Repositories; + +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 new file mode 100755 index 0000000..f86cdb5 --- /dev/null +++ b/DotBased.AspNet.Authority/Repositories/IUserRepository.cs @@ -0,0 +1,18 @@ +using DotBased.AspNet.Authority.Models.Authority; + +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); +} \ 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 100755 index 0000000..1208a21 --- /dev/null +++ b/DotBased.AspNet.Authority/Validators/IPasswordValidator.cs @@ -0,0 +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 +{ + 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 new file mode 100755 index 0000000..ef07650 --- /dev/null +++ b/DotBased.AspNet.Authority/Validators/IUserValidator.cs @@ -0,0 +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 +{ + 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 new file mode 100755 index 0000000..8278e13 --- /dev/null +++ b/DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs @@ -0,0 +1,22 @@ +using DotBased.AspNet.Authority.Managers; +using DotBased.AspNet.Authority.Models.Authority; +using DotBased.AspNet.Authority.Models.Validation; + +namespace DotBased.AspNet.Authority.Validators; + +public class PasswordEqualsValidator : IPasswordValidator +{ + private const string ValidatorId = "Authority.Validator.Password.Equals"; + private const string ValidationBase = "Authority.Validation.Password"; + public async Task ValidatePasswordAsync(AuthorityManager userManager, AuthorityUser user, string password) + { + List errors = []; + var hashedPassword = await userManager.PasswordHasher.HashPasswordAsync(password); + if (user.PasswordHash != null && user.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 new file mode 100755 index 0000000..41cab40 --- /dev/null +++ b/DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs @@ -0,0 +1,66 @@ +using DotBased.AspNet.Authority.Managers; +using DotBased.AspNet.Authority.Models.Authority; +using DotBased.AspNet.Authority.Models.Validation; +using DotBased.Extensions; + +namespace DotBased.AspNet.Authority.Validators; + +/// +/// Validates the password against the options that is configured. +/// +public class PasswordOptionsValidator : IPasswordValidator +{ + private const string ValidatorId = "Authority.Validator.Password.Options"; + private const string ValidationBase = "Authority.Validation.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.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/UserValidator.cs b/DotBased.AspNet.Authority/Validators/UserValidator.cs new file mode 100755 index 0000000..1b914bc --- /dev/null +++ b/DotBased.AspNet.Authority/Validators/UserValidator.cs @@ -0,0 +1,81 @@ +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 +{ + private const string ValidatorId = "Authority.Validator.User"; + private const string ValidationBase = "Authority.Validation.User"; + + public async Task ValidateUserAsync(AuthorityManager manager, AuthorityUser user) + { + List errors = []; + + var userOptions = manager.Options.User; + + if (userOptions.RequireUniqueEmail) + { + 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.GetAuthorityUserByEmailAsync(user.EmailAddress); + if (userEmailResult != null) + { + errors.Add(new ValidationError(ValidatorId, $"{ValidationBase}.EmailExists", + "Given email has already registered an account!")); + } + } + } + + if (!string.IsNullOrWhiteSpace(user.UserName)) + { + 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)")); + } + + if (!string.IsNullOrWhiteSpace(userOptions.UserNameCharacters)) + { + List chars = []; + if (userOptions.UserNameCharacterListType == ListOption.Whitelist) + { + chars.AddRange(user.UserName.Where(userNameChar => !userOptions.UserNameCharacters.Contains(userNameChar))); + } + if (userOptions.UserNameCharacterListType == ListOption.Blacklist) + { + chars.AddRange(user.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 diff --git a/DotBased.AspNet.Authority/Verifiers/IEmailVerifier.cs b/DotBased.AspNet.Authority/Verifiers/IEmailVerifier.cs new file mode 100755 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 100755 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 100755 index 0000000..d6e5120 --- /dev/null +++ b/DotBased.AspNet.Authority/Verifiers/IUserVerifier.cs @@ -0,0 +1,6 @@ +namespace DotBased.AspNet.Authority.Verifiers; + +public interface IUserVerifier +{ + +} \ No newline at end of file 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 4221777..b867aa7 100755 --- a/DotBased.sln +++ b/DotBased.sln @@ -20,7 +20,9 @@ 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 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotBased.Data", "DotBased.Data\DotBased.Data.csproj", "{2DF9FEEF-5A60-4B41-9B5F-F883DCE33EF4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -56,10 +58,14 @@ 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 + {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} @@ -68,6 +74,7 @@ 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} + {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 index 32c7f05..323ca0c --- a/TestWebApi/Program.cs +++ b/TestWebApi/Program.cs @@ -1,7 +1,9 @@ +using DotBased.AspNet.Authority; 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 +20,17 @@ 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; + 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 +38,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/Properties/launchSettings.json b/TestWebApi/Properties/launchSettings.json old mode 100644 new mode 100755 diff --git a/TestWebApi/SeedAuthorityData.cs b/TestWebApi/SeedAuthorityData.cs new file mode 100755 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 old mode 100644 new mode 100755 index c3c9828..ef97892 --- a/TestWebApi/TestWebApi.csproj +++ b/TestWebApi/TestWebApi.csproj @@ -13,6 +13,7 @@ + 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