-
Notifications
You must be signed in to change notification settings - Fork 1.7k
[PM-18747] Add more protections to endpoints on backend so regular members can't hit endpoints and see data #7985
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a500f31
b45dc70
2082eab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| ο»Ώ#nullable enable | ||
| using Bit.Core.Context; | ||
| using Bit.Core.Enums; | ||
| using Bit.Core.Models.Data.Organizations; | ||
|
|
||
| namespace Bit.Api.AdminConsole.Authorization.Collections; | ||
|
|
||
| /// <summary> | ||
| /// Reusable, pure permission checks for Collections that other authorization requirements/handlers can depend on | ||
| /// without needing to invoke <see cref="BulkCollectionAuthorizationHandler"/> directly. | ||
| /// </summary> | ||
| public static class CollectionPermissions | ||
| { | ||
| /// <summary> | ||
| /// Returns true if the user is allowed to create a new collection in the organization. | ||
| /// This does not account for Provider users - callers must check that separately (it requires a database call). | ||
| /// </summary> | ||
| public static bool CanCreate(CurrentContextOrganization? organizationClaims, OrganizationAbility? organizationAbility) | ||
| { | ||
| // Owners, Admins, and users with CreateNewCollections permission can always create collections | ||
| if (organizationClaims is | ||
| { Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or | ||
| { Permissions.CreateNewCollections: true }) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| // If the limit collection creation setting is disabled, allow any member to create collections | ||
| return organizationClaims is not null && organizationAbility is not { LimitCollectionCreation: true }; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| ο»Ώ#nullable enable | ||
|
|
||
| using Bit.Api.AdminConsole.Authorization.Collections; | ||
| using Bit.Core.AdminConsole.AbilitiesCache; | ||
| using Bit.Core.AdminConsole.Repositories; | ||
| using Bit.Core.Enums; | ||
| using Bit.Core.Repositories; | ||
| using Bit.Core.Services; | ||
| using Microsoft.AspNetCore.Authorization; | ||
|
|
||
| namespace Bit.Api.AdminConsole.Authorization; | ||
|
|
||
| /// <summary> | ||
| /// Requires that the user is allowed to create collections, has Can Manage permissions for at least one | ||
| /// collection in the organization, or has a custom permission (ManageUsers, ManageGroups, or AccessReports) that | ||
| /// depends on this same basic organization member/group information for an unrelated reason (e.g. the Members | ||
| /// page, Groups page, and Member Access Report). This data is not privileged - it contains as little information | ||
| /// as possible and no cryptographic keys or other sensitive data - but if an organization has restricted | ||
| /// collection management to a subset of users, there's no reason to expose it more broadly than the users who | ||
| /// actually have some legitimate need for it. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// This intentionally does not implement <see cref="IOrganizationRequirement"/> because it needs more than JWT | ||
| /// claims and a provider check to answer the question - it needs the Organization's ability settings and, in the | ||
| /// less common case where collection creation is restricted, a database call to check the user's collection | ||
| /// access. Pulls the organization ID from the route itself, following the same shape as | ||
| /// <see cref="OrgUserLinkedToUserIdHandler"/>. | ||
| /// </remarks> | ||
| public class OrganizationCollectionManagementAccessRequirement : IAuthorizationRequirement; | ||
|
|
||
| public class OrganizationCollectionManagementAccessHandler( | ||
| IHttpContextAccessor httpContextAccessor, | ||
| IUserService userService, | ||
| IProviderUserRepository providerUserRepository, | ||
| IOrganizationAbilityCacheService organizationAbilityCacheService, | ||
| ICollectionRepository collectionRepository) | ||
| : AuthorizationHandler<OrganizationCollectionManagementAccessRequirement> | ||
| { | ||
| protected override async Task HandleRequirementAsync( | ||
| AuthorizationHandlerContext context, | ||
| OrganizationCollectionManagementAccessRequirement requirement) | ||
| { | ||
| var httpContext = httpContextAccessor.HttpContext | ||
| ?? throw new InvalidOperationException("This handler requires an HTTP context."); | ||
|
|
||
| var userId = userService.GetProperUserId(httpContext.User); | ||
| if (userId is null) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| var orgId = httpContext.GetOrganizationId(); | ||
| var organizationClaims = httpContext.User.GetCurrentContextOrganization(orgId); | ||
| var organizationAbility = await organizationAbilityCacheService.GetOrganizationAbilityAsync(orgId); | ||
|
|
||
| if (CollectionPermissions.CanCreate(organizationClaims, organizationAbility)) | ||
| { | ||
| context.Succeed(requirement); | ||
| return; | ||
| } | ||
|
|
||
| // Custom users who manage org members/groups or view reports have their own legitimate need for this | ||
| // basic directory data, independent of their collection permissions. | ||
| if (organizationClaims is { Type: OrganizationUserType.Custom } && | ||
| (organizationClaims.Permissions.ManageUsers || | ||
| organizationClaims.Permissions.ManageGroups || | ||
| organizationClaims.Permissions.AccessReports)) | ||
| { | ||
| context.Succeed(requirement); | ||
| return; | ||
| } | ||
|
|
||
| // The user is a confirmed member of the organization but cannot create collections - check whether they | ||
| // have Can Manage permissions on at least one collection instead. | ||
| if (organizationClaims is not null) | ||
| { | ||
| var collections = await collectionRepository.GetManySharedByOrganizationIdWithPermissionsAsync( | ||
| orgId, userId.Value, includeAccessRelationships: false); | ||
| if (collections.Any(c => c.Manage)) | ||
| { | ||
| context.Succeed(requirement); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| // Allow provider users to access this information if they are a provider for the target organization | ||
| if (await httpContext.IsProviderUserForOrgAsync(providerUserRepository, userId.Value, orgId)) | ||
| { | ||
| context.Succeed(requirement); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -190,9 +190,11 @@ public async Task<OrganizationUserDetailsResponseModel> Get(Guid orgId, Guid id, | |
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns a set of basic information about all members of the organization. This is available to all members of | ||
| /// the organization to manage collections. For this reason, it contains as little information as possible and no | ||
| /// cryptographic keys or other sensitive data. | ||
| /// Returns a set of basic information about all members of the organization. This is available to all members | ||
| /// of the organization, since a broad range of features across the app depend on basic member lookups | ||
| /// (collection management, group management, event logs, sponsorship, etc.) that are not specific to any one | ||
| /// permission. For this reason, it contains as little information as possible and no cryptographic keys or | ||
| /// other sensitive data. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just want to confirm that this is the intended behavior. Are we keeping the permission check the same and only changing the comments? |
||
| /// </summary> | ||
| /// <param name="orgId">Organization identifier</param> | ||
| /// <returns>List of users for the organization.</returns> | ||
|
|
||
This file was deleted.
This file was deleted.
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.