forked from xiongyuxing/tiku-backend.net
146 lines
6.7 KiB
C#
146 lines
6.7 KiB
C#
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.AspNetCore.DataProtection;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Tiku.Application.Security;
|
|
using Tiku.Domain.Identity;
|
|
using Tiku.Infrastructure.Bootstrap;
|
|
using Tiku.Infrastructure.Auth;
|
|
using Tiku.Infrastructure.Persistence;
|
|
|
|
namespace Tiku.UnitTests.Bootstrap;
|
|
|
|
public sealed class PlatformAdminBootstrapperTests
|
|
{
|
|
private const string TemporaryPassword = "Temporary9Password";
|
|
|
|
[Fact]
|
|
public async Task Bootstrap_creates_forced_enrollment_super_admin_and_audit()
|
|
{
|
|
await using var provider = CreateProvider();
|
|
await using var scope = provider.CreateAsyncScope();
|
|
var bootstrapper = ActivatorUtilities.CreateInstance<PlatformAdminBootstrapper>(scope.ServiceProvider);
|
|
|
|
var result = await bootstrapper.BootstrapAsync(new PlatformAdminBootstrapOptions(
|
|
"admin@example.com",
|
|
TemporaryPassword,
|
|
"Initial Administrator"));
|
|
|
|
var context = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
|
|
var user = await context.Users.SingleAsync(item => item.Id == result.UserId);
|
|
Assert.True(user.ForcePasswordChange);
|
|
Assert.True(user.EmailConfirmed);
|
|
Assert.Equal(UserStatus.Active, user.Status);
|
|
var role = await context.PlatformBackendRoles.SingleAsync(item => item.Id == result.RoleId);
|
|
Assert.Equal(PlatformAdminBootstrapper.SuperAdminRoleCode, role.Code);
|
|
Assert.True(role.IsSystem);
|
|
Assert.Equal(BackendPermissions.Platform.Count, await context.PlatformBackendRolePermissions.CountAsync());
|
|
Assert.True(await context.PlatformBackendUserRoles.AnyAsync(item => item.UserId == user.Id && item.RoleId == role.Id));
|
|
Assert.True(await context.AuditLogs.AnyAsync(item =>
|
|
item.ActorUserId == user.Id && item.Action == "platform.bootstrap_admin.created"));
|
|
|
|
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<User>>();
|
|
Assert.True(await userManager.CheckPasswordAsync(user, TemporaryPassword));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Bootstrap_rejects_a_second_platform_administrator_without_mutating_data()
|
|
{
|
|
await using var provider = CreateProvider();
|
|
await using var scope = provider.CreateAsyncScope();
|
|
var bootstrapper = ActivatorUtilities.CreateInstance<PlatformAdminBootstrapper>(scope.ServiceProvider);
|
|
await bootstrapper.BootstrapAsync(new PlatformAdminBootstrapOptions("first@example.com", TemporaryPassword));
|
|
|
|
var exception = await Assert.ThrowsAsync<PlatformAdminBootstrapException>(() =>
|
|
bootstrapper.BootstrapAsync(new PlatformAdminBootstrapOptions("second@example.com", TemporaryPassword)));
|
|
|
|
Assert.Equal("platform_admin_already_exists", exception.Code);
|
|
var context = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
|
|
Assert.Single(context.PlatformBackendUserRoles);
|
|
Assert.Single(context.PlatformBackendRoles);
|
|
Assert.Single(context.AuditLogs);
|
|
Assert.Single(context.Users);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Bootstrap_rejects_an_existing_user_email()
|
|
{
|
|
await using var provider = CreateProvider();
|
|
await using var scope = provider.CreateAsyncScope();
|
|
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<User>>();
|
|
var createResult = await userManager.CreateAsync(new User
|
|
{
|
|
Email = "existing@example.com",
|
|
UserName = "existing@example.com"
|
|
}, TemporaryPassword);
|
|
Assert.True(createResult.Succeeded);
|
|
var bootstrapper = ActivatorUtilities.CreateInstance<PlatformAdminBootstrapper>(scope.ServiceProvider);
|
|
|
|
var exception = await Assert.ThrowsAsync<PlatformAdminBootstrapException>(() =>
|
|
bootstrapper.BootstrapAsync(new PlatformAdminBootstrapOptions("existing@example.com", TemporaryPassword)));
|
|
|
|
Assert.Equal("bootstrap_user_already_exists", exception.Code);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Development_async_seed_creates_one_forced_enrollment_administrator()
|
|
{
|
|
await using var provider = CreateProvider();
|
|
await using var scope = provider.CreateAsyncScope();
|
|
var context = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
|
|
|
|
var created = await DevelopmentPlatformAdminSeeder.SeedAsync(context);
|
|
var createdAgain = await DevelopmentPlatformAdminSeeder.SeedAsync(context);
|
|
|
|
Assert.True(created);
|
|
Assert.False(createdAgain);
|
|
var user = await context.Users.SingleAsync();
|
|
Assert.Equal(DevelopmentPlatformAdminSeeder.Email, user.Email);
|
|
Assert.True(user.ForcePasswordChange);
|
|
Assert.Equal(BackendPermissions.Platform.Count, await context.PlatformBackendRolePermissions.CountAsync());
|
|
Assert.Single(context.PlatformBackendUserRoles);
|
|
Assert.Single(context.AuditLogs);
|
|
var quote = await context.PlatformBillingQuotes.SingleAsync();
|
|
Assert.Equal(quote.OriginalAmountCents - quote.DiscountAmountCents, quote.TotalAmountCents);
|
|
var order = await context.PlatformBillingOrders.SingleAsync();
|
|
Assert.Equal(order.OriginalAmountCents - order.DiscountAmountCents, order.TotalAmountCents);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Development_sync_seed_is_idempotent()
|
|
{
|
|
await using var provider = CreateProvider();
|
|
await using var scope = provider.CreateAsyncScope();
|
|
var context = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
|
|
|
|
var created = DevelopmentPlatformAdminSeeder.Seed(context);
|
|
var createdAgain = DevelopmentPlatformAdminSeeder.Seed(context);
|
|
|
|
Assert.True(created);
|
|
Assert.False(createdAgain);
|
|
Assert.Single(context.Users);
|
|
Assert.Single(context.PlatformBackendUserRoles);
|
|
}
|
|
|
|
private static ServiceProvider CreateProvider()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddLogging();
|
|
services.AddDbContext<TikuDbContext>(options =>
|
|
options.UseInMemoryDatabase(Guid.NewGuid().ToString()));
|
|
services.AddIdentityCore<User>(options =>
|
|
{
|
|
options.Password.RequiredLength = 8;
|
|
options.Password.RequireDigit = false;
|
|
options.Password.RequireLowercase = false;
|
|
options.Password.RequireUppercase = false;
|
|
options.Password.RequireNonAlphanumeric = false;
|
|
})
|
|
.AddEntityFrameworkStores<TikuDbContext>()
|
|
.AddDefaultTokenProviders()
|
|
.AddPasswordValidator<LetterAndDigitPasswordValidator<User>>();
|
|
services.AddDataProtection().UseEphemeralDataProtectionProvider();
|
|
return services.BuildServiceProvider();
|
|
}
|
|
}
|