mirror of
https://github.com/hmaxnl/DotBased.git
synced 2025-04-19 20:18:13 +02:00
Compare commits
15 Commits
39077ef0ee
...
2b76f79f24
Author | SHA1 | Date | |
---|---|---|---|
|
2b76f79f24 | ||
|
c27890a31f | ||
|
3ccd3106c1 | ||
|
fd733b7238 | ||
|
28fcd74acf | ||
|
90cd0a2828 | ||
|
12efc92ac4 | ||
|
efc8259930 | ||
|
2d96a25906 | ||
|
172d5838e7 | ||
|
ebfafa2f29 | ||
|
361af34036 | ||
|
5c4ebd2b32 | ||
|
797323789e | ||
|
44e64793b7 |
0
Blazor.Wasm/App.razor
Normal file → Executable file
0
Blazor.Wasm/App.razor
Normal file → Executable file
0
Blazor.Wasm/Blazor.Wasm.csproj
Normal file → Executable file
0
Blazor.Wasm/Blazor.Wasm.csproj
Normal file → Executable file
0
Blazor.Wasm/Layout/MainLayout.razor
Normal file → Executable file
0
Blazor.Wasm/Layout/MainLayout.razor
Normal file → Executable file
0
Blazor.Wasm/Layout/MainLayout.razor.css
Normal file → Executable file
0
Blazor.Wasm/Layout/MainLayout.razor.css
Normal file → Executable file
0
Blazor.Wasm/Layout/NavMenu.razor
Normal file → Executable file
0
Blazor.Wasm/Layout/NavMenu.razor
Normal file → Executable file
0
Blazor.Wasm/Layout/NavMenu.razor.css
Normal file → Executable file
0
Blazor.Wasm/Layout/NavMenu.razor.css
Normal file → Executable file
0
Blazor.Wasm/Pages/Counter.razor
Normal file → Executable file
0
Blazor.Wasm/Pages/Counter.razor
Normal file → Executable file
0
Blazor.Wasm/Pages/Home.razor
Normal file → Executable file
0
Blazor.Wasm/Pages/Home.razor
Normal file → Executable file
0
Blazor.Wasm/Pages/Weather.razor
Normal file → Executable file
0
Blazor.Wasm/Pages/Weather.razor
Normal file → Executable file
0
Blazor.Wasm/Program.cs
Normal file → Executable file
0
Blazor.Wasm/Program.cs
Normal file → Executable file
0
Blazor.Wasm/Properties/launchSettings.json
Normal file → Executable file
0
Blazor.Wasm/Properties/launchSettings.json
Normal file → Executable file
0
Blazor.Wasm/_Imports.razor
Normal file → Executable file
0
Blazor.Wasm/_Imports.razor
Normal file → Executable file
0
Blazor.Wasm/wwwroot/css/app.css
Normal file → Executable file
0
Blazor.Wasm/wwwroot/css/app.css
Normal file → Executable file
0
Blazor.Wasm/wwwroot/css/bootstrap/bootstrap.min.css
vendored
Normal file → Executable file
0
Blazor.Wasm/wwwroot/css/bootstrap/bootstrap.min.css
vendored
Normal file → Executable file
0
Blazor.Wasm/wwwroot/css/bootstrap/bootstrap.min.css.map
Normal file → Executable file
0
Blazor.Wasm/wwwroot/css/bootstrap/bootstrap.min.css.map
Normal file → Executable file
0
Blazor.Wasm/wwwroot/favicon.png
Normal file → Executable file
0
Blazor.Wasm/wwwroot/favicon.png
Normal file → Executable file
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
0
Blazor.Wasm/wwwroot/icon-192.png
Normal file → Executable file
0
Blazor.Wasm/wwwroot/icon-192.png
Normal file → Executable file
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
0
Blazor.Wasm/wwwroot/index.html
Normal file → Executable file
0
Blazor.Wasm/wwwroot/index.html
Normal file → Executable file
0
Blazor.Wasm/wwwroot/sample-data/weather.json
Normal file → Executable file
0
Blazor.Wasm/wwwroot/sample-data/weather.json
Normal file → Executable file
0
DotBased.ASP.Auth/AuthDataCache.cs
Normal file → Executable file
0
DotBased.ASP.Auth/AuthDataCache.cs
Normal file → Executable file
0
DotBased.ASP.Auth/AuthenticationService.cs
Normal file → Executable file
0
DotBased.ASP.Auth/AuthenticationService.cs
Normal file → Executable file
0
DotBased.ASP.Auth/BasedAuthConfiguration.cs
Normal file → Executable file
0
DotBased.ASP.Auth/BasedAuthConfiguration.cs
Normal file → Executable file
0
DotBased.ASP.Auth/BasedAuthDefaults.cs
Normal file → Executable file
0
DotBased.ASP.Auth/BasedAuthDefaults.cs
Normal file → Executable file
0
DotBased.ASP.Auth/BasedServerAuthenticationStateProvider.cs
Normal file → Executable file
0
DotBased.ASP.Auth/BasedServerAuthenticationStateProvider.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/Auth/AuthenticationStateModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/Auth/AuthenticationStateModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/Auth/PermissionModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/Auth/PermissionModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/Auth/RoleModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/Auth/RoleModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/Identity/GroupItemModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/Identity/GroupItemModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/Identity/GroupModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/Identity/GroupModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/Identity/UserItemModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/Identity/UserItemModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/Identity/UserModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/Identity/UserModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/LoginModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/LoginModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/RegisterModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Domains/RegisterModel.cs
Normal file → Executable file
0
DotBased.ASP.Auth/DotBased.ASP.Auth.csproj
Normal file → Executable file
0
DotBased.ASP.Auth/DotBased.ASP.Auth.csproj
Normal file → Executable file
0
DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs
Normal file → Executable file
0
DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs
Normal file → Executable file
0
DotBased.ASP.Auth/IAuthDataRepository.cs
Normal file → Executable file
0
DotBased.ASP.Auth/IAuthDataRepository.cs
Normal file → Executable file
0
DotBased.ASP.Auth/ISessionStateProvider.cs
Normal file → Executable file
0
DotBased.ASP.Auth/ISessionStateProvider.cs
Normal file → Executable file
0
DotBased.ASP.Auth/MemoryAuthDataRepository.cs
Normal file → Executable file
0
DotBased.ASP.Auth/MemoryAuthDataRepository.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Models/Configuration/AuthConfiguration.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Models/Configuration/AuthConfiguration.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Models/Configuration/CacheConfiguration.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Models/Configuration/CacheConfiguration.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Models/Configuration/LockoutConfiguration.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Models/Configuration/LockoutConfiguration.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Models/Configuration/PasswordConfiguration.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Models/Configuration/PasswordConfiguration.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Models/Configuration/ProviderConfiguration.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Models/Configuration/ProviderConfiguration.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Models/Configuration/RepositoryConfiguration.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Models/Configuration/RepositoryConfiguration.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Models/Configuration/UserConfiguration.cs
Normal file → Executable file
0
DotBased.ASP.Auth/Models/Configuration/UserConfiguration.cs
Normal file → Executable file
0
DotBased.ASP.Auth/SecurityManager.cs
Normal file → Executable file
0
DotBased.ASP.Auth/SecurityManager.cs
Normal file → Executable file
0
DotBased.ASP.Auth/SecurityService.cs
Normal file → Executable file
0
DotBased.ASP.Auth/SecurityService.cs
Normal file → Executable file
2
DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs
Normal file → Executable file
2
DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs
Normal file → Executable file
|
@ -1,7 +1,7 @@
|
||||||
namespace DotBased.AspNet.Authority.Attributes;
|
namespace DotBased.AspNet.Authority.Attributes;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that the property should be protected.
|
/// Indicates to protect the property before saving/loading to the repository.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public class ProtectAttribute : Attribute
|
public class ProtectAttribute : Attribute
|
||||||
|
|
0
DotBased.AspNet.Authority/AuthorityBuilder.cs
Normal file → Executable file
0
DotBased.AspNet.Authority/AuthorityBuilder.cs
Normal file → Executable file
0
DotBased.AspNet.Authority/AuthorityDefaults.cs
Normal file → Executable file
0
DotBased.AspNet.Authority/AuthorityDefaults.cs
Normal file → Executable file
46
DotBased.AspNet.Authority/AuthorityProviderExtensions.cs
Normal file → Executable file
46
DotBased.AspNet.Authority/AuthorityProviderExtensions.cs
Normal file → Executable file
|
@ -1,17 +1,57 @@
|
||||||
using DotBased.AspNet.Authority.Interfaces;
|
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;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
|
||||||
namespace DotBased.AspNet.Authority;
|
namespace DotBased.AspNet.Authority;
|
||||||
|
|
||||||
public static class AuthorityProviderExtensions
|
public static class AuthorityProviderExtensions
|
||||||
{
|
{
|
||||||
public static AuthorityBuilder AddAuthorityProvider<TModel>(this IServiceCollection services) where TModel : class
|
public static AuthorityBuilder AddAuthority(this IServiceCollection services, Action<AuthorityOptions>? optionsAction = null)
|
||||||
{
|
{
|
||||||
|
if (optionsAction != null)
|
||||||
|
{
|
||||||
|
services.AddOptions();
|
||||||
|
services.Configure<AuthorityOptions>(optionsAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
services.TryAddScoped<ICryptographer, Cryptographer>();
|
||||||
|
services.TryAddScoped<IPasswordHasher, PasswordHasher>();
|
||||||
|
services.TryAddScoped<IPasswordValidator, PasswordOptionsValidator>();
|
||||||
|
services.TryAddScoped<IPasswordValidator, PasswordEqualsValidator>();
|
||||||
|
services.TryAddScoped<IUserValidator, UserValidator>();
|
||||||
|
/*services.TryAddScoped<IEmailVerifier, EmailVerifier>();
|
||||||
|
services.TryAddScoped<IPhoneNumberVerifier, PhoneNumberVerifier>();
|
||||||
|
services.TryAddScoped<IUserVerifier, UserVerifier>();*/
|
||||||
|
services.TryAddScoped<AuthorityManager>();
|
||||||
return new AuthorityBuilder(services);
|
return new AuthorityBuilder(services);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AuthorityBuilder AddAuthorityStore<TStore>(this AuthorityBuilder authorityBuilder) where TStore : IAuthorityRepository
|
public static AuthorityBuilder AddAuthorityRepository<TRepository>(this AuthorityBuilder authorityBuilder) where TRepository : class
|
||||||
{
|
{
|
||||||
return authorityBuilder;
|
return authorityBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static AuthorityBuilder MapAuthorityEndpoints(this AuthorityBuilder builder)
|
||||||
|
{
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Type GetBaseGenericArgumentType<TModel>(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));
|
||||||
|
}
|
||||||
}
|
}
|
14
DotBased.AspNet.Authority/Crypto/Cryptographer.cs
Executable file
14
DotBased.AspNet.Authority/Crypto/Cryptographer.cs
Executable file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace DotBased.AspNet.Authority.Crypto;
|
||||||
|
|
||||||
|
public class Cryptographer : ICryptographer
|
||||||
|
{
|
||||||
|
public Task<string?> EncryptAsync(string data)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<string?> DecryptAsync(string data)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
7
DotBased.AspNet.Authority/Crypto/ICryptographer.cs
Executable file
7
DotBased.AspNet.Authority/Crypto/ICryptographer.cs
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace DotBased.AspNet.Authority.Crypto;
|
||||||
|
|
||||||
|
public interface ICryptographer
|
||||||
|
{
|
||||||
|
public Task<string?> EncryptAsync(string data);
|
||||||
|
public Task<string?> DecryptAsync(string data);
|
||||||
|
}
|
6
DotBased.AspNet.Authority/Crypto/IPasswordHasher.cs
Executable file
6
DotBased.AspNet.Authority/Crypto/IPasswordHasher.cs
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace DotBased.AspNet.Authority.Crypto;
|
||||||
|
|
||||||
|
public interface IPasswordHasher
|
||||||
|
{
|
||||||
|
public Task<string> HashPasswordAsync(string password);
|
||||||
|
}
|
9
DotBased.AspNet.Authority/Crypto/PasswordHasher.cs
Executable file
9
DotBased.AspNet.Authority/Crypto/PasswordHasher.cs
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace DotBased.AspNet.Authority.Crypto;
|
||||||
|
|
||||||
|
public class PasswordHasher : IPasswordHasher
|
||||||
|
{
|
||||||
|
public async Task<string> HashPasswordAsync(string password)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
6
DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj
Normal file → Executable file
6
DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj
Normal file → Executable file
|
@ -17,9 +17,11 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Authentication\" />
|
|
||||||
<Folder Include="Models\Security\" />
|
<Folder Include="Models\Security\" />
|
||||||
<Folder Include="Repositories\" />
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
namespace DotBased.AspNet.Authority.Interfaces;
|
|
||||||
|
|
||||||
public interface IAttributeRepository
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
namespace DotBased.AspNet.Authority.Interfaces;
|
|
||||||
|
|
||||||
public interface IAuthorityRepository
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
namespace DotBased.AspNet.Authority.Interfaces;
|
|
||||||
|
|
||||||
public interface IRoleRepository
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
namespace DotBased.AspNet.Authority.Interfaces;
|
|
||||||
|
|
||||||
public interface IUserRepository
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
10
DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs
Executable file
10
DotBased.AspNet.Authority/Managers/AuthorityGroupManager.cs
Executable file
|
@ -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
|
||||||
|
*/
|
||||||
|
}
|
97
DotBased.AspNet.Authority/Managers/AuthorityManager.cs
Executable file
97
DotBased.AspNet.Authority/Managers/AuthorityManager.cs
Executable file
|
@ -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<AuthorityOptions> options,
|
||||||
|
IServiceProvider services,
|
||||||
|
ICryptographer cryptographer,
|
||||||
|
IUserRepository userRepository,
|
||||||
|
IRoleRepository roleRepository,
|
||||||
|
IPasswordHasher passwordHasher)
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger = LogService.RegisterLogger<AuthorityManager>();
|
||||||
|
|
||||||
|
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<IPasswordValidator> PasswordValidators { get; } = [];
|
||||||
|
public IEnumerable<IUserValidator> UserValidators { get; } = [];
|
||||||
|
|
||||||
|
|
||||||
|
public long GenerateVersion() => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Protect or unprotect the properties with the <see cref="ProtectAttribute"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The data model</param>
|
||||||
|
/// <param name="protection">True for protect false for unprotect.</param>
|
||||||
|
/// <typeparam name="TModel">The class with the properties to protect.</typeparam>
|
||||||
|
public async Task HandlePropertyProtection<TModel>(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<TModel>(string propertyName)
|
||||||
|
{
|
||||||
|
var protectedProperties = GetProtectedProperties<TModel>();
|
||||||
|
var propertyFound = protectedProperties.Where(propInfo => propInfo.Name == propertyName);
|
||||||
|
return propertyFound.Any();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PropertyInfo> GetProtectedPropertiesValues<TModel>(TModel model)
|
||||||
|
{
|
||||||
|
var protectedProperties = GetProtectedProperties<TModel>();
|
||||||
|
return protectedProperties.Count != 0 ? protectedProperties : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PropertyInfo> GetProtectedProperties<TModel>()
|
||||||
|
=> typeof(TModel).GetProperties().Where(p => Attribute.IsDefined(p, typeof(ProtectAttribute))).ToList();
|
||||||
|
}
|
65
DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs
Executable file
65
DotBased.AspNet.Authority/Managers/AuthorityRoleManager.cs
Executable file
|
@ -0,0 +1,65 @@
|
||||||
|
using DotBased.AspNet.Authority.Models.Authority;
|
||||||
|
|
||||||
|
namespace DotBased.AspNet.Authority.Managers;
|
||||||
|
|
||||||
|
public partial class AuthorityManager
|
||||||
|
{
|
||||||
|
public async Task<Result<AuthorityRole>> CreateRoleAsync(AuthorityRole role, CancellationToken? cancellationToken = null)
|
||||||
|
{
|
||||||
|
return Result<AuthorityRole>.Failed("Not implemented!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Result> DeleteRoleAsync(AuthorityRole role, CancellationToken? cancellationToken = null)
|
||||||
|
{
|
||||||
|
return Result.Failed("Not implemented!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Result<AuthorityRole>> UpdateRoleAsync(AuthorityRole role, CancellationToken? cancellationToken = null)
|
||||||
|
{
|
||||||
|
return Result<AuthorityRole>.Failed("Not implemented!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ListResult<AuthorityRole>> 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<AuthorityRole>.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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all roles (including group roles) that the user has.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The user to get the roles from</param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
public async Task<ListResult<AuthorityRole>> 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<AuthorityRole>.Failed("Not implemented!");
|
||||||
|
}
|
||||||
|
}
|
97
DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs
Executable file
97
DotBased.AspNet.Authority/Managers/AuthorityUserManager.cs
Executable file
|
@ -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<ValidationResult> ValidatePasswordAsync(AuthorityUser user, string password)
|
||||||
|
{
|
||||||
|
List<ValidationError> 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<ValidationResult> ValidateUserAsync(AuthorityUser user)
|
||||||
|
{
|
||||||
|
List<ValidationError> 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<ListResult<AuthorityUser>> 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<AuthorityUser>.Failed("No results!") : ListResult<AuthorityUser>.Ok(searchResult.Item1, searchResult.Item2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<AuthorityResult<AuthorityUser>> UpdatePasswordAsync(AuthorityUser user, string password, CancellationToken? cancellationToken = null)
|
||||||
|
{
|
||||||
|
var passwordValidation = await ValidatePasswordAsync(user, password);
|
||||||
|
if (!passwordValidation.Success)
|
||||||
|
{
|
||||||
|
List<ValidationError> errors = [];
|
||||||
|
errors.AddRange(passwordValidation.Errors);
|
||||||
|
return AuthorityResult<AuthorityUser>.Failed(errors, ResultFailReason.Validation);
|
||||||
|
}
|
||||||
|
|
||||||
|
user.PasswordHash = await PasswordHasher.HashPasswordAsync(password);
|
||||||
|
user.SecurityVersion = GenerateVersion();
|
||||||
|
|
||||||
|
var updateResult = await UserRepository.UpdateUserAsync(user, cancellationToken);
|
||||||
|
return updateResult == null ? AuthorityResult<AuthorityUser>.Error("Failed to save updates!") : AuthorityResult<AuthorityUser>.Ok(updateResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<AuthorityResult<AuthorityUser>> 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<ValidationError> errors = [];
|
||||||
|
errors.AddRange(userValidation.Errors);
|
||||||
|
errors.AddRange(passwordValidation.Errors);
|
||||||
|
return AuthorityResult<AuthorityUser>.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<AuthorityUser>.Ok(userCreationResult)
|
||||||
|
: AuthorityResult<AuthorityUser>.Error("Failed to create user in repository!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Result<AuthorityUser>> UpdateUserAsync(AuthorityUser model, CancellationToken? cancellationToken = null)
|
||||||
|
{
|
||||||
|
var updateResult = await UserRepository.UpdateUserAsync(model, cancellationToken);
|
||||||
|
return updateResult != null ? Result<AuthorityUser>.Ok(updateResult) : Result<AuthorityUser>.Failed("Failed to update user in repository!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> DeleteUserAsync(AuthorityUser model, CancellationToken? cancellationToken = null)
|
||||||
|
{
|
||||||
|
var deleteResult = await UserRepository.DeleteUserAsync(model, cancellationToken);
|
||||||
|
return deleteResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
26
DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs
Executable file
26
DotBased.AspNet.Authority/Models/Authority/AuthorityAttribute.cs
Executable file
|
@ -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; }
|
||||||
|
}
|
23
DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs
Executable file
23
DotBased.AspNet.Authority/Models/Authority/AuthorityGroup.cs
Executable file
|
@ -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; }
|
||||||
|
}
|
19
DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs
Executable file
19
DotBased.AspNet.Authority/Models/Authority/AuthorityRole.cs
Executable file
|
@ -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;
|
||||||
|
}
|
43
DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs
Normal file → Executable file
43
DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs
Normal file → Executable file
|
@ -1,10 +1,45 @@
|
||||||
|
using DotBased.AspNet.Authority.Attributes;
|
||||||
|
|
||||||
namespace DotBased.AspNet.Authority.Models.Authority;
|
namespace DotBased.AspNet.Authority.Models.Authority;
|
||||||
|
|
||||||
public class AuthorityUser : AuthorityUserBase<Guid>
|
public class AuthorityUser()
|
||||||
{
|
{
|
||||||
public AuthorityUser()
|
public AuthorityUser(string userName) : this()
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid();
|
UserName = userName;
|
||||||
CreatedDate = DateTime.Now;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
|
@ -1,35 +0,0 @@
|
||||||
using DotBased.AspNet.Authority.Attributes;
|
|
||||||
|
|
||||||
namespace DotBased.AspNet.Authority.Models.Authority;
|
|
||||||
|
|
||||||
public abstract class AuthorityUserBase<TKey> where TKey : IEquatable<TKey>
|
|
||||||
{
|
|
||||||
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; }
|
|
||||||
|
|
||||||
}
|
|
38
DotBased.AspNet.Authority/Models/AuthorityResult.cs
Executable file
38
DotBased.AspNet.Authority/Models/AuthorityResult.cs
Executable file
|
@ -0,0 +1,38 @@
|
||||||
|
using DotBased.AspNet.Authority.Models.Validation;
|
||||||
|
|
||||||
|
namespace DotBased.AspNet.Authority.Models;
|
||||||
|
|
||||||
|
public class AuthorityResult<TResultValue>
|
||||||
|
{
|
||||||
|
public AuthorityResult(bool success, string errorMessage = "", TResultValue? value = default, ResultFailReason reason = ResultFailReason.None, List<ValidationError>? 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<ValidationError>? ValidationErrors { get; }
|
||||||
|
|
||||||
|
|
||||||
|
public static AuthorityResult<TResultValue> Ok(TResultValue? value) => new AuthorityResult<TResultValue>(true, value:value);
|
||||||
|
|
||||||
|
public static AuthorityResult<TResultValue> Error(string errorMessage, ResultFailReason reason = ResultFailReason.Error) =>
|
||||||
|
new AuthorityResult<TResultValue>(false, errorMessage, reason:reason);
|
||||||
|
|
||||||
|
public static AuthorityResult<TResultValue> Failed(List<ValidationError> errors, ResultFailReason reason = ResultFailReason.None)
|
||||||
|
=> new AuthorityResult<TResultValue>(false, errors:errors, reason:reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ResultFailReason
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Unknown,
|
||||||
|
Validation,
|
||||||
|
Error
|
||||||
|
}
|
11
DotBased.AspNet.Authority/Models/Options/AuthorityOptions.cs
Executable file
11
DotBased.AspNet.Authority/Models/Options/AuthorityOptions.cs
Executable file
|
@ -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();
|
||||||
|
}
|
7
DotBased.AspNet.Authority/Models/Options/ListOption.cs
Executable file
7
DotBased.AspNet.Authority/Models/Options/ListOption.cs
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace DotBased.AspNet.Authority.Models.Options;
|
||||||
|
|
||||||
|
public enum ListOption
|
||||||
|
{
|
||||||
|
Blacklist,
|
||||||
|
Whitelist
|
||||||
|
}
|
6
DotBased.AspNet.Authority/Models/Options/LockdownOptions.cs
Executable file
6
DotBased.AspNet.Authority/Models/Options/LockdownOptions.cs
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace DotBased.AspNet.Authority.Models.Options;
|
||||||
|
|
||||||
|
public class LockdownOptions
|
||||||
|
{
|
||||||
|
public bool EnableLockout { get; set; }
|
||||||
|
}
|
8
DotBased.AspNet.Authority/Models/Options/LockoutOptions.cs
Executable file
8
DotBased.AspNet.Authority/Models/Options/LockoutOptions.cs
Executable file
|
@ -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);
|
||||||
|
}
|
14
DotBased.AspNet.Authority/Models/Options/PasswordOptions.cs
Executable file
14
DotBased.AspNet.Authority/Models/Options/PasswordOptions.cs
Executable file
|
@ -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<string> PasswordBlackList { get; set; } = ["password", "1234"];
|
||||||
|
public StringComparer PasswordBlackListComparer { get; set; } = StringComparer.OrdinalIgnoreCase;
|
||||||
|
}
|
6
DotBased.AspNet.Authority/Models/Options/ProviderOptions.cs
Executable file
6
DotBased.AspNet.Authority/Models/Options/ProviderOptions.cs
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace DotBased.AspNet.Authority.Models.Options;
|
||||||
|
|
||||||
|
public class ProviderOptions
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
10
DotBased.AspNet.Authority/Models/Options/RepositoryOptions.cs
Executable file
10
DotBased.AspNet.Authority/Models/Options/RepositoryOptions.cs
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace DotBased.AspNet.Authority.Models.Options;
|
||||||
|
|
||||||
|
public class RepositoryOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Use data encryption when a property has the <see cref="DotBased.AspNet.Authority.Attributes.ProtectAttribute"/> defined.
|
||||||
|
/// <value>Default: true</value>
|
||||||
|
/// </summary>
|
||||||
|
public bool UseDataProtection { get; set; } = true;
|
||||||
|
}
|
8
DotBased.AspNet.Authority/Models/Options/SignInOptions.cs
Executable file
8
DotBased.AspNet.Authority/Models/Options/SignInOptions.cs
Executable file
|
@ -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; }
|
||||||
|
}
|
12
DotBased.AspNet.Authority/Models/Options/UserOptions.cs
Executable file
12
DotBased.AspNet.Authority/Models/Options/UserOptions.cs
Executable file
|
@ -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<string> UserNameBlackList { get; set; } = ["admin", "administrator", "dev", "developer"];
|
||||||
|
public StringComparer UserNameBlackListComparer { get; set; } = StringComparer.OrdinalIgnoreCase;
|
||||||
|
}
|
24
DotBased.AspNet.Authority/Models/Validation/ValidationError.cs
Executable file
24
DotBased.AspNet.Authority/Models/Validation/ValidationError.cs
Executable file
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The validator name that generated this error.
|
||||||
|
/// </summary>
|
||||||
|
public string Validator { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// The error code
|
||||||
|
/// </summary>
|
||||||
|
public string ErrorCode { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// Error description
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; }
|
||||||
|
}
|
21
DotBased.AspNet.Authority/Models/Validation/ValidationResult.cs
Executable file
21
DotBased.AspNet.Authority/Models/Validation/ValidationResult.cs
Executable file
|
@ -0,0 +1,21 @@
|
||||||
|
namespace DotBased.AspNet.Authority.Models.Validation;
|
||||||
|
|
||||||
|
public class ValidationResult
|
||||||
|
{
|
||||||
|
public ValidationResult(bool success, IEnumerable<ValidationError>? errors = null)
|
||||||
|
{
|
||||||
|
if (errors != null)
|
||||||
|
{
|
||||||
|
Errors = errors.ToList();
|
||||||
|
}
|
||||||
|
Success = success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Success { get; }
|
||||||
|
public IReadOnlyList<ValidationError> Errors { get; } = [];
|
||||||
|
|
||||||
|
public static ValidationResult Failed(IEnumerable<ValidationError> errors) => new(false, errors);
|
||||||
|
public static ValidationResult Ok() => new(true);
|
||||||
|
|
||||||
|
public override string ToString() => Success ? "Success" : $"Failed ({Errors.Count} errors)";
|
||||||
|
}
|
6
DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs
Executable file
6
DotBased.AspNet.Authority/Repositories/IAttributeRepository.cs
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace DotBased.AspNet.Authority.Repositories;
|
||||||
|
|
||||||
|
public interface IAttributeRepository
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
6
DotBased.AspNet.Authority/Repositories/IGroupRepository.cs
Executable file
6
DotBased.AspNet.Authority/Repositories/IGroupRepository.cs
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace DotBased.AspNet.Authority.Repositories;
|
||||||
|
|
||||||
|
public interface IGroupRepository
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
6
DotBased.AspNet.Authority/Repositories/IRoleRepository.cs
Executable file
6
DotBased.AspNet.Authority/Repositories/IRoleRepository.cs
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace DotBased.AspNet.Authority.Repositories;
|
||||||
|
|
||||||
|
public interface IRoleRepository
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
18
DotBased.AspNet.Authority/Repositories/IUserRepository.cs
Executable file
18
DotBased.AspNet.Authority/Repositories/IUserRepository.cs
Executable file
|
@ -0,0 +1,18 @@
|
||||||
|
using DotBased.AspNet.Authority.Models.Authority;
|
||||||
|
|
||||||
|
namespace DotBased.AspNet.Authority.Repositories;
|
||||||
|
|
||||||
|
public interface IUserRepository
|
||||||
|
{
|
||||||
|
public Task<AuthorityUser?> GetAuthorityUserByIdAsync(string id, CancellationToken? cancellationToken = null);
|
||||||
|
public Task<string> GetAuthorityUserIdAsync(AuthorityUser user, CancellationToken? cancellationToken = null);
|
||||||
|
public Task<Tuple<List<AuthorityUser>?, int>> GetAuthorityUsersAsync(string query, int maxResults = 20, int offset = 0, CancellationToken? cancellationToken = null);
|
||||||
|
public Task<AuthorityUser?> GetAuthorityUserByEmailAsync(string email, CancellationToken? cancellationToken = null);
|
||||||
|
public Task SetVersionAsync(AuthorityUser user, long version, CancellationToken? cancellationToken = null);
|
||||||
|
public Task<long> GetVersionAsync(AuthorityUser user, CancellationToken? cancellationToken = null);
|
||||||
|
public Task SetSecurityVersionAsync(AuthorityUser user, long version, CancellationToken? cancellationToken = null);
|
||||||
|
public Task<long> GetSecurityVersionAsync(AuthorityUser user, CancellationToken? cancellationToken = null);
|
||||||
|
public Task<AuthorityUser?> CreateUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null);
|
||||||
|
public Task<AuthorityUser?> UpdateUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null);
|
||||||
|
public Task<bool> DeleteUserAsync(AuthorityUser user, CancellationToken? cancellationToken = null);
|
||||||
|
}
|
|
@ -1,6 +0,0 @@
|
||||||
namespace DotBased.AspNet.Authority.Services;
|
|
||||||
|
|
||||||
public class AuthorityService
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
10
DotBased.AspNet.Authority/Validators/IPasswordValidator.cs
Executable file
10
DotBased.AspNet.Authority/Validators/IPasswordValidator.cs
Executable file
|
@ -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<ValidationResult> ValidatePasswordAsync(AuthorityManager manager, AuthorityUser user, string password);
|
||||||
|
}
|
10
DotBased.AspNet.Authority/Validators/IUserValidator.cs
Executable file
10
DotBased.AspNet.Authority/Validators/IUserValidator.cs
Executable file
|
@ -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<ValidationResult> ValidateUserAsync(AuthorityManager manager, AuthorityUser user);
|
||||||
|
}
|
22
DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs
Executable file
22
DotBased.AspNet.Authority/Validators/PasswordEqualsValidator.cs
Executable file
|
@ -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<ValidationResult> ValidatePasswordAsync(AuthorityManager userManager, AuthorityUser user, string password)
|
||||||
|
{
|
||||||
|
List<ValidationError> 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();
|
||||||
|
}
|
||||||
|
}
|
66
DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs
Executable file
66
DotBased.AspNet.Authority/Validators/PasswordOptionsValidator.cs
Executable file
|
@ -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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates the password against the options that is configured.
|
||||||
|
/// </summary>
|
||||||
|
public class PasswordOptionsValidator : IPasswordValidator
|
||||||
|
{
|
||||||
|
private const string ValidatorId = "Authority.Validator.Password.Options";
|
||||||
|
private const string ValidationBase = "Authority.Validation.Password";
|
||||||
|
|
||||||
|
public async Task<ValidationResult> 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<ValidationError>();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
81
DotBased.AspNet.Authority/Validators/UserValidator.cs
Executable file
81
DotBased.AspNet.Authority/Validators/UserValidator.cs
Executable file
|
@ -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<ValidationResult> ValidateUserAsync(AuthorityManager manager, AuthorityUser user)
|
||||||
|
{
|
||||||
|
List<ValidationError> 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<char> 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();
|
||||||
|
}
|
||||||
|
}
|
6
DotBased.AspNet.Authority/Verifiers/IEmailVerifier.cs
Executable file
6
DotBased.AspNet.Authority/Verifiers/IEmailVerifier.cs
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace DotBased.AspNet.Authority.Verifiers;
|
||||||
|
|
||||||
|
public interface IEmailVerifier
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
6
DotBased.AspNet.Authority/Verifiers/IPhoneNumberVerifier.cs
Executable file
6
DotBased.AspNet.Authority/Verifiers/IPhoneNumberVerifier.cs
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace DotBased.AspNet.Authority.Verifiers;
|
||||||
|
|
||||||
|
public interface IPhoneNumberVerifier
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
6
DotBased.AspNet.Authority/Verifiers/IUserVerifier.cs
Executable file
6
DotBased.AspNet.Authority/Verifiers/IUserVerifier.cs
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace DotBased.AspNet.Authority.Verifiers;
|
||||||
|
|
||||||
|
public interface IUserVerifier
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
12
DotBased.Data/DotBased.Data.csproj
Normal file
12
DotBased.Data/DotBased.Data.csproj
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\DotBased\DotBased.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
0
DotBased.Logging.MEL/BasedLogger.cs
Normal file → Executable file
0
DotBased.Logging.MEL/BasedLogger.cs
Normal file → Executable file
0
DotBased.Logging.MEL/BasedLoggerProvider.cs
Normal file → Executable file
0
DotBased.Logging.MEL/BasedLoggerProvider.cs
Normal file → Executable file
0
DotBased.Logging.MEL/DotBased.Logging.MEL.csproj
Normal file → Executable file
0
DotBased.Logging.MEL/DotBased.Logging.MEL.csproj
Normal file → Executable file
0
DotBased.Logging.MEL/LoggerBuilderExtensions.cs
Normal file → Executable file
0
DotBased.Logging.MEL/LoggerBuilderExtensions.cs
Normal file → Executable file
0
DotBased.Logging.Serilog/BasedSerilog.cs
Normal file → Executable file
0
DotBased.Logging.Serilog/BasedSerilog.cs
Normal file → Executable file
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user