1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-05 05:00:19 -05:00

Use closures

This commit is contained in:
Thomas Rittson 2025-04-01 15:21:39 +10:00
parent 72ce1f147d
commit faa2ff8b1d
No known key found for this signature in database
GPG Key ID: CDDDA03861C35E27
3 changed files with 98 additions and 65 deletions

View File

@ -10,25 +10,46 @@ namespace Bit.Api.AdminConsole.Authorization;
public static class ClaimsExtensions public static class ClaimsExtensions
{ {
// Relevant claim types for organization roles, SM access, and custom permissions /// <summary>
private static readonly IEnumerable<string> _relevantClaimTypes = new List<string>{ /// A delegate that returns true if the user has the specified claim type for an organization, false otherwise.
/// </summary>
private delegate bool HasClaim(string claimType);
// Relevant claim types required to build a CurrentContextOrganization object.
private static readonly IEnumerable<string> _relevantClaimTypes = new HashSet<string>{
Claims.OrganizationOwner, Claims.OrganizationOwner,
Claims.OrganizationAdmin, Claims.OrganizationAdmin,
Claims.OrganizationCustom, Claims.OrganizationCustom,
Claims.OrganizationUser, Claims.OrganizationUser,
Claims.SecretsManagerAccess, Claims.SecretsManagerAccess,
}.Concat(new Permissions().ClaimsMap.Select(c => c.ClaimName)); Claims.CustomPermissions.AccessEventLogs,
Claims.CustomPermissions.AccessImportExport,
Claims.CustomPermissions.AccessReports,
Claims.CustomPermissions.CreateNewCollections,
Claims.CustomPermissions.EditAnyCollection,
Claims.CustomPermissions.DeleteAnyCollection,
Claims.CustomPermissions.ManageGroups,
Claims.CustomPermissions.ManagePolicies,
Claims.CustomPermissions.ManageSso,
Claims.CustomPermissions.ManageUsers,
Claims.CustomPermissions.ManageResetPassword,
Claims.CustomPermissions.ManageScim,
};
/// <summary>
/// Parses a user's claims and returns an object representing their claims for the specified organization.
/// </summary>
/// <param name="user">The user who has the claims.</param>
/// <param name="organizationId">The organizationId to look for in the claims.</param>
/// <returns>
/// A <see cref="CurrentContextOrganization"/> representing the user's claims for that organization, or null
/// if the user does not have any claims for that organization.
/// </returns>
public static CurrentContextOrganization? GetCurrentContextOrganization(this ClaimsPrincipal user, Guid organizationId) public static CurrentContextOrganization? GetCurrentContextOrganization(this ClaimsPrincipal user, Guid organizationId)
{ {
var claimsDict = user.Claims var hasClaim = GetClaimsParser(user, organizationId);
.Where(c => _relevantClaimTypes.Contains(c.Type) && Guid.TryParse(c.Value, out _))
.GroupBy(c => c.Type)
.ToDictionary(
c => c.Key,
c => c.Select(v => new Guid(v.Value)));
var role = claimsDict.GetRoleForOrganizationId(organizationId); var role = GetRoleFromClaims(hasClaim);
if (!role.HasValue) if (!role.HasValue)
{ {
// Not an organization member // Not an organization member
@ -39,37 +60,48 @@ public static class ClaimsExtensions
{ {
Id = organizationId, Id = organizationId,
Type = role.Value, Type = role.Value,
AccessSecretsManager = claimsDict.ContainsOrganizationId(Claims.SecretsManagerAccess, organizationId), AccessSecretsManager = hasClaim(Claims.SecretsManagerAccess),
Permissions = role == OrganizationUserType.Custom Permissions = role == OrganizationUserType.Custom
? claimsDict.GetPermissionsFromClaims(organizationId) ? GetPermissionsFromClaims(hasClaim)
: null : null
}; };
} }
private static bool ContainsOrganizationId(this Dictionary<string, IEnumerable<Guid>> claimsDict, string claimType, /// <summary>
Guid organizationId) /// Creates a <see cref="HasClaim"/> delegate specific to the user and organization.
=> claimsDict.TryGetValue(claimType, out var claimValue) && /// </summary>
claimValue.Any(guid => guid == organizationId); private static HasClaim GetClaimsParser(ClaimsPrincipal user, Guid organizationId)
private static OrganizationUserType? GetRoleForOrganizationId(this Dictionary<string, IEnumerable<Guid>> claimsDict,
Guid organizationId)
{ {
if (claimsDict.ContainsOrganizationId(Claims.OrganizationOwner, organizationId)) var claimsDict = user.Claims
.Where(c => _relevantClaimTypes.Contains(c.Type) && Guid.TryParse(c.Value, out _))
.GroupBy(c => c.Type)
.ToDictionary(
c => c.Key,
c => c.Select(v => new Guid(v.Value)));
return claimType
=> claimsDict.TryGetValue(claimType, out var claimValue) &&
claimValue.Any(v => v == organizationId);
}
private static OrganizationUserType? GetRoleFromClaims(HasClaim hasClaim)
{
if (hasClaim(Claims.OrganizationOwner))
{ {
return OrganizationUserType.Owner; return OrganizationUserType.Owner;
} }
if (claimsDict.ContainsOrganizationId(Claims.OrganizationAdmin, organizationId)) if (hasClaim(Claims.OrganizationAdmin))
{ {
return OrganizationUserType.Admin; return OrganizationUserType.Admin;
} }
if (claimsDict.ContainsOrganizationId(Claims.OrganizationCustom, organizationId)) if (hasClaim(Claims.OrganizationCustom))
{ {
return OrganizationUserType.Custom; return OrganizationUserType.Custom;
} }
if (claimsDict.ContainsOrganizationId(Claims.OrganizationUser, organizationId)) if (hasClaim(Claims.OrganizationUser))
{ {
return OrganizationUserType.User; return OrganizationUserType.User;
} }
@ -77,22 +109,20 @@ public static class ClaimsExtensions
return null; return null;
} }
private static Permissions GetPermissionsFromClaims(this Dictionary<string, IEnumerable<Guid>> claimsDict, Guid organizationId) private static Permissions GetPermissionsFromClaims(HasClaim hasClaim)
=> new()
{ {
return new Permissions AccessEventLogs = hasClaim(Claims.CustomPermissions.AccessEventLogs),
{ AccessImportExport = hasClaim(Claims.CustomPermissions.AccessImportExport),
AccessEventLogs = claimsDict.ContainsOrganizationId(Claims.AccessEventLogs, organizationId), AccessReports = hasClaim(Claims.CustomPermissions.AccessReports),
AccessImportExport = claimsDict.ContainsOrganizationId(Claims.AccessImportExport, organizationId), CreateNewCollections = hasClaim(Claims.CustomPermissions.CreateNewCollections),
AccessReports = claimsDict.ContainsOrganizationId(Claims.AccessReports, organizationId), EditAnyCollection = hasClaim(Claims.CustomPermissions.EditAnyCollection),
CreateNewCollections = claimsDict.ContainsOrganizationId(Claims.CreateNewCollections, organizationId), DeleteAnyCollection = hasClaim(Claims.CustomPermissions.DeleteAnyCollection),
EditAnyCollection = claimsDict.ContainsOrganizationId(Claims.EditAnyCollection, organizationId), ManageGroups = hasClaim(Claims.CustomPermissions.ManageGroups),
DeleteAnyCollection = claimsDict.ContainsOrganizationId(Claims.DeleteAnyCollection, organizationId), ManagePolicies = hasClaim(Claims.CustomPermissions.ManagePolicies),
ManageGroups = claimsDict.ContainsOrganizationId(Claims.ManageGroups, organizationId), ManageSso = hasClaim(Claims.CustomPermissions.ManageSso),
ManagePolicies = claimsDict.ContainsOrganizationId(Claims.ManagePolicies, organizationId), ManageUsers = hasClaim(Claims.CustomPermissions.ManageUsers),
ManageSso = claimsDict.ContainsOrganizationId(Claims.ManageSso, organizationId), ManageResetPassword = hasClaim(Claims.CustomPermissions.ManageResetPassword),
ManageUsers = claimsDict.ContainsOrganizationId(Claims.ManageUsers, organizationId), ManageScim = hasClaim(Claims.CustomPermissions.ManageScim),
ManageResetPassword = claimsDict.ContainsOrganizationId(Claims.ManageResetPassword, organizationId), };
ManageScim = claimsDict.ContainsOrganizationId(Claims.ManageScim, organizationId),
};
}
} }

View File

@ -21,17 +21,17 @@ public class Permissions
[JsonIgnore] [JsonIgnore]
public List<(bool Permission, string ClaimName)> ClaimsMap => new() public List<(bool Permission, string ClaimName)> ClaimsMap => new()
{ {
(AccessEventLogs, Claims.AccessEventLogs), (AccessEventLogs, Claims.CustomPermissions.AccessEventLogs),
(AccessImportExport, Claims.AccessImportExport), (AccessImportExport, Claims.CustomPermissions.AccessImportExport),
(AccessReports, Claims.AccessReports), (AccessReports, Claims.CustomPermissions.AccessReports),
(CreateNewCollections, Claims.CreateNewCollections), (CreateNewCollections, Claims.CustomPermissions.CreateNewCollections),
(EditAnyCollection, Claims.EditAnyCollection), (EditAnyCollection, Claims.CustomPermissions.EditAnyCollection),
(DeleteAnyCollection, Claims.DeleteAnyCollection), (DeleteAnyCollection, Claims.CustomPermissions.DeleteAnyCollection),
(ManageGroups, Claims.ManageGroups), (ManageGroups, Claims.CustomPermissions.ManageGroups),
(ManagePolicies, Claims.ManagePolicies), (ManagePolicies, Claims.CustomPermissions.ManagePolicies),
(ManageSso, Claims.ManageSso), (ManageSso, Claims.CustomPermissions.ManageSso),
(ManageUsers, Claims.ManageUsers), (ManageUsers, Claims.CustomPermissions.ManageUsers),
(ManageResetPassword, Claims.ManageResetPassword), (ManageResetPassword, Claims.CustomPermissions.ManageResetPassword),
(ManageScim, Claims.ManageScim), (ManageScim, Claims.CustomPermissions.ManageScim),
}; };
} }

View File

@ -23,17 +23,20 @@ public static class Claims
// General // General
public const string Type = "type"; public const string Type = "type";
// Organization permissions // Organization custom permissions
public const string AccessEventLogs = "accesseventlogs"; public static class CustomPermissions
public const string AccessImportExport = "accessimportexport"; {
public const string AccessReports = "accessreports"; public const string AccessEventLogs = "accesseventlogs";
public const string CreateNewCollections = "createnewcollections"; public const string AccessImportExport = "accessimportexport";
public const string EditAnyCollection = "editanycollection"; public const string AccessReports = "accessreports";
public const string DeleteAnyCollection = "deleteanycollection"; public const string CreateNewCollections = "createnewcollections";
public const string ManageGroups = "managegroups"; public const string EditAnyCollection = "editanycollection";
public const string ManagePolicies = "managepolicies"; public const string DeleteAnyCollection = "deleteanycollection";
public const string ManageSso = "managesso"; public const string ManageGroups = "managegroups";
public const string ManageUsers = "manageusers"; public const string ManagePolicies = "managepolicies";
public const string ManageResetPassword = "manageresetpassword"; public const string ManageSso = "managesso";
public const string ManageScim = "managescim"; public const string ManageUsers = "manageusers";
public const string ManageResetPassword = "manageresetpassword";
public const string ManageScim = "managescim";
}
} }