feat(admincli): add command for creating a user
This commit is contained in:
@@ -11,5 +11,6 @@
|
|||||||
</PackageVersion>
|
</PackageVersion>
|
||||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.2" />
|
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.2" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.2" />
|
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.2" />
|
||||||
|
<PackageVersion Include="Spectre.Console" Version="0.54.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
[*.cs]
|
[*.cs]
|
||||||
|
|
||||||
dotnet_diagnostic.CA1303.severity = none
|
dotnet_diagnostic.CA1303.severity = none
|
||||||
|
dotnet_diagnostic.CA2007.severity = none
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
namespace FiscalOS.AdminCLI;
|
||||||
|
|
||||||
|
internal sealed class App(
|
||||||
|
IAnsiConsole console,
|
||||||
|
IPasswordHasher passwordHasher,
|
||||||
|
IServiceScopeFactory serviceScopeFactory
|
||||||
|
) : IHostedService
|
||||||
|
{
|
||||||
|
private readonly IAnsiConsole _console = console;
|
||||||
|
private readonly IServiceScopeFactory _serviceScopeFactory = serviceScopeFactory;
|
||||||
|
|
||||||
|
public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
|
var config = scope.ServiceProvider.GetRequiredService<IConfiguration>();
|
||||||
|
var appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
|
||||||
|
var selectionPrompt = new SelectionPrompt<string>()
|
||||||
|
.Title("What do you want to do?")
|
||||||
|
.AddChoices(Commands.All);
|
||||||
|
|
||||||
|
var command = await _console.PromptAsync(selectionPrompt, cancellationToken);
|
||||||
|
|
||||||
|
switch (command)
|
||||||
|
{
|
||||||
|
case Commands.CreateUser:
|
||||||
|
var username = await _console.AskAsync<string>("Enter the [green]username[/]:", cancellationToken);
|
||||||
|
var password = _console.Prompt(
|
||||||
|
new TextPrompt<string>("Enter the [green]password[/]:")
|
||||||
|
.PromptStyle("red")
|
||||||
|
.Secret()
|
||||||
|
);
|
||||||
|
|
||||||
|
var hashedPassword = passwordHasher.Hash(password);
|
||||||
|
var user = User.From(username, hashedPassword);
|
||||||
|
|
||||||
|
await appDbContext.Users.AddAsync(user, cancellationToken);
|
||||||
|
await appDbContext.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
_console.MarkupLine($"User [green]{username}[/] created successfully.");
|
||||||
|
break;
|
||||||
|
case Commands.Exit:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace FiscalOS.AdminCLI;
|
||||||
|
|
||||||
|
internal static class Commands
|
||||||
|
{
|
||||||
|
public const string CreateUser = "Create User";
|
||||||
|
public const string Exit = "Exit";
|
||||||
|
|
||||||
|
public static readonly string[] All = [CreateUser, Exit];
|
||||||
|
}
|
||||||
@@ -4,4 +4,20 @@
|
|||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||||
|
<PackageReference Include="Spectre.Console" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="appsettings*.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\FiscalOS.Infra\FiscalOS.Infra.csproj" />
|
||||||
|
<ProjectReference Include="..\FiscalOS.Core\FiscalOS.Core.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
// TODO: We need a way to add
|
await Host.CreateDefaultBuilder(args)
|
||||||
// a user to the system with
|
.ConfigureAppConfiguration(
|
||||||
// a properly hashed password
|
static c => c.SetBasePath(AppContext.BaseDirectory)
|
||||||
// so need something like this:
|
.AddJsonFile("appsettings.json")
|
||||||
//
|
.AddEnvironmentVariables()
|
||||||
// 1. Ask for username
|
)
|
||||||
// 2. Ask for user password
|
.ConfigureLogging(static c => c.ClearProviders())
|
||||||
// 3. Hash user password
|
.ConfigureServices(static (_, services) =>
|
||||||
// 4. Save user to database
|
{
|
||||||
|
services.AddSingleton(AnsiConsole.Console);
|
||||||
Console.WriteLine("Hello, World!");
|
services.AddInfrastructure();
|
||||||
|
services.AddHostedService<App>();
|
||||||
|
})
|
||||||
|
.Build()
|
||||||
|
.StartAsync();
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
global using FiscalOS.AdminCLI;
|
||||||
|
global using FiscalOS.Core.Authentication;
|
||||||
|
global using FiscalOS.Core.Identity;
|
||||||
|
global using FiscalOS.Infra.Data;
|
||||||
|
global using FiscalOS.Infra.DependencyInjection;
|
||||||
|
|
||||||
|
global using Microsoft.Extensions.Configuration;
|
||||||
|
global using Microsoft.Extensions.DependencyInjection;
|
||||||
|
global using Microsoft.Extensions.Hosting;
|
||||||
|
global using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
global using Spectre.Console;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"AppDbContextOptions": {
|
||||||
|
"DatabaseFilePath": "DatabaseFilePath"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,9 +51,7 @@ public sealed class AppDbContext(IOptions<AppDbContextOptions> ctxOptions) : DbC
|
|||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
eb.Property(static u => u.Username);
|
eb.Property(static u => u.Username);
|
||||||
eb.Property(static u => u.HashedPassword);
|
eb.Property(static u => u.HashedPassword); });
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity<RefreshToken>(static eb =>
|
modelBuilder.Entity<RefreshToken>(static eb =>
|
||||||
{
|
{
|
||||||
eb.Property(static t => t.Id);
|
eb.Property(static t => t.Id);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ namespace FiscalOS.Infra.Data;
|
|||||||
public sealed record AppDbContextOptions
|
public sealed record AppDbContextOptions
|
||||||
{
|
{
|
||||||
public string DatabaseFilePath { get; init; } = string.Empty;
|
public string DatabaseFilePath { get; init; } = string.Empty;
|
||||||
|
|
||||||
public string GetFullyQualifiedDatabasePath()
|
public string GetFullyQualifiedDatabasePath()
|
||||||
{
|
{
|
||||||
return Path.GetFullPath(DatabaseFilePath, AppContext.BaseDirectory);
|
return Path.GetFullPath(DatabaseFilePath, AppContext.BaseDirectory);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ public static class ServiceCollectionExtensions
|
|||||||
public static IServiceCollection AddInfrastructure(this IServiceCollection services)
|
public static IServiceCollection AddInfrastructure(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddSingleton(TimeProvider.System);
|
services.AddSingleton(TimeProvider.System);
|
||||||
|
services.ConfigureOptions<JwtOptionsSetup>();
|
||||||
services.AddSingleton<ITokenGenerator, TokenGenerator>(TokenGenerator.From);
|
services.AddSingleton<ITokenGenerator, TokenGenerator>(TokenGenerator.From);
|
||||||
services.AddSingleton<IPasswordHasher, PasswordHasher>(PasswordHasher.From);
|
services.AddSingleton<IPasswordHasher, PasswordHasher>(PasswordHasher.From);
|
||||||
services.ConfigureOptions<AppDbContextOptionsSetup>();
|
services.ConfigureOptions<AppDbContextOptionsSetup>();
|
||||||
|
|||||||
Reference in New Issue
Block a user