105 lines
3.3 KiB
C#
105 lines
3.3 KiB
C#
using BuildFlow.Platform.Domain.Entities.Role;
|
|
using BuildFlow.Platform.Infrastructure.Configuration;
|
|
using BuildFlow.Platform.Infrastructure.Persistence;
|
|
using FluentAssertions;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Xunit;
|
|
|
|
namespace BuildFlow.Platform.Tests.Auth;
|
|
|
|
/// <summary>
|
|
/// 首次部署管理员初始化安全边界测试。
|
|
/// </summary>
|
|
public class BootstrapAdminSeederTests
|
|
{
|
|
private static BootstrapAdminOptions ValidOptions(string password = "BootstrapAdmin1") => new()
|
|
{
|
|
Enabled = true,
|
|
Username = "bootstrap-admin",
|
|
Email = "bootstrap-admin@test.local",
|
|
Password = password,
|
|
};
|
|
|
|
private static async Task SeedAdminRoleAsync(PlatformDbContext db)
|
|
{
|
|
db.Roles.Add(PlatformTestDb.WithAudit(new RoleEntity
|
|
{
|
|
Id = BootstrapAdminSeeder.AdminRoleId,
|
|
Name = "Admin",
|
|
Description = "管理员",
|
|
}));
|
|
await db.SaveChangesAsync();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DisabledShouldNotCreateAUser()
|
|
{
|
|
await using var db = PlatformTestDb.CreateDbContext();
|
|
|
|
var created = await BootstrapAdminSeeder.SeedAsync(
|
|
db,
|
|
new BootstrapAdminOptions());
|
|
|
|
created.Should().BeFalse();
|
|
db.Users.Should().BeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task EnabledWithoutPasswordShouldFailClearly()
|
|
{
|
|
await using var db = PlatformTestDb.CreateDbContext();
|
|
var options = ValidOptions("");
|
|
|
|
var act = () => BootstrapAdminSeeder.SeedAsync(db, options);
|
|
|
|
await act.Should().ThrowAsync<InvalidOperationException>()
|
|
.WithMessage("*密码不能为空*");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task WeakPasswordShouldFailClearly()
|
|
{
|
|
await using var db = PlatformTestDb.CreateDbContext();
|
|
|
|
var act = () => BootstrapAdminSeeder.SeedAsync(db, ValidOptions("weakpass"));
|
|
|
|
await act.Should().ThrowAsync<InvalidOperationException>()
|
|
.WithMessage("*大写字母*");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task FirstRunShouldCreateAnAdminWithTheConfiguredPassword()
|
|
{
|
|
await using var db = PlatformTestDb.CreateDbContext();
|
|
await SeedAdminRoleAsync(db);
|
|
var options = ValidOptions();
|
|
|
|
var created = await BootstrapAdminSeeder.SeedAsync(db, options);
|
|
|
|
created.Should().BeTrue();
|
|
var user = await db.Users.SingleAsync();
|
|
user.Username.Should().Be(options.Username);
|
|
BCrypt.Net.BCrypt.Verify(options.Password, user.PasswordHash).Should().BeTrue();
|
|
(await db.UserRoles.CountAsync(x =>
|
|
x.UserId == user.Id && x.RoleId == BootstrapAdminSeeder.AdminRoleId))
|
|
.Should().Be(1);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RepeatedRunShouldNotResetTheExistingAdminPassword()
|
|
{
|
|
await using var db = PlatformTestDb.CreateDbContext();
|
|
await SeedAdminRoleAsync(db);
|
|
await BootstrapAdminSeeder.SeedAsync(db, ValidOptions("BootstrapAdmin1"));
|
|
var originalHash = await db.Users.Select(x => x.PasswordHash).SingleAsync();
|
|
|
|
var created = await BootstrapAdminSeeder.SeedAsync(
|
|
db,
|
|
ValidOptions("DifferentAdmin2"));
|
|
|
|
created.Should().BeFalse();
|
|
(await db.Users.Select(x => x.PasswordHash).SingleAsync()).Should().Be(originalHash);
|
|
BCrypt.Net.BCrypt.Verify("BootstrapAdmin1", originalHash).Should().BeTrue();
|
|
}
|
|
}
|