Skip to content

Commit 25bf2bf

Browse files
committed
Authorization Completion & Tests
1 parent 031a9bc commit 25bf2bf

File tree

21 files changed

+370
-232
lines changed

21 files changed

+370
-232
lines changed

src/CodeBeam.UltimateAuth.Core/Abstractions/Stores/InMemoryVersionedStore.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,16 @@ public Task DeleteAsync(TKey key, long expectedVersion, DeleteMode mode, DateTim
105105
return Task.CompletedTask;
106106
}
107107

108+
/// <summary>
109+
/// Returns a read-only list containing the current snapshot of all entities stored in memory.
110+
/// </summary>
108111
protected IReadOnlyList<TEntity> Values() => _store.Values.Select(Snapshot).ToList().AsReadOnly();
109112

113+
/// <summary>
114+
/// Returns an enumerable collection of all entities currently stored in memory.
115+
/// Useful in hooks when change entity value directly.
116+
/// </summary>
117+
protected IEnumerable<TEntity> InternalValues() => _store.Values;
118+
110119
protected bool TryGet(TKey key, out TEntity? entity) => _store.TryGetValue(key, out entity);
111120
}

src/CodeBeam.UltimateAuth.Core/Constants/UAuthConstants.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
public static class UAuthConstants
44
{
5+
public static class Access
6+
{
7+
public const string Permissions = "permissions";
8+
}
9+
10+
public static class Claims
11+
{
12+
public const string Tenant = "uauth:tenant";
13+
public const string Permission = "uauth:permission";
14+
}
15+
516
public static class HttpItems
617
{
718
public const string SessionContext = "__UAuth.SessionContext";
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace CodeBeam.UltimateAuth.Core.Contracts;
2+
3+
public enum ActionScope
4+
{
5+
Anonymous,
6+
Self,
7+
Admin,
8+
System
9+
}

src/CodeBeam.UltimateAuth.Core/Contracts/Authority/AccessContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ internal AccessContext(
4040
bool isAuthenticated,
4141
bool isSystemActor,
4242
SessionChainId? actorChainId,
43-
string resource,
43+
string? resource,
4444
UserKey? targetUserKey,
4545
TenantKey resourceTenant,
4646
string action,

src/CodeBeam.UltimateAuth.Core/Defaults/UAuthActions.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
1-
namespace CodeBeam.UltimateAuth.Core.Defaults;
1+
using CodeBeam.UltimateAuth.Core.Contracts;
2+
3+
namespace CodeBeam.UltimateAuth.Core.Defaults;
24

35
public static class UAuthActions
46
{
7+
public static string Create(string resource, string operation, ActionScope scope, string? subResource = null)
8+
{
9+
if (string.IsNullOrWhiteSpace(resource))
10+
throw new ArgumentException("resource required");
11+
12+
if (string.IsNullOrWhiteSpace(operation))
13+
throw new ArgumentException("operation required");
14+
15+
var scopePart = scope.ToString().ToLowerInvariant();
16+
17+
return subResource is null
18+
? $"{resource}.{operation}.{scopePart}"
19+
: $"{resource}.{subResource}.{operation}.{scopePart}";
20+
}
21+
522
public static class Sessions
623
{
724
public const string GetChainSelf = "sessions.getchain.self";

src/CodeBeam.UltimateAuth.Server/Infrastructure/Orchestrator/UAuthAccessOrchestrator.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using CodeBeam.UltimateAuth.Authorization;
2+
using CodeBeam.UltimateAuth.Authorization.Contracts;
23
using CodeBeam.UltimateAuth.Core.Abstractions;
4+
using CodeBeam.UltimateAuth.Core.Constants;
35
using CodeBeam.UltimateAuth.Core.Contracts;
46
using CodeBeam.UltimateAuth.Core.Errors;
57
using CodeBeam.UltimateAuth.Policies.Abstractions;
@@ -61,6 +63,7 @@ private async Task<AccessContext> EnrichAsync(AccessContext context, Cancellatio
6163
return context;
6264

6365
var perms = await _permissions.GetPermissionsAsync(context.ResourceTenant, context.ActorUserKey.Value, ct);
64-
return context.WithAttribute("permissions", perms);
66+
var compiled = new CompiledPermissionSet(perms);
67+
return context.WithAttribute(UAuthConstants.Access.Permissions, compiled);
6568
}
6669
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
namespace CodeBeam.UltimateAuth.Authorization.Contracts;
2+
3+
public sealed class CompiledPermissionSet
4+
{
5+
private readonly HashSet<string> _exact = new();
6+
private readonly HashSet<string> _prefix = new();
7+
private readonly bool _hasWildcard;
8+
9+
public CompiledPermissionSet(IEnumerable<Permission> permissions)
10+
{
11+
foreach (var p in permissions)
12+
{
13+
var value = p.Value;
14+
15+
if (value == "*")
16+
{
17+
_hasWildcard = true;
18+
continue;
19+
}
20+
21+
if (value.EndsWith(".*"))
22+
{
23+
_prefix.Add(value[..^2]);
24+
continue;
25+
}
26+
27+
_exact.Add(value);
28+
}
29+
}
30+
31+
public bool IsAllowed(string action)
32+
{
33+
if (_hasWildcard)
34+
return true;
35+
36+
if (_exact.Contains(action))
37+
return true;
38+
39+
foreach (var prefix in _prefix)
40+
{
41+
if (action.StartsWith(prefix + ".", StringComparison.OrdinalIgnoreCase))
42+
return true;
43+
}
44+
45+
return false;
46+
}
47+
}
Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,36 @@
11
using CodeBeam.UltimateAuth.Authorization.Contracts;
2-
using CodeBeam.UltimateAuth.Authorization.Domain;
3-
using CodeBeam.UltimateAuth.Core.Abstractions;
42
using CodeBeam.UltimateAuth.Core.Contracts;
5-
using CodeBeam.UltimateAuth.Policies.Abstractions;
3+
using CodeBeam.UltimateAuth.Core.Errors;
4+
using CodeBeam.UltimateAuth.Server.Infrastructure;
65

76
namespace CodeBeam.UltimateAuth.Authorization.Reference;
87

98
internal sealed class AuthorizationService : IAuthorizationService
109
{
11-
private readonly IUserPermissionStore _permissions;
12-
private readonly IAccessPolicyProvider _policyProvider;
13-
private readonly IAccessAuthority _accessAuthority;
10+
private readonly IAccessOrchestrator _accessOrchestrator;
1411

15-
public AuthorizationService(IUserPermissionStore permissions, IAccessPolicyProvider policyProvider, IAccessAuthority accessAuthority)
12+
public AuthorizationService(IAccessOrchestrator accessOrchestrator)
1613
{
17-
_permissions = permissions;
18-
_policyProvider = policyProvider;
19-
_accessAuthority = accessAuthority;
14+
_accessOrchestrator = accessOrchestrator;
2015
}
2116

2217
public async Task<AuthorizationResult> AuthorizeAsync(AccessContext context, CancellationToken ct = default)
2318
{
2419
ct.ThrowIfCancellationRequested();
2520

26-
IReadOnlyCollection<Permission> permissions = Array.Empty<Permission>();
21+
var cmd = new AccessCommand<AuthorizationResult>(innerCt =>
22+
{
23+
return Task.FromResult(AuthorizationResult.Allow());
24+
});
2725

28-
if (context.ActorUserKey is not null)
26+
try
2927
{
30-
permissions = await _permissions.GetPermissionsAsync(context.ResourceTenant, context.ActorUserKey.Value, ct);
28+
await _accessOrchestrator.ExecuteAsync(context, cmd, ct);
29+
return AuthorizationResult.Allow();
30+
}
31+
catch (UAuthAuthorizationException ex)
32+
{
33+
return AuthorizationResult.Deny(ex.Message);
3134
}
32-
33-
var enrichedContext = context.WithAttribute("permissions", permissions);
34-
35-
var policies = _policyProvider.GetPolicies(enrichedContext);
36-
var decision = _accessAuthority.Decide(enrichedContext, policies);
37-
38-
if (decision.RequiresReauthentication)
39-
return AuthorizationResult.ReauthRequired();
40-
41-
return decision.IsAllowed
42-
? AuthorizationResult.Allow()
43-
: AuthorizationResult.Deny(decision.DenyReason);
4435
}
4536
}

src/authorization/CodeBeam.UltimateAuth.Authorization/AuthorizationClaimsProvider.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using CodeBeam.UltimateAuth.Core;
2+
using CodeBeam.UltimateAuth.Core.Constants;
23
using CodeBeam.UltimateAuth.Core.Domain;
34
using CodeBeam.UltimateAuth.Core.MultiTenancy;
45
using System.Security.Claims;
@@ -26,13 +27,13 @@ public async Task<ClaimsSnapshot> GetClaimsAsync(TenantKey tenant, UserKey userK
2627

2728
var builder = ClaimsSnapshot.Create();
2829

29-
builder.Add("uauth:tenant", tenant.Value);
30+
builder.Add(UAuthConstants.Claims.Tenant, tenant.Value);
3031

3132
foreach (var role in roles)
3233
builder.Add(ClaimTypes.Role, role.Name);
3334

3435
foreach (var perm in perms)
35-
builder.Add("uauth:permission", perm.Value);
36+
builder.Add(UAuthConstants.Claims.Permission, perm.Value);
3637

3738
return builder.Build();
3839
}

src/policies/CodeBeam.UltimateAuth.Policies/Defaults/DefaultPolicySet.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ public static void Register(AccessPolicyRegistry registry)
1616

1717
// Intent-based
1818
registry.Add("", _ => new RequireSelfPolicy());
19-
registry.Add("", _ => new RequireAdminPolicy());
20-
registry.Add("", _ => new RequireSelfOrAdminPolicy());
2119
registry.Add("", _ => new RequireSystemPolicy());
2220

2321
// Permission

0 commit comments

Comments
 (0)