Why: provide service-side canonical login/refresh orchestration for session-based web auth. What: add session contracts, refresh token codec with provider-agnostic secret boundary, grpc session methods, DI wiring, tests, and docs. Rule: preserve thalos identity ownership and keep transport adapters at service edge.
116 lines
5.0 KiB
C#
116 lines
5.0 KiB
C#
using Microsoft.Extensions.DependencyInjection;
|
|
using BuildingBlock.Identity.Contracts.Conventions;
|
|
using BuildingBlock.Identity.Contracts.Requests;
|
|
using Thalos.Service.Application.Adapters;
|
|
using Thalos.Service.Application.DependencyInjection;
|
|
using Thalos.Service.Application.Grpc;
|
|
using Thalos.Service.Application.UseCases;
|
|
using BuildingIssueRequest = BuildingBlock.Identity.Contracts.Requests.IssueIdentityTokenRequest;
|
|
using BuildingPolicyRequest = BuildingBlock.Identity.Contracts.Requests.EvaluateIdentityPolicyRequest;
|
|
using StartSessionRequest = Thalos.Service.Identity.Abstractions.Contracts.StartIdentitySessionRequest;
|
|
using SessionRefreshRequest = Thalos.Service.Identity.Abstractions.Contracts.RefreshIdentitySessionRequest;
|
|
|
|
namespace Thalos.Service.Application.UnitTests;
|
|
|
|
public class RuntimeWiringTests
|
|
{
|
|
[Fact]
|
|
public async Task AddThalosServiceRuntime_WhenInvoked_ResolvesUseCases()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddThalosServiceRuntime();
|
|
|
|
using var provider = services.BuildServiceProvider();
|
|
var issueTokenUseCase = provider.GetRequiredService<IIssueIdentityTokenUseCase>();
|
|
var startSessionUseCase = provider.GetRequiredService<IStartIdentitySessionUseCase>();
|
|
var refreshSessionUseCase = provider.GetRequiredService<IRefreshIdentitySessionUseCase>();
|
|
var evaluatePolicyUseCase = provider.GetRequiredService<IEvaluateIdentityPolicyUseCase>();
|
|
|
|
var tokenResponse = await issueTokenUseCase.HandleAsync(new BuildingIssueRequest("user-1", "tenant-1"));
|
|
var policyResponse = await evaluatePolicyUseCase.HandleAsync(
|
|
new BuildingPolicyRequest("user-1", "tenant-1", "identity.token.issue"));
|
|
var startSessionResponse = await startSessionUseCase.HandleAsync(
|
|
new StartSessionRequest("user-1", "tenant-1"));
|
|
var refreshSessionResponse = await refreshSessionUseCase.HandleAsync(
|
|
new SessionRefreshRequest(startSessionResponse.RefreshToken, "corr-rt-1"));
|
|
|
|
Assert.Equal("user-1:tenant-1:token", tokenResponse.Token);
|
|
Assert.Equal(1800, tokenResponse.ExpiresInSeconds);
|
|
Assert.True(policyResponse.IsAllowed);
|
|
Assert.Equal("user-1", startSessionResponse.SubjectId);
|
|
Assert.Equal("tenant-1", startSessionResponse.TenantId);
|
|
Assert.NotEmpty(startSessionResponse.RefreshToken);
|
|
Assert.Equal("user-1", refreshSessionResponse.SubjectId);
|
|
Assert.Equal("tenant-1", refreshSessionResponse.TenantId);
|
|
Assert.NotEmpty(refreshSessionResponse.RefreshToken);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AddThalosServiceRuntime_WhenSubjectMissing_ReturnsEmptyToken()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddThalosServiceRuntime();
|
|
|
|
using var provider = services.BuildServiceProvider();
|
|
var issueTokenUseCase = provider.GetRequiredService<IIssueIdentityTokenUseCase>();
|
|
|
|
var tokenResponse = await issueTokenUseCase.HandleAsync(
|
|
new BuildingIssueRequest("missing-user", "tenant-1"));
|
|
|
|
Assert.Equal(string.Empty, tokenResponse.Token);
|
|
Assert.Equal(0, tokenResponse.ExpiresInSeconds);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AddThalosServiceRuntime_WhenAzureProviderUsed_IssuesProviderToken()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddThalosServiceRuntime();
|
|
|
|
using var provider = services.BuildServiceProvider();
|
|
var issueTokenUseCase = provider.GetRequiredService<IIssueIdentityTokenUseCase>();
|
|
|
|
var tokenResponse = await issueTokenUseCase.HandleAsync(
|
|
new BuildingIssueRequest(
|
|
string.Empty,
|
|
"tenant-2",
|
|
IdentityAuthProvider.AzureAd,
|
|
"azure-id-token"));
|
|
|
|
Assert.StartsWith("azure:", tokenResponse.Token);
|
|
Assert.True(tokenResponse.ExpiresInSeconds > 0);
|
|
}
|
|
|
|
[Fact]
|
|
public void IdentityPolicyGrpcContractAdapter_WhenMapped_PreservesValues()
|
|
{
|
|
var adapter = new IdentityPolicyGrpcContractAdapter();
|
|
var useCaseRequest = new BuildingPolicyRequest("user-2", "tenant-2", "identity.policy.evaluate");
|
|
|
|
var grpcContract = adapter.ToGrpc(useCaseRequest);
|
|
var roundtrip = adapter.FromGrpc(grpcContract);
|
|
|
|
Assert.Equal("user-2", roundtrip.SubjectId);
|
|
Assert.Equal("tenant-2", roundtrip.TenantId);
|
|
Assert.Equal("identity.policy.evaluate", roundtrip.PermissionCode);
|
|
}
|
|
|
|
[Fact]
|
|
public void IdentityPolicyGrpcContractAdapter_WhenFromGrpc_UsesExpectedContractShape()
|
|
{
|
|
var adapter = new IdentityPolicyGrpcContractAdapter();
|
|
var contract = new EvaluateIdentityPolicyGrpcContract(
|
|
"subject-9",
|
|
"tenant-9",
|
|
"identity.token.issue",
|
|
IdentityAuthProvider.Google.ToString());
|
|
|
|
var request = adapter.FromGrpc(contract);
|
|
|
|
Assert.Equal("subject-9", request.SubjectId);
|
|
Assert.Equal("tenant-9", request.TenantId);
|
|
Assert.Equal("identity.token.issue", request.PermissionCode);
|
|
Assert.Equal(IdentityAuthProvider.Google, request.Provider);
|
|
}
|
|
}
|