1
0
mirror of https://github.com/bitwarden/server.git synced 2025-05-29 23:34:53 -05:00

Parse claims upfront, define custom permissions

This commit is contained in:
Thomas Rittson 2025-04-01 12:11:23 +10:00
parent ef30805d0e
commit 72ce1f147d
No known key found for this signature in database
GPG Key ID: CDDDA03861C35E27
3 changed files with 64 additions and 27 deletions

View File

@ -4,23 +4,29 @@ using System.Security.Claims;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Identity; using Bit.Core.Identity;
using Bit.Core.Models.Data;
namespace Bit.Api.AdminConsole.Authorization; namespace Bit.Api.AdminConsole.Authorization;
public static class ClaimsExtensions public static class ClaimsExtensions
{ {
// Relevant claim types for organization roles, SM access, and custom permissions
private static readonly IEnumerable<string> _relevantClaimTypes = new List<string>{
Claims.OrganizationOwner,
Claims.OrganizationAdmin,
Claims.OrganizationCustom,
Claims.OrganizationUser,
Claims.SecretsManagerAccess,
}.Concat(new Permissions().ClaimsMap.Select(c => c.ClaimName));
public static CurrentContextOrganization? GetCurrentContextOrganization(this ClaimsPrincipal user, Guid organizationId) public static CurrentContextOrganization? GetCurrentContextOrganization(this ClaimsPrincipal user, Guid organizationId)
{ {
var claimsDict = user.Claims var claimsDict = user.Claims
.Where(c => _relevantClaimTypes.Contains(c.Type) && Guid.TryParse(c.Value, out _))
.GroupBy(c => c.Type) .GroupBy(c => c.Type)
.ToDictionary(c => c.Key, c => c.Select(v => v)); .ToDictionary(
c => c.Key,
var accessSecretsManager = claimsDict.TryGetValue(Claims.SecretsManagerAccess, out var value) c => c.Select(v => new Guid(v.Value)));
? value
.Where(s => Guid.TryParse(s.Value, out _))
.Select(s => new Guid(s.Value))
.ToHashSet()
: [];
var role = claimsDict.GetRoleForOrganizationId(organizationId); var role = claimsDict.GetRoleForOrganizationId(organizationId);
if (!role.HasValue) if (!role.HasValue)
@ -33,19 +39,19 @@ public static class ClaimsExtensions
{ {
Id = organizationId, Id = organizationId,
Type = role.Value, Type = role.Value,
AccessSecretsManager = accessSecretsManager.Contains(organizationId), AccessSecretsManager = claimsDict.ContainsOrganizationId(Claims.SecretsManagerAccess, organizationId),
Permissions = role == OrganizationUserType.Custom Permissions = role == OrganizationUserType.Custom
? CurrentContext.SetOrganizationPermissionsFromClaims(organizationId.ToString(), claimsDict) ? claimsDict.GetPermissionsFromClaims(organizationId)
: null : null
}; };
} }
private static bool ContainsOrganizationId(this Dictionary<string, IEnumerable<Claim>> claimsDict, string claimType, private static bool ContainsOrganizationId(this Dictionary<string, IEnumerable<Guid>> claimsDict, string claimType,
Guid organizationId) Guid organizationId)
=> claimsDict.TryGetValue(claimType, out var claimValue) && => claimsDict.TryGetValue(claimType, out var claimValue) &&
claimValue.Any(c => c.Value.EqualsGuid(organizationId)); claimValue.Any(guid => guid == organizationId);
private static OrganizationUserType? GetRoleForOrganizationId(this Dictionary<string, IEnumerable<Claim>> claimsDict, private static OrganizationUserType? GetRoleForOrganizationId(this Dictionary<string, IEnumerable<Guid>> claimsDict,
Guid organizationId) Guid organizationId)
{ {
if (claimsDict.ContainsOrganizationId(Claims.OrganizationOwner, organizationId)) if (claimsDict.ContainsOrganizationId(Claims.OrganizationOwner, organizationId))
@ -71,6 +77,22 @@ public static class ClaimsExtensions
return null; return null;
} }
private static bool EqualsGuid(this string value, Guid guid) private static Permissions GetPermissionsFromClaims(this Dictionary<string, IEnumerable<Guid>> claimsDict, Guid organizationId)
=> Guid.TryParse(value, out var parsedValue) && parsedValue == guid; {
return new Permissions
{
AccessEventLogs = claimsDict.ContainsOrganizationId(Claims.AccessEventLogs, organizationId),
AccessImportExport = claimsDict.ContainsOrganizationId(Claims.AccessImportExport, organizationId),
AccessReports = claimsDict.ContainsOrganizationId(Claims.AccessReports, organizationId),
CreateNewCollections = claimsDict.ContainsOrganizationId(Claims.CreateNewCollections, organizationId),
EditAnyCollection = claimsDict.ContainsOrganizationId(Claims.EditAnyCollection, organizationId),
DeleteAnyCollection = claimsDict.ContainsOrganizationId(Claims.DeleteAnyCollection, organizationId),
ManageGroups = claimsDict.ContainsOrganizationId(Claims.ManageGroups, organizationId),
ManagePolicies = claimsDict.ContainsOrganizationId(Claims.ManagePolicies, organizationId),
ManageSso = claimsDict.ContainsOrganizationId(Claims.ManageSso, organizationId),
ManageUsers = claimsDict.ContainsOrganizationId(Claims.ManageUsers, organizationId),
ManageResetPassword = claimsDict.ContainsOrganizationId(Claims.ManageResetPassword, organizationId),
ManageScim = claimsDict.ContainsOrganizationId(Claims.ManageScim, organizationId),
};
}
} }

View File

@ -1,4 +1,5 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Bit.Core.Identity;
namespace Bit.Core.Models.Data; namespace Bit.Core.Models.Data;
@ -20,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, "accesseventlogs"), (AccessEventLogs, Claims.AccessEventLogs),
(AccessImportExport, "accessimportexport"), (AccessImportExport, Claims.AccessImportExport),
(AccessReports, "accessreports"), (AccessReports, Claims.AccessReports),
(CreateNewCollections, "createnewcollections"), (CreateNewCollections, Claims.CreateNewCollections),
(EditAnyCollection, "editanycollection"), (EditAnyCollection, Claims.EditAnyCollection),
(DeleteAnyCollection, "deleteanycollection"), (DeleteAnyCollection, Claims.DeleteAnyCollection),
(ManageGroups, "managegroups"), (ManageGroups, Claims.ManageGroups),
(ManagePolicies, "managepolicies"), (ManagePolicies, Claims.ManagePolicies),
(ManageSso, "managesso"), (ManageSso, Claims.ManageSso),
(ManageUsers, "manageusers"), (ManageUsers, Claims.ManageUsers),
(ManageResetPassword, "manageresetpassword"), (ManageResetPassword, Claims.ManageResetPassword),
(ManageScim, "managescim"), (ManageScim, Claims.ManageScim),
}; };
} }

View File

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