Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions src/BuildingBlocks/Security/Authorization/PolicyEvaluator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace OpenHealthOS.Security.Authorization;

using OpenHealthOS.Contracts.Authorization;

/// <summary>
/// Evaluates authorization requests using an authorization policy set.
/// </summary>
public sealed class PolicyEvaluator : IAuthorizationEvaluator
{
private readonly IAuthorizationPolicySet _policySet;

/// <summary>
/// Initializes a new instance of the <see cref="PolicyEvaluator"/> class.
/// </summary>
/// <param name="policySet">
/// The authorization policy set used to evaluate requests.
/// </param>
public PolicyEvaluator(IAuthorizationPolicySet policySet)
{
ArgumentNullException.ThrowIfNull(policySet);

_policySet = policySet;
}

/// <summary>
/// Evaluates whether the authorization context satisfies the policy set.
/// </summary>
/// <param name="context">
/// The authorization context to evaluate.
/// </param>
/// <returns>
/// <see cref="AuthorizationDecision.Allow"/> when all policies are
/// satisfied; otherwise <see cref="AuthorizationDecision.Deny"/>.
/// </returns>
public AuthorizationDecision Evaluate(AuthorizationContextDto context)
{
ArgumentNullException.ThrowIfNull(context);

return _policySet.IsSatisfied(context)
? AuthorizationDecision.Allow
: AuthorizationDecision.Deny;
}
}
124 changes: 124 additions & 0 deletions tests/UnitTests/Authorization/PolicyEvaluatorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
namespace OpenHealthOS.UnitTests.Authorization;

using OpenHealthOS.Contracts.Authorization;
using OpenHealthOS.Contracts.Identity;
using OpenHealthOS.Security.Authorization;
using Xunit;

public sealed class PolicyEvaluatorTests
{
[Fact]
public void Evaluate_ShouldAllow_WhenPolicySetIsSatisfied()
{
var policySet = new AuthorizationPolicySet(
[
new PermissionPolicy(new Permission("patient.read")),
]);

var evaluator = new PolicyEvaluator(policySet);

var context = CreateContext("patient.read");

var result = evaluator.Evaluate(context);

Assert.Equal(
AuthorizationDecision.Allow,
result);
}

[Fact]
public void Evaluate_ShouldDeny_WhenPolicySetIsNotSatisfied()
{
var policySet = new AuthorizationPolicySet(
[
new PermissionPolicy(new Permission("patient.write")),
]);

var evaluator = new PolicyEvaluator(policySet);

var context = CreateContext("patient.read");

var result = evaluator.Evaluate(context);

Assert.Equal(
AuthorizationDecision.Deny,
result);
}

[Fact]
public void Evaluate_ShouldAllow_WhenAllPoliciesAreSatisfied()
{
var policySet = new AuthorizationPolicySet(
[
new PermissionPolicy(new Permission("patient.read")),
new PermissionPolicy(new Permission("patient.read")),
]);

var evaluator = new PolicyEvaluator(policySet);

var context = CreateContext("patient.read");

var result = evaluator.Evaluate(context);

Assert.Equal(
AuthorizationDecision.Allow,
result);
}

[Fact]
public void Evaluate_ShouldDeny_WhenAnyPolicyIsNotSatisfied()
{
var policySet = new AuthorizationPolicySet(
[
new PermissionPolicy(new Permission("patient.read")),
new PermissionPolicy(new Permission("patient.write")),
]);

var evaluator = new PolicyEvaluator(policySet);

var context = CreateContext("patient.read");

var result = evaluator.Evaluate(context);

Assert.Equal(
AuthorizationDecision.Deny,
result);
}

[Fact]
public void Constructor_ShouldThrow_WhenPolicySetIsNull()
{
Assert.Throws<ArgumentNullException>(
() => new PolicyEvaluator(null!));
}

[Fact]
public void Evaluate_ShouldThrow_WhenContextIsNull()
{
var policySet = new AuthorizationPolicySet(
[
new PermissionPolicy(new Permission("patient.read")),
]);

var evaluator = new PolicyEvaluator(policySet);

Assert.Throws<ArgumentNullException>(
() => evaluator.Evaluate(null!));
}

private static AuthorizationContextDto CreateContext(
string permission)
{
return new AuthorizationContextDto
{
Identity = new IdentityContextDto
{
SubjectId = "test-user",
PrincipalType = PrincipalType.User,
Scopes = [permission],
},
Permission = new Permission(permission),
ResourceType = "Patient",
};
}
}
Loading