193 lines
5.7 KiB
C#
193 lines
5.7 KiB
C#
using IdentityShroud.Api;
|
|
using IdentityShroud.Core.Contracts;
|
|
using IdentityShroud.Core.EFCore;
|
|
using IdentityShroud.Core.Model;
|
|
using IdentityShroud.Core.Security;
|
|
using IdentityShroud.Core.Security.Keys;
|
|
using IdentityShroud.Core.Services;
|
|
using IdentityShroud.Core.Tests.Fixtures;
|
|
using IdentityShroud.TestUtils.Substitutes;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace IdentityShroud.Core.Tests.Services;
|
|
|
|
public static class RealmDekBuilder
|
|
{
|
|
public static RealmDek DefaultActive() =>
|
|
new()
|
|
{
|
|
Id = DekId.NewId(),
|
|
Active = true,
|
|
Algorithm = KeyType.AES,
|
|
KeyData = new EncryptedDek(KekId.NewId(),
|
|
[
|
|
0
|
|
])
|
|
};
|
|
}
|
|
|
|
public static class ClientCreateRequestBuilder
|
|
{
|
|
public static ClientCreateRequest Default() => new(
|
|
"test-client",
|
|
"Test Client",
|
|
"A test client");
|
|
}
|
|
|
|
public class ClientServiceTests : IClassFixture<DbFixture>
|
|
{
|
|
private readonly DbFixture _dbFixture;
|
|
private readonly NullDataEncryptionService _dataEncryptionService = new();
|
|
|
|
private readonly IClock _clock = Substitute.For<IClock>();
|
|
private readonly Guid _realmId = new("a1b2c3d4-0000-0000-0000-000000000001");
|
|
|
|
public ClientServiceTests(DbFixture dbFixture)
|
|
{
|
|
_dbFixture = dbFixture;
|
|
using Db db = dbFixture.CreateDbContext();
|
|
if (!db.Database.EnsureCreated())
|
|
TruncateTables(db);
|
|
EnsureRealm(db);
|
|
}
|
|
|
|
private void TruncateTables(Db db)
|
|
{
|
|
db.Database.ExecuteSqlRaw("TRUNCATE client CASCADE;");
|
|
db.Database.ExecuteSqlRaw("TRUNCATE realm CASCADE;");
|
|
}
|
|
|
|
private void EnsureRealm(Db db)
|
|
{
|
|
if (!db.Realms.Any(r => r.Id == _realmId))
|
|
{
|
|
db.Realms.Add(new()
|
|
{
|
|
Id = _realmId,
|
|
Slug = "test-realm",
|
|
Name = "Test Realm",
|
|
DataEncryptionKeys = [ RealmDekBuilder.DefaultActive(), ],
|
|
});
|
|
|
|
db.SaveChanges();
|
|
}
|
|
}
|
|
|
|
private ClientService CreateSut(Db db) => new(db,
|
|
_dataEncryptionService,
|
|
new ClientCreateRequestValidator(),
|
|
_clock);
|
|
|
|
|
|
[Theory]
|
|
[InlineData(false)]
|
|
[InlineData(true)]
|
|
public async Task Create(bool withSecret)
|
|
{
|
|
// Setup
|
|
DateTime now = DateTime.UtcNow;
|
|
_clock.UtcNow().Returns(now);
|
|
|
|
Client val;
|
|
await using (var db = _dbFixture.CreateDbContext())
|
|
{
|
|
// Act
|
|
ClientService sut = CreateSut(db);
|
|
var response = await sut.Create(
|
|
_realmId,
|
|
ClientCreateRequestBuilder.Default() with
|
|
{
|
|
Confidential = withSecret,
|
|
GenerateSecret = withSecret,
|
|
},
|
|
TestContext.Current.CancellationToken);
|
|
|
|
// Verify
|
|
val = ResultAssert.Success(response);
|
|
Assert.Equal(_realmId, val.RealmId);
|
|
Assert.Equal("test-client", val.ClientId);
|
|
Assert.Equal("Test Client", val.Name);
|
|
Assert.Equal("A test client", val.Description);
|
|
Assert.Equal(withSecret, val.Confidential);
|
|
Assert.Equal(now, val.CreatedAt);
|
|
}
|
|
|
|
await using (var db = _dbFixture.CreateDbContext())
|
|
{
|
|
var dbRecord = await db.Clients
|
|
.Include(e => e.Secrets)
|
|
.SingleAsync(e => e.Id == val.Id, TestContext.Current.CancellationToken);
|
|
|
|
if (withSecret)
|
|
Assert.Single(dbRecord.Secrets);
|
|
else
|
|
Assert.Empty(dbRecord.Secrets);
|
|
}
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("existing-client", true)]
|
|
[InlineData("missing-client", false)]
|
|
public async Task GetByClientId(string clientId, bool shouldFind)
|
|
{
|
|
// Setup
|
|
_clock.UtcNow().Returns(DateTime.UtcNow);
|
|
await using (var setupContext = _dbFixture.CreateDbContext())
|
|
{
|
|
setupContext.Clients.Add(new()
|
|
{
|
|
RealmId = _realmId,
|
|
ClientId = "existing-client",
|
|
CreatedAt = DateTime.UtcNow,
|
|
});
|
|
|
|
await setupContext.SaveChangesAsync(TestContext.Current.CancellationToken);
|
|
}
|
|
|
|
await using var actContext = _dbFixture.CreateDbContext();
|
|
// Act
|
|
ClientService sut = CreateSut(actContext);
|
|
Client? result = await sut.GetByClientId(_realmId, clientId, TestContext.Current.CancellationToken);
|
|
|
|
// Verify
|
|
if (shouldFind)
|
|
Assert.NotNull(result);
|
|
else
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(true)]
|
|
[InlineData(false)]
|
|
public async Task FindById(bool shouldFind)
|
|
{
|
|
// Setup
|
|
_clock.UtcNow().Returns(DateTime.UtcNow);
|
|
int existingId;
|
|
await using (var setupContext = _dbFixture.CreateDbContext())
|
|
{
|
|
Client client = new()
|
|
{
|
|
RealmId = _realmId,
|
|
ClientId = "find-by-id-client",
|
|
CreatedAt = DateTime.UtcNow,
|
|
};
|
|
setupContext.Clients.Add(client);
|
|
await setupContext.SaveChangesAsync(TestContext.Current.CancellationToken);
|
|
existingId = client.Id;
|
|
}
|
|
|
|
int searchId = shouldFind ? existingId : existingId + 9999;
|
|
|
|
await using var actContext = _dbFixture.CreateDbContext();
|
|
// Act
|
|
ClientService sut = CreateSut(actContext);
|
|
Client? result = await sut.FindById(_realmId, searchId, TestContext.Current.CancellationToken);
|
|
|
|
// Verify
|
|
if (shouldFind)
|
|
Assert.NotNull(result);
|
|
else
|
|
Assert.Null(result);
|
|
}
|
|
}
|