hopefully not a horribly foolish refactoring
This commit is contained in:
parent
59d660165f
commit
1ecaf64dea
82 changed files with 782 additions and 398 deletions
|
@ -0,0 +1,23 @@
|
|||
using Femto.Modules.Authentication.Data;
|
||||
using Femto.Modules.Authentication.Models;
|
||||
|
||||
namespace Femto.Modules.Authentication.Contracts;
|
||||
|
||||
internal class AuthenticationService(AuthenticationContext context) : IAuthenticationService
|
||||
{
|
||||
public async Task<UserInfo> Register(string username, string password)
|
||||
{
|
||||
var user = new UserIdentity(username).WithPassword(password);
|
||||
await context.AddAsync(user);
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
return new(user.Id, user.Username);
|
||||
}
|
||||
|
||||
public async Task<UserInfo> Authenticate(string username, string password)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class AuthenticationError(string message, Exception inner) : Exception(message, inner);
|
|
@ -0,0 +1,7 @@
|
|||
namespace Femto.Modules.Authentication.Contracts;
|
||||
|
||||
public interface IAuthenticationService
|
||||
{
|
||||
public Task<UserInfo?> Register(string username, string password);
|
||||
public Task<UserInfo?> Authenticate(string username, string password);
|
||||
}
|
3
Femto.Modules.Authentication/Contracts/UserInfo.cs
Normal file
3
Femto.Modules.Authentication/Contracts/UserInfo.cs
Normal file
|
@ -0,0 +1,3 @@
|
|||
namespace Femto.Modules.Authentication.Contracts;
|
||||
|
||||
public record UserInfo(Guid UserId, string Username);
|
16
Femto.Modules.Authentication/Data/AuthenticationContext.cs
Normal file
16
Femto.Modules.Authentication/Data/AuthenticationContext.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using Femto.Common.Infrastructure.Outbox;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Femto.Modules.Authentication.Data;
|
||||
|
||||
internal class AuthenticationContext : DbContext, IOutboxContext
|
||||
{
|
||||
public virtual DbSet<OutboxEntry> Outbox { get; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
base.OnModelCreating(builder);
|
||||
builder.HasDefaultSchema("authn");
|
||||
builder.ApplyConfigurationsFromAssembly(typeof(AuthenticationContext).Assembly);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using Femto.Modules.Authentication.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace Femto.Modules.Authentication.Data.Configurations;
|
||||
|
||||
internal class UserIdentityTypeConfiguration : IEntityTypeConfiguration<UserIdentity>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<UserIdentity> builder)
|
||||
{
|
||||
builder.ToTable("user_identity");
|
||||
builder.OwnsOne(u => u.Password).WithOwner().HasForeignKey("user_id");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac" Version="8.3.0" />
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
|
||||
<PackageReference Include="Geralt" Version="3.3.0" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2024.3.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Femto.Common\Femto.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,5 @@
|
|||
using Femto.Common.Domain;
|
||||
|
||||
namespace Femto.Modules.Authentication.Models.Events;
|
||||
|
||||
internal record UserWasCreatedEvent(UserIdentity User) : DomainEvent;
|
52
Femto.Modules.Authentication/Models/UserIdentity.cs
Normal file
52
Femto.Modules.Authentication/Models/UserIdentity.cs
Normal file
|
@ -0,0 +1,52 @@
|
|||
using System.Text;
|
||||
using Femto.Common.Domain;
|
||||
using Femto.Modules.Authentication.Contracts;
|
||||
using Femto.Modules.Authentication.Models.Events;
|
||||
using Geralt;
|
||||
|
||||
namespace Femto.Modules.Authentication.Models;
|
||||
|
||||
internal class UserIdentity : Entity
|
||||
{
|
||||
public Guid Id { get; private set; }
|
||||
|
||||
public string Username { get; private set; }
|
||||
|
||||
public UserPassword Password { get; private set; }
|
||||
|
||||
private UserIdentity()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public UserIdentity(string username)
|
||||
{
|
||||
this.Id = Guid.CreateVersion7();
|
||||
this.Username = username;
|
||||
|
||||
this.AddDomainEvent(new UserWasCreatedEvent(this));
|
||||
}
|
||||
|
||||
public UserIdentity WithPassword(string password)
|
||||
{
|
||||
this.SetPassword(password);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void SetPassword(string password)
|
||||
{
|
||||
var hash = new byte[128];
|
||||
try
|
||||
{
|
||||
Argon2id.ComputeHash(hash, Encoding.UTF8.GetBytes(password), 3, 67108864);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new SetPasswordError("Failed to hash password", e);
|
||||
}
|
||||
|
||||
this.Password = new UserPassword(this.Id, hash, new byte[128]);
|
||||
}
|
||||
}
|
||||
|
||||
public class SetPasswordError(string message, Exception inner) : DomainException(message, inner);
|
19
Femto.Modules.Authentication/Models/UserPassword.cs
Normal file
19
Femto.Modules.Authentication/Models/UserPassword.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
namespace Femto.Modules.Authentication.Models;
|
||||
|
||||
internal class UserPassword
|
||||
{
|
||||
public Guid Id { get; private set; }
|
||||
|
||||
public byte[] Hash { get; private set; }
|
||||
|
||||
public byte[] Salt { get; private set; }
|
||||
|
||||
private UserPassword() {}
|
||||
|
||||
public UserPassword(Guid id, byte[] hash, byte[] salt)
|
||||
{
|
||||
Id = id;
|
||||
Hash = hash;
|
||||
Salt = salt;
|
||||
}
|
||||
}
|
57
Femto.Modules.Authentication/Module.cs
Normal file
57
Femto.Modules.Authentication/Module.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
using Femto.Common.Infrastructure.Outbox;
|
||||
using Femto.Modules.Authentication.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Femto.Modules.Authentication;
|
||||
|
||||
public static class Module
|
||||
{
|
||||
public static void UseIdentityModule(this IServiceCollection services, string connectionString)
|
||||
{
|
||||
services.AddDbContext<AuthenticationContext>(
|
||||
builder =>
|
||||
{
|
||||
builder.UseNpgsql(connectionString);
|
||||
builder.UseSnakeCaseNamingConvention();
|
||||
});
|
||||
|
||||
services.AddMediatR(c => c.RegisterServicesFromAssembly(typeof(Module).Assembly));
|
||||
|
||||
services.AddDbContext<AuthenticationContext>(builder =>
|
||||
{
|
||||
builder.UseNpgsql(
|
||||
connectionString,
|
||||
o =>
|
||||
{
|
||||
o.MapEnum<OutboxEntryStatus>("outbox_status");
|
||||
}
|
||||
);
|
||||
|
||||
builder.UseSnakeCaseNamingConvention();
|
||||
|
||||
var loggerFactory = LoggerFactory.Create(b =>
|
||||
{
|
||||
// b.AddConsole();
|
||||
// .AddFilter(
|
||||
// (category, level) =>
|
||||
// category == DbLoggerCategory.Database.Command.Name
|
||||
// && level == LogLevel.Debug
|
||||
// );
|
||||
});
|
||||
|
||||
builder.UseLoggerFactory(loggerFactory);
|
||||
builder.EnableSensitiveDataLogging();
|
||||
});
|
||||
|
||||
// services.AddOutbox<AuthenticationContext>();
|
||||
|
||||
services.AddMediatR(c =>
|
||||
{
|
||||
c.RegisterServicesFromAssembly(typeof(Module).Assembly);
|
||||
});
|
||||
|
||||
services.AddTransient<Outbox<AuthenticationContext>, Outbox<AuthenticationContext>>();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue