diff --git a/src/Api/AdminConsole/Controllers/GroupsController.cs b/src/Api/AdminConsole/Controllers/GroupsController.cs index f3f1d343c..0e4665699 100644 --- a/src/Api/AdminConsole/Controllers/GroupsController.cs +++ b/src/Api/AdminConsole/Controllers/GroupsController.cs @@ -2,15 +2,16 @@ using Bit.Api.AdminConsole.Models.Response; using Bit.Api.Models.Response; using Bit.Api.Vault.AuthorizationHandlers.Collections; -using Bit.Api.Vault.AuthorizationHandlers.Groups; +using Bit.Core; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Authorization; using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces; +using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; using Bit.Core.AdminConsole.Repositories; using Bit.Core.AdminConsole.Services; using Bit.Core.Context; using Bit.Core.Exceptions; using Bit.Core.Repositories; using Bit.Core.Services; -using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -89,11 +90,34 @@ public class GroupsController : Controller } [HttpGet("")] - public async Task> Get(Guid orgId) + public async Task> GetOrganizationGroups(Guid orgId) { - var authorized = - (await _authorizationService.AuthorizeAsync(User, GroupOperations.ReadAll(orgId))).Succeeded; - if (!authorized) + var authResult = await _authorizationService.AuthorizeAsync(User, new OrganizationScope(orgId), GroupOperations.ReadAll); + if (!authResult.Succeeded) + { + throw new NotFoundException(); + } + + if (_featureService.IsEnabled(FeatureFlagKeys.SecureOrgGroupDetails)) + { + var groups = await _groupRepository.GetManyByOrganizationIdAsync(orgId); + var responses = groups.Select(g => new GroupDetailsResponseModel(g, [])); + return new ListResponseModel(responses); + } + + var groupDetails = await _groupRepository.GetManyWithCollectionsByOrganizationIdAsync(orgId); + var detailResponses = groupDetails.Select(g => new GroupDetailsResponseModel(g.Item1, g.Item2)); + return new ListResponseModel(detailResponses); + } + + [HttpGet("details")] + public async Task> GetOrganizationGroupDetails(Guid orgId) + { + var authResult = _featureService.IsEnabled(FeatureFlagKeys.SecureOrgGroupDetails) + ? await _authorizationService.AuthorizeAsync(User, new OrganizationScope(orgId), GroupOperations.ReadAllDetails) + : await _authorizationService.AuthorizeAsync(User, new OrganizationScope(orgId), GroupOperations.ReadAll); + + if (!authResult.Succeeded) { throw new NotFoundException(); } diff --git a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs index b81cb068f..3193962fa 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs @@ -8,6 +8,7 @@ using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Models.Data.Organizations.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; +using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; using Bit.Core.AdminConsole.Repositories; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Repositories; diff --git a/src/Api/Api.csproj b/src/Api/Api.csproj index d6d055e90..4a018b219 100644 --- a/src/Api/Api.csproj +++ b/src/Api/Api.csproj @@ -1,5 +1,4 @@  - bitwarden-Api false diff --git a/src/Api/Utilities/ServiceCollectionExtensions.cs b/src/Api/Utilities/ServiceCollectionExtensions.cs index a7fbddbaa..8a58a5f23 100644 --- a/src/Api/Utilities/ServiceCollectionExtensions.cs +++ b/src/Api/Utilities/ServiceCollectionExtensions.cs @@ -1,5 +1,5 @@ using Bit.Api.Vault.AuthorizationHandlers.Collections; -using Bit.Api.Vault.AuthorizationHandlers.Groups; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Authorization; using Bit.Core.IdentityServer; using Bit.Core.Settings; using Bit.Core.Utilities; diff --git a/src/Api/Vault/AuthorizationHandlers/Groups/GroupAuthorizationHandler.cs b/src/Api/Vault/AuthorizationHandlers/Groups/GroupAuthorizationHandler.cs deleted file mode 100644 index 666cd725e..000000000 --- a/src/Api/Vault/AuthorizationHandlers/Groups/GroupAuthorizationHandler.cs +++ /dev/null @@ -1,62 +0,0 @@ -#nullable enable -using Bit.Core.Context; -using Microsoft.AspNetCore.Authorization; - -namespace Bit.Api.Vault.AuthorizationHandlers.Groups; - -/// -/// Handles authorization logic for Group operations. -/// This uses new logic implemented in the Flexible Collections initiative. -/// -public class GroupAuthorizationHandler : AuthorizationHandler -{ - private readonly ICurrentContext _currentContext; - - public GroupAuthorizationHandler(ICurrentContext currentContext) - { - _currentContext = currentContext; - } - - protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, - GroupOperationRequirement requirement) - { - // Acting user is not authenticated, fail - if (!_currentContext.UserId.HasValue) - { - context.Fail(); - return; - } - - if (requirement.OrganizationId == default) - { - context.Fail(); - return; - } - - var org = _currentContext.GetOrganization(requirement.OrganizationId); - - switch (requirement) - { - case not null when requirement.Name == nameof(GroupOperations.ReadAll): - await CanReadAllAsync(context, requirement, org); - break; - } - } - - private async Task CanReadAllAsync(AuthorizationHandlerContext context, GroupOperationRequirement requirement, - CurrentContextOrganization? org) - { - // All users of an organization can read all groups belonging to the organization for collection access management - if (org is not null) - { - context.Succeed(requirement); - return; - } - - // Allow provider users to read all groups if they are a provider for the target organization - if (await _currentContext.ProviderUserForOrgAsync(requirement.OrganizationId)) - { - context.Succeed(requirement); - } - } -} diff --git a/src/Api/Vault/AuthorizationHandlers/Groups/GroupOperations.cs b/src/Api/Vault/AuthorizationHandlers/Groups/GroupOperations.cs deleted file mode 100644 index 7735bd52a..000000000 --- a/src/Api/Vault/AuthorizationHandlers/Groups/GroupOperations.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.AspNetCore.Authorization.Infrastructure; - -namespace Bit.Api.Vault.AuthorizationHandlers.Groups; - -public class GroupOperationRequirement : OperationAuthorizationRequirement -{ - public Guid OrganizationId { get; init; } - - public GroupOperationRequirement(string name, Guid organizationId) - { - Name = name; - OrganizationId = organizationId; - } -} - -public static class GroupOperations -{ - public static GroupOperationRequirement ReadAll(Guid organizationId) - { - return new GroupOperationRequirement(nameof(ReadAll), organizationId); - } -} diff --git a/src/Core/AdminConsole/OrganizationFeatures/Groups/Authorization/GroupAuthorizationHandler.cs b/src/Core/AdminConsole/OrganizationFeatures/Groups/Authorization/GroupAuthorizationHandler.cs new file mode 100644 index 000000000..d531c1175 --- /dev/null +++ b/src/Core/AdminConsole/OrganizationFeatures/Groups/Authorization/GroupAuthorizationHandler.cs @@ -0,0 +1,43 @@ +#nullable enable +using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; +using Bit.Core.Context; +using Bit.Core.Enums; +using Microsoft.AspNetCore.Authorization; + +namespace Bit.Core.AdminConsole.OrganizationFeatures.Groups.Authorization; + +public class GroupAuthorizationHandler(ICurrentContext currentContext) + : AuthorizationHandler +{ + protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, + GroupOperationRequirement requirement, OrganizationScope organizationScope) + { + var authorized = requirement switch + { + not null when requirement.Name == nameof(GroupOperations.ReadAll) => + await CanReadAllAsync(organizationScope), + not null when requirement.Name == nameof(GroupOperations.ReadAllDetails) => + await CanViewGroupDetailsAsync(organizationScope), + _ => false + }; + + if (requirement is not null && authorized) + { + context.Succeed(requirement); + } + } + + private async Task CanReadAllAsync(OrganizationScope organizationScope) => + currentContext.GetOrganization(organizationScope) is not null + || await currentContext.ProviderUserForOrgAsync(organizationScope); + + private async Task CanViewGroupDetailsAsync(OrganizationScope organizationScope) => + currentContext.GetOrganization(organizationScope) is + { Type: OrganizationUserType.Owner } or + { Type: OrganizationUserType.Admin } or + { + Permissions: { ManageGroups: true } or + { ManageUsers: true } + } || + await currentContext.ProviderUserForOrgAsync(organizationScope); +} diff --git a/src/Core/AdminConsole/OrganizationFeatures/Groups/Authorization/GroupOperations.cs b/src/Core/AdminConsole/OrganizationFeatures/Groups/Authorization/GroupOperations.cs new file mode 100644 index 000000000..f5b3b975f --- /dev/null +++ b/src/Core/AdminConsole/OrganizationFeatures/Groups/Authorization/GroupOperations.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Authorization.Infrastructure; + +namespace Bit.Core.AdminConsole.OrganizationFeatures.Groups.Authorization; + +public class GroupOperationRequirement : OperationAuthorizationRequirement +{ + public GroupOperationRequirement(string name) + { + Name = name; + } +} + +public static class GroupOperations +{ + public static readonly GroupOperationRequirement ReadAll = new(nameof(ReadAll)); + public static readonly GroupOperationRequirement ReadAllDetails = new(nameof(ReadAllDetails)); +} diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Authorization/OrganizationUserUserDetailsAuthorizationHandler.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Authorization/OrganizationUserUserDetailsAuthorizationHandler.cs index e890e4d9f..f57850e64 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Authorization/OrganizationUserUserDetailsAuthorizationHandler.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Authorization/OrganizationUserUserDetailsAuthorizationHandler.cs @@ -1,4 +1,5 @@ #nullable enable +using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; using Bit.Core.Context; using Bit.Core.Enums; using Microsoft.AspNetCore.Authorization; diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Authorization/OrganizationUserUserMiniDetailsAuthorizationHandler.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Authorization/OrganizationUserUserMiniDetailsAuthorizationHandler.cs index 24d9d0c26..01b77a05b 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Authorization/OrganizationUserUserMiniDetailsAuthorizationHandler.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Authorization/OrganizationUserUserMiniDetailsAuthorizationHandler.cs @@ -1,5 +1,5 @@ -using Bit.Core.Context; -using Bit.Core.Services; +using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; +using Bit.Core.Context; using Microsoft.AspNetCore.Authorization; namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization; @@ -7,14 +7,10 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authoriza public class OrganizationUserUserMiniDetailsAuthorizationHandler : AuthorizationHandler { - private readonly IApplicationCacheService _applicationCacheService; private readonly ICurrentContext _currentContext; - public OrganizationUserUserMiniDetailsAuthorizationHandler( - IApplicationCacheService applicationCacheService, - ICurrentContext currentContext) + public OrganizationUserUserMiniDetailsAuthorizationHandler(ICurrentContext currentContext) { - _applicationCacheService = applicationCacheService; _currentContext = currentContext; } diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Authorization/OrganizationScope.cs b/src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/OrganizationScope.cs similarity index 90% rename from src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Authorization/OrganizationScope.cs rename to src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/OrganizationScope.cs index 57856e702..689cff24e 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Authorization/OrganizationScope.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/Shared/Authorization/OrganizationScope.cs @@ -1,6 +1,6 @@ #nullable enable -namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization; +namespace Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; /// /// A typed wrapper for an organization Guid. This is used for authorization checks diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index f75920749..67d463bac 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -143,6 +143,7 @@ public static class FeatureFlagKeys public const string StorageReseedRefactor = "storage-reseed-refactor"; public const string TrialPayment = "PM-8163-trial-payment"; public const string RemoveServerVersionHeader = "remove-server-version-header"; + public const string SecureOrgGroupDetails = "pm-3479-secure-org-group-details"; public const string AccessIntelligence = "pm-13227-access-intelligence"; public const string VerifiedSsoDomainEndpoint = "pm-12337-refactor-sso-details-endpoint"; public const string PM12275_MultiOrganizationEnterprises = "pm-12275-multi-organization-enterprises"; diff --git a/test/Api.Test/AdminConsole/AuthorizationHandlers/GroupAuthorizationHandlerTests.cs b/test/Api.Test/AdminConsole/AuthorizationHandlers/GroupAuthorizationHandlerTests.cs new file mode 100644 index 000000000..a1b63c4fd --- /dev/null +++ b/test/Api.Test/AdminConsole/AuthorizationHandlers/GroupAuthorizationHandlerTests.cs @@ -0,0 +1,223 @@ +using System.Security.Claims; +using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Authorization; +using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; +using Bit.Core.Context; +using Bit.Core.Enums; +using Bit.Core.Models.Data; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using Microsoft.AspNetCore.Authorization; +using NSubstitute; +using Xunit; + +namespace Bit.Api.Test.AdminConsole.AuthorizationHandlers; + +[SutProviderCustomize] +public class GroupAuthorizationHandlerTests +{ + [Theory] + [BitAutoData(OrganizationUserType.Admin)] + [BitAutoData(OrganizationUserType.Owner)] + [BitAutoData(OrganizationUserType.User)] + [BitAutoData(OrganizationUserType.Custom)] + public async Task CanReadAllAsync_WhenMemberOfOrg_Success( + OrganizationUserType userType, + OrganizationScope scope, + Guid userId, SutProvider sutProvider, + CurrentContextOrganization organization) + { + organization.Type = userType; + organization.Permissions = new Permissions(); + + var context = new AuthorizationHandlerContext( + new[] { GroupOperations.ReadAll }, + new ClaimsPrincipal(), + scope); + + sutProvider.GetDependency().UserId.Returns(userId); + sutProvider.GetDependency().GetOrganization(scope).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory, BitAutoData] + public async Task CanReadAllAsync_WithProviderUser_Success( + Guid userId, + OrganizationScope scope, + SutProvider sutProvider, CurrentContextOrganization organization) + { + organization.Type = OrganizationUserType.User; + organization.Permissions = new Permissions(); + + var context = new AuthorizationHandlerContext( + new[] { GroupOperations.ReadAll }, + new ClaimsPrincipal(), + scope); + + sutProvider.GetDependency() + .UserId + .Returns(userId); + sutProvider.GetDependency() + .ProviderUserForOrgAsync(scope) + .Returns(true); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } + + [Theory, BitAutoData] + public async Task CanReadAllAsync_WhenMissingOrgAccess_NoSuccess( + Guid userId, + OrganizationScope scope, + SutProvider sutProvider) + { + + var context = new AuthorizationHandlerContext( + new[] { GroupOperations.ReadAll }, + new ClaimsPrincipal(), + scope + ); + + sutProvider.GetDependency().UserId.Returns(userId); + sutProvider.GetDependency().GetOrganization(Arg.Any()).Returns((CurrentContextOrganization)null); + sutProvider.GetDependency().ProviderUserForOrgAsync(Arg.Any()).Returns(false); + + await sutProvider.Sut.HandleAsync(context); + Assert.False(context.HasSucceeded); + } + + [Theory] + [BitAutoData(OrganizationUserType.Admin)] + [BitAutoData(OrganizationUserType.Owner)] + public async Task HandleRequirementsAsync_GivenViewDetailsOperation_WhenUserIsAdminOwner_ThenShouldSucceed(OrganizationUserType userType, + OrganizationScope scope, + CurrentContextOrganization organization, SutProvider sutProvider) + { + organization.Type = userType; + organization.Permissions = new Permissions(); + + var context = new AuthorizationHandlerContext( + [GroupOperations.ReadAllDetails], + new ClaimsPrincipal(), + scope + ); + + sutProvider.GetDependency().GetOrganization(scope).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + Assert.True(context.HasSucceeded); + } + + [Theory] + [BitAutoData(OrganizationUserType.User)] + [BitAutoData(OrganizationUserType.Custom)] + public async Task HandleRequirementsAsync_GivenViewDetailsOperation_WhenUserIsNotOwnerOrAdmin_ThenShouldFail(OrganizationUserType userType, + OrganizationScope scope, + CurrentContextOrganization organization, SutProvider sutProvider) + { + organization.Type = userType; + organization.Permissions = new Permissions(); + + var context = new AuthorizationHandlerContext( + [GroupOperations.ReadAllDetails], + new ClaimsPrincipal(), + scope + ); + + sutProvider.GetDependency().GetOrganization(scope).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + Assert.False(context.HasSucceeded); + } + + [Theory, BitAutoData] + public async Task HandleRequirementsAsync_GivenViewDetailsOperation_WhenUserHasManageGroupPermission_ThenShouldSucceed( + OrganizationScope scope, + CurrentContextOrganization organization, SutProvider sutProvider) + { + organization.Type = OrganizationUserType.Custom; + organization.Permissions = new Permissions + { + ManageGroups = true + }; + + var context = new AuthorizationHandlerContext( + [GroupOperations.ReadAllDetails], + new ClaimsPrincipal(), + scope + ); + + sutProvider.GetDependency().GetOrganization(scope).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + Assert.True(context.HasSucceeded); + } + + [Theory, BitAutoData] + public async Task HandleRequirementsAsync_GivenViewDetailsOperation_WhenUserHasManageUserPermission_ThenShouldSucceed( + OrganizationScope scope, + CurrentContextOrganization organization, SutProvider sutProvider) + { + organization.Type = OrganizationUserType.Custom; + organization.Permissions = new Permissions + { + ManageUsers = true + }; + + var context = new AuthorizationHandlerContext( + [GroupOperations.ReadAllDetails], + new ClaimsPrincipal(), + scope + ); + + sutProvider.GetDependency().GetOrganization(scope).Returns(organization); + + await sutProvider.Sut.HandleAsync(context); + Assert.True(context.HasSucceeded); + } + + [Theory, BitAutoData] + public async Task HandleRequirementsAsync_GivenViewDetailsOperation_WhenUserIsStandardUserTypeWithoutElevatedPermissions_ThenShouldFail( + OrganizationScope scope, + CurrentContextOrganization organization, SutProvider sutProvider) + { + organization.Type = OrganizationUserType.User; + organization.Permissions = new Permissions(); + + var context = new AuthorizationHandlerContext( + [GroupOperations.ReadAllDetails], + new ClaimsPrincipal(), + scope + ); + + sutProvider.GetDependency().GetOrganization(scope).Returns(organization); + sutProvider.GetDependency().ProviderUserForOrgAsync(scope).Returns(false); + + await sutProvider.Sut.HandleAsync(context); + Assert.False(context.HasSucceeded); + } + + [Theory, BitAutoData] + public async Task HandleRequirementsAsync_GivenViewDetailsOperation_WhenIsProviderUser_ThenShouldSucceed( + OrganizationScope scope, + SutProvider sutProvider, CurrentContextOrganization organization) + { + organization.Type = OrganizationUserType.User; + organization.Permissions = new Permissions(); + + var context = new AuthorizationHandlerContext( + new[] { GroupOperations.ReadAll }, + new ClaimsPrincipal(), + scope); + + sutProvider.GetDependency().GetOrganization(scope).Returns(organization); + sutProvider.GetDependency().ProviderUserForOrgAsync(scope).Returns(true); + + await sutProvider.Sut.HandleAsync(context); + + Assert.True(context.HasSucceeded); + } +} diff --git a/test/Api.Test/Vault/AuthorizationHandlers/GroupAuthorizationHandlerTests.cs b/test/Api.Test/Vault/AuthorizationHandlers/GroupAuthorizationHandlerTests.cs deleted file mode 100644 index 608e201c5..000000000 --- a/test/Api.Test/Vault/AuthorizationHandlers/GroupAuthorizationHandlerTests.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System.Security.Claims; -using Bit.Api.Vault.AuthorizationHandlers.Groups; -using Bit.Core.Context; -using Bit.Core.Enums; -using Bit.Core.Models.Data; -using Bit.Test.Common.AutoFixture; -using Bit.Test.Common.AutoFixture.Attributes; -using Microsoft.AspNetCore.Authorization; -using NSubstitute; -using Xunit; - -namespace Bit.Api.Test.Vault.AuthorizationHandlers; - -[SutProviderCustomize] -public class GroupAuthorizationHandlerTests -{ - [Theory] - [BitAutoData(OrganizationUserType.Admin)] - [BitAutoData(OrganizationUserType.Owner)] - [BitAutoData(OrganizationUserType.User)] - [BitAutoData(OrganizationUserType.Custom)] - public async Task CanReadAllAsync_WhenMemberOfOrg_Success( - OrganizationUserType userType, - Guid userId, SutProvider sutProvider, - CurrentContextOrganization organization) - { - organization.Type = userType; - organization.Permissions = new Permissions(); - - var context = new AuthorizationHandlerContext( - new[] { GroupOperations.ReadAll(organization.Id) }, - new ClaimsPrincipal(), - null); - - sutProvider.GetDependency().UserId.Returns(userId); - sutProvider.GetDependency().GetOrganization(organization.Id).Returns(organization); - - await sutProvider.Sut.HandleAsync(context); - - Assert.True(context.HasSucceeded); - } - - [Theory, BitAutoData] - public async Task CanReadAllAsync_WithProviderUser_Success( - Guid userId, - SutProvider sutProvider, CurrentContextOrganization organization) - { - organization.Type = OrganizationUserType.User; - organization.Permissions = new Permissions(); - - var context = new AuthorizationHandlerContext( - new[] { GroupOperations.ReadAll(organization.Id) }, - new ClaimsPrincipal(), - null); - - sutProvider.GetDependency() - .UserId - .Returns(userId); - sutProvider.GetDependency() - .ProviderUserForOrgAsync(organization.Id) - .Returns(true); - - await sutProvider.Sut.HandleAsync(context); - - Assert.True(context.HasSucceeded); - } - - [Theory, BitAutoData] - public async Task CanReadAllAsync_WhenMissingOrgAccess_NoSuccess( - Guid userId, - CurrentContextOrganization organization, - SutProvider sutProvider) - { - - var context = new AuthorizationHandlerContext( - new[] { GroupOperations.ReadAll(organization.Id) }, - new ClaimsPrincipal(), - null - ); - - sutProvider.GetDependency().UserId.Returns(userId); - sutProvider.GetDependency().GetOrganization(Arg.Any()).Returns((CurrentContextOrganization)null); - sutProvider.GetDependency().ProviderUserForOrgAsync(Arg.Any()).Returns(false); - - await sutProvider.Sut.HandleAsync(context); - Assert.False(context.HasSucceeded); - } - - [Theory, BitAutoData] - public async Task HandleRequirementAsync_MissingUserId_Failure( - Guid organizationId, - SutProvider sutProvider) - { - var context = new AuthorizationHandlerContext( - new[] { GroupOperations.ReadAll(organizationId) }, - new ClaimsPrincipal(), - null - ); - - // Simulate missing user id - sutProvider.GetDependency().UserId.Returns((Guid?)null); - - await sutProvider.Sut.HandleAsync(context); - Assert.False(context.HasSucceeded); - Assert.True(context.HasFailed); - } - - [Theory, BitAutoData] - public async Task HandleRequirementAsync_NoSpecifiedOrgId_Failure( - SutProvider sutProvider) - { - var context = new AuthorizationHandlerContext( - new[] { GroupOperations.ReadAll(default) }, - new ClaimsPrincipal(), - null - ); - - sutProvider.GetDependency().UserId.Returns(new Guid()); - - await sutProvider.Sut.HandleAsync(context); - - Assert.False(context.HasSucceeded); - Assert.True(context.HasFailed); - } -} diff --git a/test/Core.Test/AdminConsole/Authorization/OrganizationUserUserDetailsAuthorizationHandlerTests.cs b/test/Core.Test/AdminConsole/Authorization/OrganizationUserUserDetailsAuthorizationHandlerTests.cs index 4a3a7f647..0814754f8 100644 --- a/test/Core.Test/AdminConsole/Authorization/OrganizationUserUserDetailsAuthorizationHandlerTests.cs +++ b/test/Core.Test/AdminConsole/Authorization/OrganizationUserUserDetailsAuthorizationHandlerTests.cs @@ -1,5 +1,6 @@ using System.Security.Claims; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization; +using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Test.AdminConsole.AutoFixture; diff --git a/test/Core.Test/AdminConsole/Authorization/OrganizationUserUserMiniDetailsAuthorizationHandlerTests.cs b/test/Core.Test/AdminConsole/Authorization/OrganizationUserUserMiniDetailsAuthorizationHandlerTests.cs index c74cb2d5c..6732486a5 100644 --- a/test/Core.Test/AdminConsole/Authorization/OrganizationUserUserMiniDetailsAuthorizationHandlerTests.cs +++ b/test/Core.Test/AdminConsole/Authorization/OrganizationUserUserMiniDetailsAuthorizationHandlerTests.cs @@ -1,5 +1,6 @@ using System.Security.Claims; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization; +using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Test.AdminConsole.AutoFixture;