feat(identity): add policy contract integration
- WHY: enforce identity-only contract boundaries for policy orchestration - WHAT: add thalos-owned policy contracts, adapters, and grpc translation surfaces - RULE: apply workspace dependency graph and identity ownership constraints
This commit is contained in:
parent
8dd1ee67d0
commit
ab4013fcf4
@ -5,25 +5,32 @@ package "thalos-service" {
|
|||||||
package "Thalos.Service.Identity.Abstractions" {
|
package "Thalos.Service.Identity.Abstractions" {
|
||||||
class IssueIdentityTokenRequest
|
class IssueIdentityTokenRequest
|
||||||
class IssueIdentityTokenResponse
|
class IssueIdentityTokenResponse
|
||||||
|
class EvaluateIdentityPolicyRequest
|
||||||
|
class EvaluateIdentityPolicyResponse
|
||||||
|
class IdentityPolicyContextRequest
|
||||||
|
class IdentityPolicyContextResponse
|
||||||
|
class ThalosIdentityPackageContract
|
||||||
interface IdentityAbstractionBoundary
|
interface IdentityAbstractionBoundary
|
||||||
}
|
}
|
||||||
|
|
||||||
package "Thalos.Service.Application" {
|
package "Thalos.Service.Application" {
|
||||||
interface IIssueIdentityTokenUseCase
|
interface IIssueIdentityTokenUseCase
|
||||||
class IssueIdentityTokenUseCase
|
class IssueIdentityTokenUseCase
|
||||||
|
interface IEvaluateIdentityPolicyUseCase
|
||||||
|
class EvaluateIdentityPolicyUseCase
|
||||||
interface IIdentityTokenReadPort
|
interface IIdentityTokenReadPort
|
||||||
|
interface IIdentityPolicyContextReadPort
|
||||||
|
interface IIdentityCapabilityContractAdapter
|
||||||
|
interface IIdentityPolicyGrpcContractAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
package "Thalos.Service.Grpc" {
|
package "Thalos.Service.Grpc" {
|
||||||
class Program
|
class Program
|
||||||
}
|
}
|
||||||
|
|
||||||
IssueIdentityTokenUseCase ..|> IIssueIdentityTokenUseCase
|
|
||||||
IssueIdentityTokenUseCase --> IIdentityTokenReadPort
|
|
||||||
IIssueIdentityTokenUseCase --> IssueIdentityTokenRequest
|
|
||||||
IIssueIdentityTokenUseCase --> IssueIdentityTokenResponse
|
|
||||||
}
|
}
|
||||||
|
|
||||||
package "thalos-dal" as ThalosDal
|
package "thalos-dal" as ThalosDal
|
||||||
|
|
||||||
|
IIdentityPolicyContextReadPort ..> ThalosDal
|
||||||
IIdentityTokenReadPort ..> ThalosDal
|
IIdentityTokenReadPort ..> ThalosDal
|
||||||
@enduml
|
@enduml
|
||||||
|
|||||||
@ -3,10 +3,18 @@
|
|||||||
## Use-Case Boundaries
|
## Use-Case Boundaries
|
||||||
|
|
||||||
- `IIssueIdentityTokenUseCase`: orchestrates token issuance behavior.
|
- `IIssueIdentityTokenUseCase`: orchestrates token issuance behavior.
|
||||||
|
- `IEvaluateIdentityPolicyUseCase`: orchestrates policy evaluation behavior.
|
||||||
- `IIdentityTokenReadPort`: DAL-facing identity token boundary.
|
- `IIdentityTokenReadPort`: DAL-facing identity token boundary.
|
||||||
|
- `IIdentityPolicyContextReadPort`: DAL/integration-facing identity policy context boundary.
|
||||||
|
|
||||||
|
## Contract Integration
|
||||||
|
|
||||||
|
- Policy orchestration uses Thalos-owned transport-neutral identity contracts.
|
||||||
|
- gRPC translation boundaries are isolated behind `IIdentityPolicyGrpcContractAdapter`.
|
||||||
|
- Service contracts remain transport-neutral at the application boundary.
|
||||||
|
|
||||||
## Policy Baseline
|
## Policy Baseline
|
||||||
|
|
||||||
- Token issuance policy is orchestrated in service use cases.
|
- Token issuance and policy evaluation are orchestrated in service use cases.
|
||||||
- Data retrieval and persistence details remain in thalos-dal.
|
- Data retrieval and persistence details remain in thalos-dal and identity adapters.
|
||||||
- Protocol adaptation remains outside use-case logic.
|
- Protocol adaptation remains outside use-case logic.
|
||||||
|
|||||||
@ -0,0 +1,26 @@
|
|||||||
|
using Thalos.Service.Identity.Abstractions.Contracts;
|
||||||
|
|
||||||
|
namespace Thalos.Service.Application.Adapters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines adapter boundary for integrating identity contracts into policy use cases.
|
||||||
|
/// </summary>
|
||||||
|
public interface IIdentityCapabilityContractAdapter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a transport-neutral context request for policy evaluation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="identityRequest">Identity policy request.</param>
|
||||||
|
/// <returns>Identity policy context request.</returns>
|
||||||
|
IdentityPolicyContextRequest CreatePolicyContext(EvaluateIdentityPolicyRequest identityRequest);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps policy context response into identity policy response.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="identityRequest">Identity policy request.</param>
|
||||||
|
/// <param name="contextResponse">Identity policy context response.</param>
|
||||||
|
/// <returns>Identity policy response.</returns>
|
||||||
|
EvaluateIdentityPolicyResponse MapPolicyResponse(
|
||||||
|
EvaluateIdentityPolicyRequest identityRequest,
|
||||||
|
IdentityPolicyContextResponse contextResponse);
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
using Thalos.Service.Application.Grpc;
|
||||||
|
using Thalos.Service.Identity.Abstractions.Contracts;
|
||||||
|
|
||||||
|
namespace Thalos.Service.Application.Adapters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines adapter boundary for gRPC contract translation of identity policy flows.
|
||||||
|
/// </summary>
|
||||||
|
public interface IIdentityPolicyGrpcContractAdapter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Maps transport-neutral request into gRPC contract shape.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">Identity policy request.</param>
|
||||||
|
/// <returns>gRPC policy contract.</returns>
|
||||||
|
EvaluateIdentityPolicyGrpcContract ToGrpc(EvaluateIdentityPolicyRequest request);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps gRPC contract into transport-neutral request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="contract">gRPC policy contract.</param>
|
||||||
|
/// <returns>Identity policy request.</returns>
|
||||||
|
EvaluateIdentityPolicyRequest FromGrpc(EvaluateIdentityPolicyGrpcContract contract);
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
namespace Thalos.Service.Application.Grpc;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines minimal gRPC contract shape for identity policy adapter translation.
|
||||||
|
/// </summary>
|
||||||
|
/// <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);
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
using Thalos.Service.Identity.Abstractions.Contracts;
|
||||||
|
|
||||||
|
namespace Thalos.Service.Application.Ports;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines DAL/integration read boundary for identity policy context contracts.
|
||||||
|
/// </summary>
|
||||||
|
public interface IIdentityPolicyContextReadPort
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Reads identity policy context for policy evaluation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">Identity policy context request.</param>
|
||||||
|
/// <returns>Identity policy context response.</returns>
|
||||||
|
Task<IdentityPolicyContextResponse> ReadPolicyContextAsync(IdentityPolicyContextRequest request);
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
using Thalos.Service.Application.Adapters;
|
||||||
|
using Thalos.Service.Application.Ports;
|
||||||
|
using Thalos.Service.Identity.Abstractions.Contracts;
|
||||||
|
|
||||||
|
namespace Thalos.Service.Application.UseCases;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default orchestration implementation for identity policy evaluation.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class EvaluateIdentityPolicyUseCase(
|
||||||
|
IIdentityCapabilityContractAdapter contractAdapter,
|
||||||
|
IIdentityPolicyContextReadPort policyContextReadPort)
|
||||||
|
: IEvaluateIdentityPolicyUseCase
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<EvaluateIdentityPolicyResponse> HandleAsync(EvaluateIdentityPolicyRequest request)
|
||||||
|
{
|
||||||
|
var policyContextRequest = contractAdapter.CreatePolicyContext(request);
|
||||||
|
var policyContextResponse = await policyContextReadPort.ReadPolicyContextAsync(policyContextRequest);
|
||||||
|
|
||||||
|
return contractAdapter.MapPolicyResponse(request, policyContextResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
using Thalos.Service.Identity.Abstractions.Contracts;
|
||||||
|
|
||||||
|
namespace Thalos.Service.Application.UseCases;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines orchestration boundary for identity policy evaluation.
|
||||||
|
/// </summary>
|
||||||
|
public interface IEvaluateIdentityPolicyUseCase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Handles identity policy evaluation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">Identity policy request contract.</param>
|
||||||
|
/// <returns>Identity policy response contract.</returns>
|
||||||
|
Task<EvaluateIdentityPolicyResponse> HandleAsync(EvaluateIdentityPolicyRequest request);
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
namespace Thalos.Service.Identity.Abstractions.Contracts;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transport-neutral request contract for identity policy evaluation.
|
||||||
|
/// </summary>
|
||||||
|
/// <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 EvaluateIdentityPolicyRequest(string SubjectId, string TenantId, string PermissionCode);
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
namespace Thalos.Service.Identity.Abstractions.Contracts;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transport-neutral response contract for identity policy evaluation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="SubjectId">Identity subject identifier.</param>
|
||||||
|
/// <param name="PermissionCode">Permission code evaluated.</param>
|
||||||
|
/// <param name="IsAllowed">Policy result.</param>
|
||||||
|
public sealed record EvaluateIdentityPolicyResponse(string SubjectId, string PermissionCode, bool IsAllowed);
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
namespace Thalos.Service.Identity.Abstractions.Contracts;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transport-neutral request contract for identity policy context retrieval.
|
||||||
|
/// </summary>
|
||||||
|
/// <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 IdentityPolicyContextRequest(string SubjectId, string TenantId, string PermissionCode);
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
namespace Thalos.Service.Identity.Abstractions.Contracts;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transport-neutral response contract for identity policy context retrieval.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="SubjectId">Identity subject identifier.</param>
|
||||||
|
/// <param name="PermissionCode">Permission code evaluated.</param>
|
||||||
|
/// <param name="ContextSatisfied">Indicates whether context satisfies policy preconditions.</param>
|
||||||
|
public sealed record IdentityPolicyContextResponse(string SubjectId, string PermissionCode, bool ContextSatisfied);
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
using Core.Blueprint.Common.Contracts;
|
||||||
|
|
||||||
|
namespace Thalos.Service.Identity.Abstractions.Contracts;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines package descriptor metadata for Thalos identity abstractions.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ThalosIdentityPackageContract : IBlueprintPackageContract
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public BlueprintPackageDescriptor Descriptor { get; } = new(
|
||||||
|
"Thalos.Service.Identity.Abstractions",
|
||||||
|
PackageVersionPolicy.Minor,
|
||||||
|
["Core.Blueprint.Common"]);
|
||||||
|
}
|
||||||
@ -4,4 +4,7 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\blueprint-platform\src\Core.Blueprint.Common\Core.Blueprint.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -0,0 +1,49 @@
|
|||||||
|
using Thalos.Service.Application.Adapters;
|
||||||
|
using Thalos.Service.Application.Ports;
|
||||||
|
using Thalos.Service.Application.UseCases;
|
||||||
|
using Thalos.Service.Identity.Abstractions.Contracts;
|
||||||
|
|
||||||
|
namespace Thalos.Service.Application.UnitTests;
|
||||||
|
|
||||||
|
public class EvaluateIdentityPolicyUseCaseTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task HandleAsync_WhenCalled_UsesIdentityContractsAndReturnsMappedResponse()
|
||||||
|
{
|
||||||
|
var useCase = new EvaluateIdentityPolicyUseCase(
|
||||||
|
new FakeIdentityCapabilityContractAdapter(),
|
||||||
|
new FakeIdentityPolicyContextReadPort());
|
||||||
|
|
||||||
|
var response = await useCase.HandleAsync(new EvaluateIdentityPolicyRequest("subject-1", "tenant-1", "perm.read"));
|
||||||
|
|
||||||
|
Assert.Equal("subject-1", response.SubjectId);
|
||||||
|
Assert.Equal("perm.read", response.PermissionCode);
|
||||||
|
Assert.True(response.IsAllowed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class FakeIdentityCapabilityContractAdapter : IIdentityCapabilityContractAdapter
|
||||||
|
{
|
||||||
|
public IdentityPolicyContextRequest CreatePolicyContext(EvaluateIdentityPolicyRequest identityRequest)
|
||||||
|
{
|
||||||
|
return new IdentityPolicyContextRequest(identityRequest.SubjectId, identityRequest.TenantId, identityRequest.PermissionCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EvaluateIdentityPolicyResponse MapPolicyResponse(
|
||||||
|
EvaluateIdentityPolicyRequest identityRequest,
|
||||||
|
IdentityPolicyContextResponse contextResponse)
|
||||||
|
{
|
||||||
|
return new EvaluateIdentityPolicyResponse(
|
||||||
|
identityRequest.SubjectId,
|
||||||
|
identityRequest.PermissionCode,
|
||||||
|
contextResponse.ContextSatisfied);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class FakeIdentityPolicyContextReadPort : IIdentityPolicyContextReadPort
|
||||||
|
{
|
||||||
|
public Task<IdentityPolicyContextResponse> ReadPolicyContextAsync(IdentityPolicyContextRequest request)
|
||||||
|
{
|
||||||
|
return Task.FromResult(new IdentityPolicyContextResponse(request.SubjectId, request.PermissionCode, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user