Skip to content

Commit 021a1c7

Browse files
Implement API token access checks and refactor access checks
1 parent d1b8c03 commit 021a1c7

File tree

13 files changed

+294
-154
lines changed

13 files changed

+294
-154
lines changed

src/Certify.Client/CertifyApiClient.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -749,8 +749,13 @@ public async Task<List<SecurityPrinciple>> GetAccessSecurityPrinciples(AuthConte
749749
return JsonToObject<List<SecurityPrinciple>>(result);
750750
}
751751

752-
#endregion
752+
public async Task<Certify.Models.Config.ActionResult> CheckApiTokenHasAccess(AccessToken token, AccessCheck check, AuthContext authContext = null)
753+
{
754+
var result = await PostAsync("access/checkapitoken", new AccessTokenCheck { Check = check, Token = token }, authContext);
755+
return JsonConvert.DeserializeObject<ActionResult>(await result.Content.ReadAsStringAsync());
756+
}
753757

758+
#endregion
754759
private T JsonToObject<T>(string json)
755760
{
756761
return JsonConvert.DeserializeObject<T>(json);

src/Certify.Client/ICertifyClient.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public partial interface ICertifyInternalApiClient
2929
Task<List<ActionStep>> TestDataStoreConnection(DataStoreConnection dataStoreConnection, AuthContext authContext = null);
3030

3131
Task<ActionStep> UpdateManagementHub(string url, string joiningKey, AuthContext authContext = null);
32+
Task<Certify.Models.Config.ActionResult> CheckApiTokenHasAccess(AccessToken token, AccessCheck check, AuthContext authContext = null);
3233
#endregion System
3334

3435
#region Server

src/Certify.Core/Management/Access/AccessControl.cs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ public async Task<SecurityPrinciple> GetSecurityPrincipleByUsername(string conte
209209
/// <param name="identifier">optional resource identifier, if access is limited by specific resource</param>
210210
/// <param name="scopedAssignedRoles">optional scoped assigned roles to limit access to (for scoped access token checks etc)</param>
211211
/// <returns></returns>
212-
public async Task<bool> IsAuthorised(string contextUserId, string principleId, string resourceType, string actionId, string identifier = null, List<string> scopedAssignedRoles = null)
212+
public async Task<bool> IsSecurityPrincipleAuthorised(string contextUserId, AccessCheck check)
213213
{
214214
// to determine is a principle has access to perform a particular action
215215
// for each group the principle is part of
@@ -226,12 +226,12 @@ public async Task<bool> IsAuthorised(string contextUserId, string principleId, s
226226
var allPolicies = await _store.GetItems<ResourcePolicy>(nameof(ResourcePolicy));
227227

228228
// get the assigned roles for this specific security principle
229-
var spAssignedRoles = allAssignedRoles.Where(a => a.SecurityPrincipleId == principleId);
229+
var spAssignedRoles = allAssignedRoles.Where(a => a.SecurityPrincipleId == check.SecurityPrincipleId);
230230

231231
// if scoped assigned role ID specified (access token check etc), reduce scope of assigned roles to check
232-
if (scopedAssignedRoles?.Any() == true)
232+
if (check.ScopedAssignedRoles?.Any() == true)
233233
{
234-
spAssignedRoles = spAssignedRoles.Where(a => scopedAssignedRoles.Contains(a.Id));
234+
spAssignedRoles = spAssignedRoles.Where(a => check.ScopedAssignedRoles.Contains(a.Id));
235235
}
236236

237237
// get all role definitions included in the principles assigned roles
@@ -243,32 +243,32 @@ public async Task<bool> IsAuthorised(string contextUserId, string principleId, s
243243
var spAssignedPolicies = allPolicies.Where(r => spAssignedRoleDefinitions.Any(p => p.Policies.Contains(r.Id)));
244244

245245
// check an assigned policy allows the required resource action
246-
if (spAssignedPolicies.Any(a => a.ResourceActions.Contains(actionId)))
246+
if (spAssignedPolicies.Any(a => a.ResourceActions.Contains(check.ResourceActionId)))
247247
{
248248

249249
// if any of the service principles assigned roles are restricted by resource type,
250250
// check for identifier matches (e.g. role assignment restricted on domains )
251251

252-
if (spSpecificAssignedRoles.Any(a => a.IncludedResources?.Any(r => r.ResourceType == resourceType) == true))
252+
if (spSpecificAssignedRoles.Any(a => a.IncludedResources?.Any(r => r.ResourceType == check.ResourceType) == true))
253253
{
254254
var allIncludedResources = spSpecificAssignedRoles.SelectMany(a => a.IncludedResources).Distinct();
255255

256-
if (resourceType == ResourceTypes.Domain && !identifier.Trim().StartsWith("*") && identifier.Contains("."))
256+
if (check.ResourceType == ResourceTypes.Domain && !check.Identifier.Trim().StartsWith("*") && check.Identifier.Contains("."))
257257
{
258258
// get wildcard for respective domain identifier
259-
var identifierComponents = identifier.Split('.');
259+
var identifierComponents = check.Identifier.Split('.');
260260

261261
var wildcard = "*." + string.Join(".", identifierComponents.Skip(1));
262262

263263
// search for matching identifier
264264

265265
foreach (var includedResource in allIncludedResources)
266266
{
267-
if (includedResource.ResourceType == resourceType && includedResource.Identifier == wildcard)
267+
if (includedResource.ResourceType == check.ResourceType && includedResource.Identifier == wildcard)
268268
{
269269
return true;
270270
}
271-
else if (includedResource.ResourceType == resourceType && includedResource.Identifier == identifier)
271+
else if (includedResource.ResourceType == check.ResourceType && includedResource.Identifier == check.Identifier)
272272
{
273273
return true;
274274
}
@@ -289,7 +289,7 @@ public async Task<bool> IsAuthorised(string contextUserId, string principleId, s
289289
}
290290
}
291291

292-
public async Task<ActionResult> IsAccessTokenAuthorised(string contextUserId, AccessToken accessToken, string resourceType, string actionId, string identifier)
292+
public async Task<ActionResult> IsAccessTokenAuthorised(string contextUserId, AccessToken accessToken, AccessCheck check)
293293
{
294294
// resolve security principle from access token
295295

@@ -305,7 +305,16 @@ public async Task<ActionResult> IsAccessTokenAuthorised(string contextUserId, Ac
305305

306306
// check related principle has access
307307

308-
var isAuthorised = await IsAuthorised(contextUserId, knownAssignedToken.SecurityPrincipleId, resourceType, actionId, identifier, knownAssignedToken.ScopedAssignedRoles);
308+
var scopedCheck = new AccessCheck
309+
{
310+
SecurityPrincipleId = knownAssignedToken.SecurityPrincipleId,
311+
ResourceActionId = check.ResourceActionId,
312+
Identifier = check.Identifier,
313+
ResourceType = check.ResourceType,
314+
ScopedAssignedRoles = knownAssignedToken.ScopedAssignedRoles
315+
};
316+
317+
var isAuthorised = await IsSecurityPrincipleAuthorised(contextUserId, scopedCheck);
309318

310319
if (isAuthorised)
311320
{

src/Certify.Core/Management/Access/IAccessControl.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ public interface IAccessControl
1717
/// </summary>
1818
/// <returns></returns>
1919
Task<List<Role>> GetRoles();
20-
Task<bool> IsAuthorised(string contextUserId, string principleId, string resourceType, string actionId, string identifier = null, List<string> scopedAssignedRoles = null);
20+
Task<bool> IsSecurityPrincipleAuthorised(string contextUserId, AccessCheck check);
21+
Task<Models.Config.ActionResult> IsAccessTokenAuthorised(string contextUserId, AccessToken accessToken, AccessCheck check);
2122
Task<bool> IsPrincipleInRole(string contextUserId, string id, string roleId);
2223
Task<List<AssignedRole>> GetAssignedRoles(string contextUserId, string id);
2324
Task<RoleStatus> GetSecurityPrincipleRoleStatus(string contextUserId, string id);

src/Certify.Models/Hub/AccessControl.cs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,35 @@ public class AssignedRole : ConfigurationStoreItem
8080
public List<Resource>? IncludedResources { get; set; } = [];
8181
}
8282

83+
public class AccessCheck
84+
{
85+
public string? SecurityPrincipleId { get; set; } = default!;
86+
public string ResourceType { get; set; } = default!;
87+
public string ResourceActionId { get; set; } = default!;
88+
public string? Identifier { get; set; } = default!;
89+
90+
public List<string> ScopedAssignedRoles { get; set; } = [];
91+
92+
public AccessCheck() { }
93+
public AccessCheck(string? securityPrincipleId, string resourceType, string resourceActionId, string? identifier = null)
94+
{
95+
SecurityPrincipleId = securityPrincipleId;
96+
ResourceType = resourceType;
97+
ResourceActionId = resourceActionId;
98+
Identifier = identifier;
99+
}
100+
}
101+
102+
public class AccessTokenCheck
103+
{
104+
public AccessToken Token { get; set; }
105+
public AccessCheck Check { get; set; }
106+
}
83107
public class AccessToken : ConfigurationStoreItem
84108
{
85-
public string TokenType { get; set; }
86-
public string Secret { get; set; }
87-
public string ClientId { get; set; }
109+
public string TokenType { get; set; } = default!;
110+
public string Secret { get; set; } = default!;
111+
public string ClientId { get; set; } = default!;
88112
public DateTimeOffset? DateCreated { get; set; }
89113
public DateTimeOffset? DateExpiry { get; set; }
90114
public DateTimeOffset? DateRevoked { get; set; }

0 commit comments

Comments
 (0)