mirror of
https://github.com/bitwarden/server.git
synced 2025-04-05 05:00:19 -05:00
154 lines
6.0 KiB
C#
154 lines
6.0 KiB
C#
using System.Text.Json;
|
|
using Bit.Core.AdminConsole.Entities;
|
|
using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces;
|
|
using Bit.Core.AdminConsole.Repositories;
|
|
using Bit.Core.AdminConsole.Services;
|
|
using Bit.Core.Enums;
|
|
using Bit.Core.Exceptions;
|
|
using Bit.Scim.Groups.Interfaces;
|
|
using Bit.Scim.Models;
|
|
|
|
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;
|
|
|
|
public PatchGroupCommand(
|
|
IGroupRepository groupRepository,
|
|
IGroupService groupService,
|
|
IUpdateGroupCommand updateGroupCommand,
|
|
ILogger<PatchGroupCommand> logger)
|
|
{
|
|
_groupRepository = groupRepository;
|
|
_groupService = groupService;
|
|
_updateGroupCommand = updateGroupCommand;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task PatchGroupAsync(Organization organization, Guid id, ScimPatchModel model)
|
|
{
|
|
var group = await _groupRepository.GetByIdAsync(id);
|
|
if (group == null || group.OrganizationId != organization.Id)
|
|
{
|
|
throw new NotFoundException("Group not found.");
|
|
}
|
|
|
|
var operationHandled = false;
|
|
foreach (var operation in model.Operations)
|
|
{
|
|
// Replace operations
|
|
if (operation.Op?.ToLowerInvariant() == "replace")
|
|
{
|
|
// Replace a list of members
|
|
if (operation.Path?.ToLowerInvariant() == "members")
|
|
{
|
|
var ids = GetOperationValueIds(operation.Value);
|
|
await _groupRepository.UpdateUsersAsync(group.Id, ids);
|
|
operationHandled = true;
|
|
}
|
|
// Replace group name from path
|
|
else if (operation.Path?.ToLowerInvariant() == "displayname")
|
|
{
|
|
group.Name = operation.Value.GetString();
|
|
await _updateGroupCommand.UpdateGroupAsync(group, organization, EventSystemUser.SCIM);
|
|
operationHandled = true;
|
|
}
|
|
// Replace group name from value object
|
|
else if (string.IsNullOrWhiteSpace(operation.Path) &&
|
|
operation.Value.TryGetProperty("displayName", out var displayNameProperty))
|
|
{
|
|
group.Name = displayNameProperty.GetString();
|
|
await _updateGroupCommand.UpdateGroupAsync(group, organization, EventSystemUser.SCIM);
|
|
operationHandled = true;
|
|
}
|
|
}
|
|
// Add a single member
|
|
else if (operation.Op?.ToLowerInvariant() == "add" &&
|
|
!string.IsNullOrWhiteSpace(operation.Path) &&
|
|
operation.Path.ToLowerInvariant().StartsWith("members[value eq "))
|
|
{
|
|
var addId = GetOperationPathId(operation.Path);
|
|
if (addId.HasValue)
|
|
{
|
|
var orgUserIds = (await _groupRepository.GetManyUserIdsByIdAsync(group.Id)).ToHashSet();
|
|
orgUserIds.Add(addId.Value);
|
|
await _groupRepository.UpdateUsersAsync(group.Id, orgUserIds);
|
|
operationHandled = true;
|
|
}
|
|
}
|
|
// Add a list of members
|
|
else if (operation.Op?.ToLowerInvariant() == "add" &&
|
|
operation.Path?.ToLowerInvariant() == "members")
|
|
{
|
|
var orgUserIds = (await _groupRepository.GetManyUserIdsByIdAsync(group.Id)).ToHashSet();
|
|
foreach (var v in GetOperationValueIds(operation.Value))
|
|
{
|
|
orgUserIds.Add(v);
|
|
}
|
|
await _groupRepository.UpdateUsersAsync(group.Id, orgUserIds);
|
|
operationHandled = true;
|
|
}
|
|
// Remove a single member
|
|
else if (operation.Op?.ToLowerInvariant() == "remove" &&
|
|
!string.IsNullOrWhiteSpace(operation.Path) &&
|
|
operation.Path.ToLowerInvariant().StartsWith("members[value eq "))
|
|
{
|
|
var removeId = GetOperationPathId(operation.Path);
|
|
if (removeId.HasValue)
|
|
{
|
|
await _groupService.DeleteUserAsync(group, removeId.Value, EventSystemUser.SCIM);
|
|
operationHandled = true;
|
|
}
|
|
}
|
|
// Remove a list of members
|
|
else if (operation.Op?.ToLowerInvariant() == "remove" &&
|
|
operation.Path?.ToLowerInvariant() == "members")
|
|
{
|
|
var orgUserIds = (await _groupRepository.GetManyUserIdsByIdAsync(group.Id)).ToHashSet();
|
|
foreach (var v in GetOperationValueIds(operation.Value))
|
|
{
|
|
orgUserIds.Remove(v);
|
|
}
|
|
await _groupRepository.UpdateUsersAsync(group.Id, orgUserIds);
|
|
operationHandled = true;
|
|
}
|
|
}
|
|
|
|
if (!operationHandled)
|
|
{
|
|
_logger.LogWarning("Group patch operation not handled: {0} : ",
|
|
string.Join(", ", model.Operations.Select(o => $"{o.Op}:{o.Path}")));
|
|
}
|
|
}
|
|
|
|
private List<Guid> GetOperationValueIds(JsonElement objArray)
|
|
{
|
|
var ids = new List<Guid>();
|
|
foreach (var obj in objArray.EnumerateArray())
|
|
{
|
|
if (obj.TryGetProperty("value", out var valueProperty))
|
|
{
|
|
if (valueProperty.TryGetGuid(out var guid))
|
|
{
|
|
ids.Add(guid);
|
|
}
|
|
}
|
|
}
|
|
return ids;
|
|
}
|
|
|
|
private Guid? GetOperationPathId(string path)
|
|
{
|
|
// Parse Guid from string like: members[value eq "{GUID}"}]
|
|
if (Guid.TryParse(path.Substring(18).Replace("\"]", string.Empty), out var id))
|
|
{
|
|
return id;
|
|
}
|
|
return null;
|
|
}
|
|
}
|