Skip to content
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

[AC-1387] Resource-based authentication for Groups #2845

Closed
wants to merge 53 commits into from
Closed
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
73219ea
Initial proof of concept for resource based auth
eliykat Apr 10, 2023
09815d6
Use generic CRUD operations, remove unnecessary policies
eliykat Apr 10, 2023
313a5ae
Remove unused deps
eliykat Apr 10, 2023
22db106
Fill out the rest of GroupsController
eliykat Apr 11, 2023
ad3edfb
Move CreateGroup to Groups auth handler
eliykat Apr 11, 2023
1ce0c89
Add proper 403 exception and status code
eliykat Apr 11, 2023
4897086
Enable nullable where required
eliykat Apr 12, 2023
1da60bb
Use Guids instead of strings
eliykat Apr 12, 2023
c6c5a31
Unabbreviate Authorization
eliykat Apr 12, 2023
68ed862
Use nameof instead of string literals
eliykat Apr 12, 2023
7ae2162
Use named tuples
eliykat Apr 12, 2023
7fa4d15
Fix EF return tuple
eliykat Apr 12, 2023
22fa5b8
Assign default Permissions object, remove null checks
eliykat Apr 12, 2023
33a7a6d
Fix structure and async stuff in handlers
eliykat Apr 12, 2023
bde9519
Merge branch 'master' into experiment/resource-based-auth
eliykat Apr 24, 2023
1936bbe
Use switch statements
eliykat Apr 25, 2023
a2ed58b
Revert "Add proper 403 exception and status code"
eliykat Apr 25, 2023
9fba463
Check for null within auth handler
eliykat Apr 25, 2023
639b07b
Create extension methods for AuthService
eliykat Apr 25, 2023
933af46
Treat GroupUsers as their own resource
eliykat Apr 25, 2023
fee6057
Create shim methods for individual requirements
eliykat Apr 25, 2023
f4c7324
Fix line breaks
eliykat Apr 25, 2023
e1468c2
Remove unnecessary comment
eliykat Apr 25, 2023
50399e8
Add extra null checks
eliykat Apr 25, 2023
af23ee5
Remove GroupService
eliykat Apr 26, 2023
29a6d23
Add bitAuthorizationService and update tests
eliykat Apr 26, 2023
bf77e5a
Suggestion: move ReadAllGroups into group authorizationHandler
eliykat Apr 27, 2023
e577c24
Revert "Suggestion: move ReadAllGroups into group authorizationHandler"
eliykat Apr 27, 2023
2c06a80
Merge branch 'master' into experiment/resource-based-auth
eliykat May 9, 2023
f3681e2
Add provider checks using CurrentContext
eliykat May 9, 2023
e5f971e
Move ReadAllGroups requirement into group handler
eliykat May 10, 2023
3c52440
Fix sql syntax and update date
eliykat May 10, 2023
d73c81e
Fix comments and spacing
eliykat May 10, 2023
1cc18d4
Fix sql syntax
eliykat May 10, 2023
7f70cd3
Run dotnet format
eliykat May 10, 2023
27ff510
Delete unused code
eliykat May 10, 2023
4855003
Fix tests
eliykat May 10, 2023
6024216
Minor tweaks
eliykat May 10, 2023
2599427
Remove DeleteGroupCommand
eliykat May 11, 2023
89c65b9
Fix error message
eliykat May 11, 2023
3464d57
Remove #nullable from CurrentContext
eliykat May 11, 2023
719fcba
Merge branch 'master' into experiment/resource-based-auth
eliykat May 23, 2023
4465e1a
Fix order of using/namespace
eliykat May 23, 2023
f62a4f5
Make ReadAllForOrganization a static method
eliykat May 23, 2023
8413679
Merge remote-tracking branch 'origin/master' into experiment/resource…
eliykat May 29, 2023
6bba9f3
Remove access modifier in interface
eliykat May 29, 2023
c5583ab
Update date on sproc
eliykat May 29, 2023
eeb78a9
Use PascalCase for named tuple
eliykat May 29, 2023
5753267
Add comment
eliykat May 29, 2023
0ee583d
Finish updating named tuple case
eliykat May 30, 2023
b7b8053
Finish updating named tuple case
eliykat May 30, 2023
d5b68d8
Merge remote-tracking branch 'origin/master' into experiment/resource…
eliykat Aug 27, 2023
23622ee
dotnet format
eliykat Aug 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions bitwarden_license/src/Scim/Controllers/v2/GroupsController.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.OrganizationFeatures.Groups.Interfaces;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Scim.Groups.Interfaces;
using Bit.Scim.Models;
using Bit.Scim.Utilities;
Expand All @@ -18,7 +18,7 @@ public class GroupsController : Controller
private readonly IGroupRepository _groupRepository;
private readonly IOrganizationRepository _organizationRepository;
private readonly IGetGroupsListQuery _getGroupsListQuery;
private readonly IDeleteGroupCommand _deleteGroupCommand;
private readonly IEventService _eventService;
private readonly IPatchGroupCommand _patchGroupCommand;
private readonly IPostGroupCommand _postGroupCommand;
private readonly IPutGroupCommand _putGroupCommand;
Expand All @@ -28,7 +28,7 @@ public GroupsController(
IGroupRepository groupRepository,
IOrganizationRepository organizationRepository,
IGetGroupsListQuery getGroupsListQuery,
IDeleteGroupCommand deleteGroupCommand,
IEventService eventService,
IPatchGroupCommand patchGroupCommand,
IPostGroupCommand postGroupCommand,
IPutGroupCommand putGroupCommand,
Expand All @@ -37,7 +37,7 @@ public GroupsController(
_groupRepository = groupRepository;
_organizationRepository = organizationRepository;
_getGroupsListQuery = getGroupsListQuery;
_deleteGroupCommand = deleteGroupCommand;
_eventService = eventService;
_patchGroupCommand = patchGroupCommand;
_postGroupCommand = postGroupCommand;
_putGroupCommand = putGroupCommand;
Expand Down Expand Up @@ -103,7 +103,14 @@ public async Task<IActionResult> Patch(Guid organizationId, Guid id, [FromBody]
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(Guid organizationId, Guid id)
{
await _deleteGroupCommand.DeleteGroupAsync(organizationId, id, EventSystemUser.SCIM);
var group = await _groupRepository.GetByIdAsync(id);
if (group == null || group.OrganizationId != organizationId)
{
throw new NotFoundException("Group not found.");
}

await _groupRepository.DeleteAsync(group);
await _eventService.LogGroupEventAsync(group, EventType.Group_Deleted, EventSystemUser.SCIM);
return new NoContentResult();
}
}
27 changes: 20 additions & 7 deletions bitwarden_license/src/Scim/Groups/PatchGroupCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,23 @@ namespace Bit.Scim.Groups;
public class PatchGroupCommand : IPatchGroupCommand
{
private readonly IGroupRepository _groupRepository;
private readonly IGroupService _groupService;
private readonly IUpdateGroupCommand _updateGroupCommand;
private readonly ILogger<PatchGroupCommand> _logger;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IEventService _eventService;

public PatchGroupCommand(
IGroupRepository groupRepository,
IGroupService groupService,
IUpdateGroupCommand updateGroupCommand,
ILogger<PatchGroupCommand> logger)
ILogger<PatchGroupCommand> logger,
IOrganizationUserRepository organizationUserRepository,
IEventService eventService)
{
_groupRepository = groupRepository;
_groupService = groupService;
_updateGroupCommand = updateGroupCommand;
_logger = logger;
_organizationUserRepository = organizationUserRepository;
_eventService = eventService;
}

public async Task PatchGroupAsync(Organization organization, Guid id, ScimPatchModel model)
Expand Down Expand Up @@ -97,10 +100,20 @@ public async Task PatchGroupAsync(Organization organization, Guid id, ScimPatchM
!string.IsNullOrWhiteSpace(operation.Path) &&
operation.Path.ToLowerInvariant().StartsWith("members[value eq "))
{
var removeId = GetOperationPathId(operation.Path);
if (removeId.HasValue)
var organizationUserId = GetOperationPathId(operation.Path);
if (organizationUserId.HasValue)
{
await _groupService.DeleteUserAsync(group, removeId.Value, EventSystemUser.SCIM);
var groupUser = await _groupRepository.GetGroupUserByGroupIdOrganizationUserId(group.Id, organizationUserId.Value);
if (groupUser == null)
{
throw new NotFoundException();
}

var orgUser = await _organizationUserRepository.GetByIdAsync(groupUser.OrganizationUserId);
await _groupRepository.DeleteUserAsync(groupUser.GroupId, groupUser.OrganizationUserId);
await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_UpdatedGroups,
EventSystemUser.SCIM);

operationHandled = true;
}
}
Expand Down
23 changes: 19 additions & 4 deletions bitwarden_license/test/Scim.Test/Groups/PatchGroupCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,30 +173,45 @@ public async Task PatchGroup_AddListMembers_Success(SutProvider<PatchGroupComman

[Theory]
[BitAutoData]
public async Task PatchGroup_RemoveSingleMember_Success(SutProvider<PatchGroupCommand> sutProvider, Organization organization, Group group, Guid userId)
public async Task PatchGroup_RemoveSingleMember_Success(SutProvider<PatchGroupCommand> sutProvider,
Organization organization, Group group, OrganizationUser organizationUser)
{
group.OrganizationId = organization.Id;

sutProvider.GetDependency<IGroupRepository>()
.GetByIdAsync(group.Id)
.Returns(group);

sutProvider.GetDependency<IGroupRepository>()
.GetGroupUserByGroupIdOrganizationUserId(group.Id, organizationUser.Id)
.Returns(new GroupUser()
{
OrganizationUserId = organizationUser.Id,
GroupId = group.Id
});

sutProvider.GetDependency<IOrganizationUserRepository>()
.GetByIdAsync(organizationUser.Id)
.Returns(organizationUser);

var scimPatchModel = new Models.ScimPatchModel
{
Operations = new List<ScimPatchModel.OperationModel>
{
new ScimPatchModel.OperationModel
{
Op = "remove",
Path = $"members[value eq \"{userId}\"]",
Path = $"members[value eq \"{organizationUser.Id}\"]",
}
},
Schemas = new List<string> { ScimConstants.Scim2SchemaUser }
};

await sutProvider.Sut.PatchGroupAsync(organization, group.Id, scimPatchModel);

await sutProvider.GetDependency<IGroupService>().Received(1).DeleteUserAsync(group, userId, EventSystemUser.SCIM);
await sutProvider.GetDependency<IGroupRepository>().Received(1).DeleteUserAsync(group.Id, organizationUser.Id);
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventAsync(organizationUser,
EventType.OrganizationUser_UpdatedGroups, EventSystemUser.SCIM);
}

[Theory]
Expand Down Expand Up @@ -253,7 +268,7 @@ public async Task PatchGroup_NoAction_Success(SutProvider<PatchGroupCommand> sut
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs().UpdateUsersAsync(default, default);
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs().GetManyUserIdsByIdAsync(default);
await sutProvider.GetDependency<IUpdateGroupCommand>().DidNotReceiveWithAnyArgs().UpdateGroupAsync(default, default);
await sutProvider.GetDependency<IGroupService>().DidNotReceiveWithAnyArgs().DeleteUserAsync(default, default);
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs().DeleteUserAsync(default, default);
}

[Theory]
Expand Down
Loading