|
1 | | -using CodeBeam.UltimateAuth.Core.Domain; |
| 1 | +using CodeBeam.UltimateAuth.Core.Contracts; |
| 2 | +using CodeBeam.UltimateAuth.Core.Domain; |
2 | 3 | using Microsoft.AspNetCore.Authorization; |
3 | 4 | using Microsoft.AspNetCore.Components; |
4 | 5 |
|
@@ -40,11 +41,29 @@ public partial class UAuthStateView : UAuthReactiveComponentBase |
40 | 41 | public string? Policy { get; set; } |
41 | 42 |
|
42 | 43 | /// <summary> |
43 | | - /// Gets or sets a value indicating whether all set conditions must be matched for the operation to succeed. |
44 | | - /// Null parameters don't count as condition. |
| 44 | + /// Determines how authorization conditions are evaluated. |
| 45 | + /// |
| 46 | + /// <para> |
| 47 | + /// <see cref="AuthorizationMatchMode.Any"/>: |
| 48 | + /// Any configured condition may succeed. |
| 49 | + /// </para> |
| 50 | + /// |
| 51 | + /// <para> |
| 52 | + /// <see cref="AuthorizationMatchMode.All"/>: |
| 53 | + /// All configured conditions and values must succeed. |
| 54 | + /// </para> |
| 55 | + /// |
| 56 | + /// <para> |
| 57 | + /// <see cref="AuthorizationMatchMode.Category"/>: |
| 58 | + /// At least one value from each configured category must succeed. |
| 59 | + /// For example: |
| 60 | + /// one matching role AND one matching permission. |
| 61 | + /// </para> |
| 62 | + /// |
| 63 | + /// Null or empty parameters are ignored. |
45 | 64 | /// </summary> |
46 | 65 | [Parameter] |
47 | | - public bool MatchAll { get; set; } = true; |
| 66 | + public AuthorizationMatchMode MatchMode { get; set; } = AuthorizationMatchMode.Category; |
48 | 67 |
|
49 | 68 | [Parameter] |
50 | 69 | public bool RequireActive { get; set; } = true; |
@@ -92,34 +111,67 @@ private async Task<bool> EvaluateAuthorizationAsync() |
92 | 111 | if (!AuthState.IsAuthenticated) |
93 | 112 | return false; |
94 | 113 |
|
95 | | - var roles = _rolesParsed; |
96 | | - var permissions = _permissionsParsed; |
| 114 | + var roleResults = _rolesParsed |
| 115 | + .Select(AuthState.IsInRole) |
| 116 | + .ToList(); |
97 | 117 |
|
98 | | - var results = new List<bool>(); |
| 118 | + var permissionResults = _permissionsParsed |
| 119 | + .Select(AuthState.HasPermission) |
| 120 | + .ToList(); |
99 | 121 |
|
100 | | - if (roles.Count > 0) |
| 122 | + bool? policyResult = null; |
| 123 | + |
| 124 | + if (!string.IsNullOrWhiteSpace(Policy)) |
101 | 125 | { |
102 | | - results.Add(MatchAll |
103 | | - ? roles.All(AuthState.IsInRole) |
104 | | - : roles.Any(AuthState.IsInRole)); |
| 126 | + policyResult = await EvaluatePolicyAsync(); |
105 | 127 | } |
106 | 128 |
|
107 | | - if (permissions.Count > 0) |
| 129 | + return MatchMode switch |
108 | 130 | { |
109 | | - results.Add(MatchAll |
110 | | - ? permissions.All(AuthState.HasPermission) |
111 | | - : permissions.Any(AuthState.HasPermission)); |
112 | | - } |
| 131 | + AuthorizationMatchMode.Any |
| 132 | + => EvaluateAny(roleResults, permissionResults, policyResult), |
113 | 133 |
|
114 | | - if (!string.IsNullOrWhiteSpace(Policy)) |
115 | | - results.Add(await EvaluatePolicyAsync()); |
| 134 | + AuthorizationMatchMode.All |
| 135 | + => EvaluateAll(roleResults, permissionResults, policyResult), |
116 | 136 |
|
117 | | - if (results.Count == 0) |
118 | | - return true; |
| 137 | + AuthorizationMatchMode.Category |
| 138 | + => EvaluateCategory(roleResults, permissionResults, policyResult), |
| 139 | + |
| 140 | + _ => false |
| 141 | + }; |
| 142 | + } |
| 143 | + |
| 144 | + private static bool EvaluateAny(IReadOnlyList<bool> roles, IReadOnlyList<bool> permissions, bool? policy) |
| 145 | + { |
| 146 | + return roles.Any(x => x) || permissions.Any(x => x) || policy == true; |
| 147 | + } |
| 148 | + |
| 149 | + private static bool EvaluateAll(IReadOnlyList<bool> roles, IReadOnlyList<bool> permissions, bool? policy) |
| 150 | + { |
| 151 | + if (roles.Count > 0 && roles.Any(x => !x)) |
| 152 | + return false; |
| 153 | + |
| 154 | + if (permissions.Count > 0 && permissions.Any(x => !x)) |
| 155 | + return false; |
| 156 | + |
| 157 | + if (policy.HasValue && !policy.Value) |
| 158 | + return false; |
| 159 | + |
| 160 | + return true; |
| 161 | + } |
| 162 | + |
| 163 | + private static bool EvaluateCategory(IReadOnlyList<bool> roles, IReadOnlyList<bool> permissions, bool? policy) |
| 164 | + { |
| 165 | + if (roles.Count > 0 && !roles.Any(x => x)) |
| 166 | + return false; |
| 167 | + |
| 168 | + if (permissions.Count > 0 && !permissions.Any(x => x)) |
| 169 | + return false; |
| 170 | + |
| 171 | + if (policy.HasValue && !policy.Value) |
| 172 | + return false; |
119 | 173 |
|
120 | | - return MatchAll |
121 | | - ? results.All(x => x) |
122 | | - : results.Any(x => x); |
| 174 | + return true; |
123 | 175 | } |
124 | 176 |
|
125 | 177 | private void EvaluateSessionState() |
@@ -171,6 +223,6 @@ private async Task<bool> EvaluatePolicyAsync() |
171 | 223 |
|
172 | 224 | private string BuildAuthKey() |
173 | 225 | { |
174 | | - return $"{Roles}|{Permissions}|{Policy}|{MatchAll}"; |
| 226 | + return $"{Roles}|{Permissions}|{Policy}|{MatchMode}"; |
175 | 227 | } |
176 | 228 | } |
0 commit comments