mirror of
https://github.com/hmaxnl/DotBased.git
synced 2025-04-19 20:18:13 +02:00
Merge c27890a31f
into 21675300bb
This commit is contained in:
commit
2b76f79f24
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
2
DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs
Normal file → Executable file
2
DotBased.ASP.Auth/DotBasedAuthDependencyInjection.cs
Normal file → Executable file
|
@ -30,7 +30,7 @@ public static class DotBasedAuthDependencyInjection
|
||||||
services.AddAuthentication(options =>
|
services.AddAuthentication(options =>
|
||||||
{
|
{
|
||||||
options.DefaultScheme = BasedAuthDefaults.AuthenticationScheme;
|
options.DefaultScheme = BasedAuthDefaults.AuthenticationScheme;
|
||||||
});/*.AddScheme<BasedAuthenticationHandlerOptions, BasedAuthenticationHandler>(BasedAuthDefaults.AuthenticationScheme, null);*/
|
});
|
||||||
services.AddAuthorization();
|
services.AddAuthorization();
|
||||||
services.AddCascadingAuthenticationState();
|
services.AddCascadingAuthenticationState();
|
||||||
return services;
|
return services;
|
||||||
|
|
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
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
10
DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs
Executable file
10
DotBased.AspNet.Authority/Attributes/ProtectAttribute.cs
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace DotBased.AspNet.Authority.Attributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates to protect the property before saving/loading to the repository.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
|
public class ProtectAttribute : Attribute
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
13
DotBased.AspNet.Authority/AuthorityBuilder.cs
Executable file
13
DotBased.AspNet.Authority/AuthorityBuilder.cs
Executable file
|
@ -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; }
|
||||||
|
}
|
11
DotBased.AspNet.Authority/AuthorityDefaults.cs
Executable file
11
DotBased.AspNet.Authority/AuthorityDefaults.cs
Executable file
|
@ -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";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
57
DotBased.AspNet.Authority/AuthorityProviderExtensions.cs
Executable file
57
DotBased.AspNet.Authority/AuthorityProviderExtensions.cs
Executable file
|
@ -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<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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AuthorityBuilder AddAuthorityRepository<TRepository>(this AuthorityBuilder authorityBuilder) where TRepository : class
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
14
DotBased.AspNet.Auth/DotBased.AspNet.Auth.csproj → DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj
Normal file → Executable file
14
DotBased.AspNet.Auth/DotBased.AspNet.Auth.csproj → DotBased.AspNet.Authority/DotBased.AspNet.Authority.csproj
Normal file → Executable file
|
@ -7,8 +7,9 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Models\" />
|
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions">
|
||||||
<Folder Include="Repositories\" />
|
<HintPath>..\..\..\.nuget\packages\microsoft.extensions.dependencyinjection.abstractions\8.0.2\lib\net8.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -16,16 +17,11 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.AspNetCore.Authentication">
|
<Folder Include="Models\Security\" />
|
||||||
<HintPath>..\..\..\..\..\usr\lib64\dotnet\shared\Microsoft.AspNetCore.App\8.0.11\Microsoft.AspNetCore.Authentication.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions">
|
|
||||||
<HintPath>..\..\..\.nuget\packages\microsoft.extensions.dependencyinjection.abstractions\8.0.2\lib\net8.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.11" />
|
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
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;
|
||||||
|
}
|
45
DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs
Executable file
45
DotBased.AspNet.Authority/Models/Authority/AuthorityUser.cs
Executable file
|
@ -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;
|
||||||
|
}
|
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);
|
||||||
|
}
|
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
0
DotBased.Logging.Serilog/BasedSerilogEnricher.cs
Normal file → Executable file
0
DotBased.Logging.Serilog/BasedSerilogEnricher.cs
Normal file → Executable file
19
DotBased.sln
19
DotBased.sln
|
@ -20,7 +20,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazor.Wasm", "Blazor.Wasm\
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AspNet", "AspNet", "{624E7B11-8A18-46E5-AB1F-6AF6097F9D4D}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AspNet", "AspNet", "{624E7B11-8A18-46E5-AB1F-6AF6097F9D4D}"
|
||||||
EndProject
|
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
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{AC8343A5-7953-4E1D-A926-406BE4D7E819}.Release|Any CPU.Build.0 = 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
|
{A3ADC9AF-39B7-4EC4-8022-946118A8C322}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{6F407D81-DFAC-4936-ACDD-D75E9FDE2E7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{A3ADC9AF-39B7-4EC4-8022-946118A8C322}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{6F407D81-DFAC-4936-ACDD-D75E9FDE2E7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{A3ADC9AF-39B7-4EC4-8022-946118A8C322}.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}.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
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{EBBDAF9A-BFC7-4BDC-8C51-0501B59A1DDC} = {2156FB93-C252-4B33-8A0C-73C82FABB163}
|
{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}
|
{BADA4BAF-142B-47A8-95FC-B25E1D3D0020} = {DBDB4538-85D4-45AC-9E0A-A684467AEABA}
|
||||||
{AC8343A5-7953-4E1D-A926-406BE4D7E819} = {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}
|
{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
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
0
DotBased/Objects/DbObjectAttribute.cs
Normal file → Executable file
0
DotBased/Objects/DbObjectAttribute.cs
Normal file → Executable file
0
DotBased/Objects/IObjectAttribute.cs
Normal file → Executable file
0
DotBased/Objects/IObjectAttribute.cs
Normal file → Executable file
0
DotBased/Objects/ObjectAttribute.cs
Normal file → Executable file
0
DotBased/Objects/ObjectAttribute.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