feat(thalos-service): propagate provider metadata through grpc flow
This commit is contained in:
parent
654a808c54
commit
f960f0656d
@ -7,6 +7,7 @@ Constrain thalos-service to orchestration responsibilities after thalos-domain e
|
||||
- Coordinate identity use-case flow
|
||||
- Delegate policy/token decisions to thalos-domain abstractions
|
||||
- Adapt transport contracts
|
||||
- Route provider metadata (`InternalJwt`, `AzureAd`, `Google`) between edge/service/dal boundaries
|
||||
|
||||
## Prohibited Responsibilities
|
||||
- Owning identity decision policies
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using Thalos.Service.Application.Grpc;
|
||||
using BuildingBlock.Identity.Contracts.Conventions;
|
||||
using BuildingBlock.Identity.Contracts.Requests;
|
||||
|
||||
namespace Thalos.Service.Application.Adapters;
|
||||
@ -11,12 +12,24 @@ public sealed class IdentityPolicyGrpcContractAdapter : IIdentityPolicyGrpcContr
|
||||
/// <inheritdoc />
|
||||
public EvaluateIdentityPolicyGrpcContract ToGrpc(EvaluateIdentityPolicyRequest request)
|
||||
{
|
||||
return new EvaluateIdentityPolicyGrpcContract(request.SubjectId, request.TenantId, request.PermissionCode);
|
||||
return new EvaluateIdentityPolicyGrpcContract(
|
||||
request.SubjectId,
|
||||
request.TenantId,
|
||||
request.PermissionCode,
|
||||
request.Provider.ToString());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EvaluateIdentityPolicyRequest FromGrpc(EvaluateIdentityPolicyGrpcContract contract)
|
||||
{
|
||||
return new EvaluateIdentityPolicyRequest(contract.SubjectId, contract.TenantId, contract.PermissionCode);
|
||||
var provider = Enum.TryParse<IdentityAuthProvider>(contract.Provider, true, out var parsedProvider)
|
||||
? parsedProvider
|
||||
: IdentityAuthProvider.InternalJwt;
|
||||
|
||||
return new EvaluateIdentityPolicyRequest(
|
||||
contract.SubjectId,
|
||||
contract.TenantId,
|
||||
contract.PermissionCode,
|
||||
provider);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,4 +6,9 @@ namespace Thalos.Service.Application.Grpc;
|
||||
/// <param name="SubjectId">Identity subject identifier.</param>
|
||||
/// <param name="TenantId">Tenant scope identifier.</param>
|
||||
/// <param name="PermissionCode">Permission code to evaluate.</param>
|
||||
public sealed record EvaluateIdentityPolicyGrpcContract(string SubjectId, string TenantId, string PermissionCode);
|
||||
/// <param name="Provider">Auth provider.</param>
|
||||
public sealed record EvaluateIdentityPolicyGrpcContract(
|
||||
string SubjectId,
|
||||
string TenantId,
|
||||
string PermissionCode,
|
||||
string Provider = "InternalJwt");
|
||||
|
||||
@ -20,7 +20,8 @@ public sealed class IdentityPolicyContextReadPortDalAdapter(
|
||||
CreateEnvelope(),
|
||||
request.SubjectId,
|
||||
request.TenantId,
|
||||
request.PermissionCode);
|
||||
request.PermissionCode,
|
||||
request.Provider);
|
||||
|
||||
var policyRecord = await identityRepository.ReadIdentityPolicyAsync(policyLookupRequest);
|
||||
if (policyRecord is null)
|
||||
@ -28,6 +29,7 @@ public sealed class IdentityPolicyContextReadPortDalAdapter(
|
||||
return new IdentityPolicyContextData(
|
||||
request.SubjectId,
|
||||
request.PermissionCode,
|
||||
request.Provider,
|
||||
false,
|
||||
[]);
|
||||
}
|
||||
@ -35,7 +37,8 @@ public sealed class IdentityPolicyContextReadPortDalAdapter(
|
||||
var permissionSetRequest = new IdentityPermissionSetLookupRequest(
|
||||
policyLookupRequest.Envelope,
|
||||
request.SubjectId,
|
||||
request.TenantId);
|
||||
request.TenantId,
|
||||
request.Provider);
|
||||
|
||||
var permissions = await identityRepository.ReadPermissionSetAsync(permissionSetRequest);
|
||||
var grantedPermissions = permissions
|
||||
@ -45,6 +48,7 @@ public sealed class IdentityPolicyContextReadPortDalAdapter(
|
||||
return new IdentityPolicyContextData(
|
||||
request.SubjectId,
|
||||
request.PermissionCode,
|
||||
policyRecord.Provider,
|
||||
policyRecord.ContextSatisfied,
|
||||
grantedPermissions);
|
||||
}
|
||||
|
||||
@ -19,15 +19,17 @@ public sealed class IdentityTokenReadPortDalAdapter(
|
||||
var lookupRequest = new IdentityTokenLookupRequest(
|
||||
CreateEnvelope(),
|
||||
request.SubjectId,
|
||||
request.TenantId);
|
||||
request.TenantId,
|
||||
request.Provider,
|
||||
request.ExternalToken);
|
||||
|
||||
var tokenRecord = await identityRepository.ReadIdentityTokenAsync(lookupRequest);
|
||||
if (tokenRecord is null)
|
||||
{
|
||||
return new IdentityTokenData(null, null);
|
||||
return new IdentityTokenData(null, null, request.Provider);
|
||||
}
|
||||
|
||||
return new IdentityTokenData(tokenRecord.Token, tokenRecord.ExpiresInSeconds);
|
||||
return new IdentityTokenData(tokenRecord.Token, tokenRecord.ExpiresInSeconds, tokenRecord.Provider);
|
||||
}
|
||||
|
||||
private IdentityContractEnvelope CreateEnvelope()
|
||||
|
||||
@ -12,6 +12,8 @@ service IdentityRuntime {
|
||||
message IssueIdentityTokenGrpcRequest {
|
||||
string subject_id = 1;
|
||||
string tenant_id = 2;
|
||||
string provider = 3;
|
||||
string external_token = 4;
|
||||
}
|
||||
|
||||
message IssueIdentityTokenGrpcResponse {
|
||||
@ -23,6 +25,7 @@ message EvaluateIdentityPolicyGrpcRequest {
|
||||
string subject_id = 1;
|
||||
string tenant_id = 2;
|
||||
string permission_code = 3;
|
||||
string provider = 4;
|
||||
}
|
||||
|
||||
message EvaluateIdentityPolicyGrpcResponse {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using Grpc.Core;
|
||||
using BuildingBlock.Identity.Contracts.Conventions;
|
||||
using Thalos.Service.Application.Adapters;
|
||||
using Thalos.Service.Application.Grpc;
|
||||
using Thalos.Service.Application.UseCases;
|
||||
@ -24,7 +25,11 @@ public sealed class IdentityRuntimeGrpcService(
|
||||
IssueIdentityTokenGrpcRequest request,
|
||||
ServerCallContext context)
|
||||
{
|
||||
var useCaseRequest = new IssueIdentityTokenRequest(request.SubjectId, request.TenantId);
|
||||
var useCaseRequest = new IssueIdentityTokenRequest(
|
||||
request.SubjectId,
|
||||
request.TenantId,
|
||||
ParseProvider(request.Provider),
|
||||
request.ExternalToken);
|
||||
var useCaseResponse = await issueIdentityTokenUseCase.HandleAsync(useCaseRequest);
|
||||
|
||||
return new IssueIdentityTokenGrpcResponse
|
||||
@ -47,7 +52,8 @@ public sealed class IdentityRuntimeGrpcService(
|
||||
var grpcContract = new EvaluateIdentityPolicyGrpcContract(
|
||||
request.SubjectId,
|
||||
request.TenantId,
|
||||
request.PermissionCode);
|
||||
request.PermissionCode,
|
||||
request.Provider);
|
||||
|
||||
var useCaseRequest = grpcContractAdapter.FromGrpc(grpcContract);
|
||||
var useCaseResponse = await evaluateIdentityPolicyUseCase.HandleAsync(useCaseRequest);
|
||||
@ -59,4 +65,11 @@ public sealed class IdentityRuntimeGrpcService(
|
||||
IsAllowed = useCaseResponse.IsAllowed
|
||||
};
|
||||
}
|
||||
|
||||
private static IdentityAuthProvider ParseProvider(string provider)
|
||||
{
|
||||
return Enum.TryParse<IdentityAuthProvider>(provider, true, out var parsedProvider)
|
||||
? parsedProvider
|
||||
: IdentityAuthProvider.InternalJwt;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using BuildingBlock.Identity.Contracts.Requests;
|
||||
using BuildingBlock.Identity.Contracts.Responses;
|
||||
using BuildingBlock.Identity.Contracts.Conventions;
|
||||
using Thalos.Service.Application.Ports;
|
||||
using Thalos.Service.Application.UseCases;
|
||||
using Thalos.Domain.Contracts;
|
||||
@ -27,7 +28,11 @@ public class EvaluateIdentityPolicyUseCaseTests
|
||||
{
|
||||
public IdentityPolicyContextRequest BuildPolicyContextRequest(EvaluateIdentityPolicyRequest request)
|
||||
{
|
||||
return new IdentityPolicyContextRequest(request.SubjectId, request.TenantId, request.PermissionCode);
|
||||
return new IdentityPolicyContextRequest(
|
||||
request.SubjectId,
|
||||
request.TenantId,
|
||||
request.PermissionCode,
|
||||
request.Provider);
|
||||
}
|
||||
|
||||
public EvaluateIdentityPolicyResponse Evaluate(
|
||||
@ -48,6 +53,7 @@ public class EvaluateIdentityPolicyUseCaseTests
|
||||
return Task.FromResult(new IdentityPolicyContextData(
|
||||
request.SubjectId,
|
||||
request.PermissionCode,
|
||||
IdentityAuthProvider.InternalJwt,
|
||||
true,
|
||||
[request.PermissionCode]));
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using BuildingBlock.Identity.Contracts.Requests;
|
||||
using BuildingBlock.Identity.Contracts.Conventions;
|
||||
using Thalos.Service.Application.Ports;
|
||||
using Thalos.Service.Application.UseCases;
|
||||
using Thalos.Domain.Contracts;
|
||||
@ -25,7 +26,7 @@ public class IssueIdentityTokenUseCaseTests
|
||||
{
|
||||
public Task<IdentityTokenData> ReadTokenAsync(IssueIdentityTokenRequest request)
|
||||
{
|
||||
return Task.FromResult(new IdentityTokenData("token-123", 3600));
|
||||
return Task.FromResult(new IdentityTokenData("token-123", 3600, IdentityAuthProvider.InternalJwt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using BuildingBlock.Identity.Contracts.Conventions;
|
||||
using BuildingBlock.Identity.Contracts.Requests;
|
||||
using Thalos.Service.Application.Adapters;
|
||||
using Thalos.Service.Application.DependencyInjection;
|
||||
@ -44,6 +45,26 @@ public class RuntimeWiringTests
|
||||
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 IssueIdentityTokenRequest(
|
||||
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()
|
||||
{
|
||||
@ -62,12 +83,17 @@ public class RuntimeWiringTests
|
||||
public void IdentityPolicyGrpcContractAdapter_WhenFromGrpc_UsesExpectedContractShape()
|
||||
{
|
||||
var adapter = new IdentityPolicyGrpcContractAdapter();
|
||||
var contract = new EvaluateIdentityPolicyGrpcContract("subject-9", "tenant-9", "identity.token.issue");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user