|
4 | 4 | using System.Security.Cryptography; |
5 | 5 | using System.Text; |
6 | 6 | using System.Threading.Tasks; |
| 7 | +using Certify.Models.Config; |
7 | 8 | using Certify.Models.Hub; |
8 | 9 | using Certify.Models.Providers; |
9 | 10 | using Certify.Providers; |
@@ -198,31 +199,56 @@ public async Task<SecurityPrinciple> GetSecurityPrincipleByUsername(string conte |
198 | 199 | return list?.SingleOrDefault(sp => sp.Username?.ToLowerInvariant() == username.ToLowerInvariant()); |
199 | 200 | } |
200 | 201 |
|
201 | | - public async Task<bool> IsAuthorised(string contextUserId, string principleId, string roleId, string resourceType, string actionId, string identifier) |
| 202 | + /// <summary> |
| 203 | + /// Check if a security principle has access to the given resource action |
| 204 | + /// </summary> |
| 205 | + /// <param name="contextUserId">Security principle performing access check</param> |
| 206 | + /// <param name="principleId">Security principle to check access for</param> |
| 207 | + /// <param name="resourceType">resource type being accessed</param> |
| 208 | + /// <param name="actionId">resource action required</param> |
| 209 | + /// <param name="identifier">optional resource identifier, if access is limited by specific resource</param> |
| 210 | + /// <param name="scopedAssignedRoles">optional scoped assigned roles to limit access to (for scoped access token checks etc)</param> |
| 211 | + /// <returns></returns> |
| 212 | + public async Task<bool> IsAuthorised(string contextUserId, string principleId, string resourceType, string actionId, string identifier = null, List<string> scopedAssignedRoles = null) |
202 | 213 | { |
203 | 214 | // to determine is a principle has access to perform a particular action |
204 | 215 | // for each group the principle is part of |
205 | 216 |
|
206 | | - // TODO: cache results for performance |
| 217 | + // TODO: cache results for performance based on last update of access control config, which will be largely static |
207 | 218 |
|
| 219 | + // get all assigned roles (all users) |
208 | 220 | var allAssignedRoles = await _store.GetItems<AssignedRole>(nameof(AssignedRole)); |
209 | 221 |
|
210 | | - var spAssigned = allAssignedRoles.Where(a => a.SecurityPrincipleId == principleId); |
211 | | - |
| 222 | + // get all defined roles |
212 | 223 | var allRoles = await _store.GetItems<Role>(nameof(Role)); |
213 | 224 |
|
214 | | - var spAssignedRoles = allRoles.Where(r => spAssigned.Any(t => t.RoleId == r.Id)); |
| 225 | + // get all defined policies |
| 226 | + var allPolicies = await _store.GetItems<ResourcePolicy>(nameof(ResourcePolicy)); |
215 | 227 |
|
216 | | - var spSpecificAssignedRoles = spAssigned.Where(a => spAssignedRoles.Any(r => r.Id == a.RoleId)); |
| 228 | + // get the assigned roles for this specific security principle |
| 229 | + var spAssignedRoles = allAssignedRoles.Where(a => a.SecurityPrincipleId == principleId); |
217 | 230 |
|
218 | | - var allPolicies = await _store.GetItems<ResourcePolicy>(nameof(ResourcePolicy)); |
| 231 | + // if scoped assigned role ID specified (access token check etc), reduce scope of assigned roles to check |
| 232 | + if (scopedAssignedRoles?.Any() == true) |
| 233 | + { |
| 234 | + spAssignedRoles = spAssignedRoles.Where(a => scopedAssignedRoles.Contains(a.Id)); |
| 235 | + } |
219 | 236 |
|
220 | | - var spAssignedPolicies = allPolicies.Where(r => spAssignedRoles.Any(p => p.Policies.Contains(r.Id))); |
| 237 | + // get all role definitions included in the principles assigned roles |
| 238 | + var spAssignedRoleDefinitions = allRoles.Where(r => spAssignedRoles.Any(t => t.RoleId == r.Id)); |
221 | 239 |
|
| 240 | + var spSpecificAssignedRoles = spAssignedRoles.Where(a => spAssignedRoleDefinitions.Any(r => r.Id == a.RoleId)); |
| 241 | + |
| 242 | + // get all resource policies included in the principles assigned roles |
| 243 | + var spAssignedPolicies = allPolicies.Where(r => spAssignedRoleDefinitions.Any(p => p.Policies.Contains(r.Id))); |
| 244 | + |
| 245 | + // check an assigned policy allows the required resource action |
222 | 246 | if (spAssignedPolicies.Any(a => a.ResourceActions.Contains(actionId))) |
223 | 247 | { |
224 | | - // if any of the service principles assigned roles are restricted by the type of resource type, |
| 248 | + |
| 249 | + // if any of the service principles assigned roles are restricted by resource type, |
225 | 250 | // check for identifier matches (e.g. role assignment restricted on domains ) |
| 251 | + |
226 | 252 | if (spSpecificAssignedRoles.Any(a => a.IncludedResources?.Any(r => r.ResourceType == resourceType) == true)) |
227 | 253 | { |
228 | 254 | var allIncludedResources = spSpecificAssignedRoles.SelectMany(a => a.IncludedResources).Distinct(); |
@@ -263,6 +289,36 @@ public async Task<bool> IsAuthorised(string contextUserId, string principleId, s |
263 | 289 | } |
264 | 290 | } |
265 | 291 |
|
| 292 | + public async Task<ActionResult> IsAccessTokenAuthorised(string contextUserId, AccessToken accessToken, string resourceType, string actionId, string identifier) |
| 293 | + { |
| 294 | + // resolve security principle from access token |
| 295 | + |
| 296 | + var assignedTokens = await _store.GetItems<AssignedAccessToken>(nameof(AssignedAccessToken)); |
| 297 | + |
| 298 | + // check if a non-expired/non-revoked access token exists matching the given client ID |
| 299 | + var knownAssignedToken = assignedTokens.SingleOrDefault(t => t.AccessTokens.Any(a => a.ClientId == accessToken.ClientId && a.Secret == accessToken.Secret && a.DateRevoked == null && (a.DateExpiry == null || a.DateExpiry >= DateTimeOffset.UtcNow))); |
| 300 | + |
| 301 | + if (knownAssignedToken == null) |
| 302 | + { |
| 303 | + return new ActionResult("Access token unknown, expired or revoked.", false); |
| 304 | + } |
| 305 | + |
| 306 | + // check related principle has access |
| 307 | + |
| 308 | + var isAuthorised = await IsAuthorised(contextUserId, knownAssignedToken.SecurityPrincipleId, resourceType, actionId, identifier, knownAssignedToken.ScopedAssignedRoles); |
| 309 | + |
| 310 | + if (isAuthorised) |
| 311 | + { |
| 312 | + // TODO: check token scope restrictions |
| 313 | + |
| 314 | + return new ActionResult("OK", true); |
| 315 | + } |
| 316 | + else |
| 317 | + { |
| 318 | + return new ActionResult("Access token not authorized or invalid for action, resource or identifier", false); |
| 319 | + } |
| 320 | + } |
| 321 | + |
266 | 322 | /// <summary> |
267 | 323 | /// Check security principle is in a given role at the system level |
268 | 324 | /// </summary> |
@@ -398,6 +454,11 @@ public async Task AddAssignedRole(AssignedRole r) |
398 | 454 | await _store.Add(nameof(AssignedRole), r); |
399 | 455 | } |
400 | 456 |
|
| 457 | + public async Task AddAssignedAccessToken(AssignedAccessToken t) |
| 458 | + { |
| 459 | + await _store.Add(nameof(AssignedAccessToken), t); |
| 460 | + } |
| 461 | + |
401 | 462 | public async Task AddResourceAction(ResourceAction action) |
402 | 463 | { |
403 | 464 | await _store.Add(nameof(ResourceAction), action); |
|
0 commit comments