2022-10-31 10:58:21 +01:00
|
|
|
|
using System.Text.Json;
|
2023-10-19 22:37:46 +02:00
|
|
|
|
using Bit.Core.AdminConsole.Entities;
|
|
|
|
|
using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces;
|
|
|
|
|
using Bit.Core.AdminConsole.Repositories;
|
|
|
|
|
using Bit.Core.AdminConsole.Services;
|
2022-11-09 13:13:29 +01:00
|
|
|
|
using Bit.Core.Enums;
|
2022-10-31 10:58:21 +01:00
|
|
|
|
using Bit.Core.Exceptions;
|
|
|
|
|
using Bit.Scim.Groups;
|
|
|
|
|
using Bit.Scim.Models;
|
|
|
|
|
using Bit.Scim.Utilities;
|
|
|
|
|
using Bit.Test.Common.AutoFixture;
|
|
|
|
|
using Bit.Test.Common.AutoFixture.Attributes;
|
|
|
|
|
using NSubstitute;
|
|
|
|
|
using Xunit;
|
|
|
|
|
|
|
|
|
|
namespace Bit.Scim.Test.Groups;
|
|
|
|
|
|
|
|
|
|
[SutProviderCustomize]
|
|
|
|
|
public class PatchGroupCommandTests
|
|
|
|
|
{
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData]
|
2022-12-12 10:59:48 +01:00
|
|
|
|
public async Task PatchGroup_ReplaceListMembers_Success(SutProvider<PatchGroupCommand> sutProvider, Organization organization, Group group, IEnumerable<Guid> userIds)
|
2022-10-31 10:58:21 +01:00
|
|
|
|
{
|
2022-12-12 10:59:48 +01:00
|
|
|
|
group.OrganizationId = organization.Id;
|
|
|
|
|
|
2022-10-31 10:58:21 +01:00
|
|
|
|
sutProvider.GetDependency<IGroupRepository>()
|
|
|
|
|
.GetByIdAsync(group.Id)
|
|
|
|
|
.Returns(group);
|
|
|
|
|
|
|
|
|
|
var scimPatchModel = new Models.ScimPatchModel
|
|
|
|
|
{
|
|
|
|
|
Operations = new List<ScimPatchModel.OperationModel>
|
|
|
|
|
{
|
|
|
|
|
new ScimPatchModel.OperationModel
|
|
|
|
|
{
|
|
|
|
|
Op = "replace",
|
|
|
|
|
Path = "members",
|
|
|
|
|
Value = JsonDocument.Parse(JsonSerializer.Serialize(userIds.Select(uid => new { value = uid }).ToArray())).RootElement
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Schemas = new List<string> { ScimConstants.Scim2SchemaUser }
|
|
|
|
|
};
|
|
|
|
|
|
2022-12-12 10:59:48 +01:00
|
|
|
|
await sutProvider.Sut.PatchGroupAsync(organization, group.Id, scimPatchModel);
|
2022-10-31 10:58:21 +01:00
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IGroupRepository>().Received(1).UpdateUsersAsync(group.Id, Arg.Is<IEnumerable<Guid>>(arg => arg.All(id => userIds.Contains(id))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData]
|
2022-12-12 10:59:48 +01:00
|
|
|
|
public async Task PatchGroup_ReplaceDisplayNameFromPath_Success(SutProvider<PatchGroupCommand> sutProvider, Organization organization, Group group, string displayName)
|
2022-10-31 10:58:21 +01:00
|
|
|
|
{
|
2022-12-12 10:59:48 +01:00
|
|
|
|
group.OrganizationId = organization.Id;
|
|
|
|
|
|
2022-10-31 10:58:21 +01:00
|
|
|
|
sutProvider.GetDependency<IGroupRepository>()
|
|
|
|
|
.GetByIdAsync(group.Id)
|
|
|
|
|
.Returns(group);
|
|
|
|
|
|
|
|
|
|
var scimPatchModel = new Models.ScimPatchModel
|
|
|
|
|
{
|
|
|
|
|
Operations = new List<ScimPatchModel.OperationModel>
|
|
|
|
|
{
|
|
|
|
|
new ScimPatchModel.OperationModel
|
|
|
|
|
{
|
|
|
|
|
Op = "replace",
|
|
|
|
|
Path = "displayname",
|
|
|
|
|
Value = JsonDocument.Parse($"\"{displayName}\"").RootElement
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Schemas = new List<string> { ScimConstants.Scim2SchemaUser }
|
|
|
|
|
};
|
|
|
|
|
|
2022-12-12 10:59:48 +01:00
|
|
|
|
await sutProvider.Sut.PatchGroupAsync(organization, group.Id, scimPatchModel);
|
2022-10-31 10:58:21 +01:00
|
|
|
|
|
2022-12-12 10:59:48 +01:00
|
|
|
|
await sutProvider.GetDependency<IUpdateGroupCommand>().Received(1).UpdateGroupAsync(group, organization, EventSystemUser.SCIM);
|
2022-10-31 10:58:21 +01:00
|
|
|
|
Assert.Equal(displayName, group.Name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData]
|
2022-12-12 10:59:48 +01:00
|
|
|
|
public async Task PatchGroup_ReplaceDisplayNameFromValueObject_Success(SutProvider<PatchGroupCommand> sutProvider, Organization organization, Group group, string displayName)
|
2022-10-31 10:58:21 +01:00
|
|
|
|
{
|
2022-12-12 10:59:48 +01:00
|
|
|
|
group.OrganizationId = organization.Id;
|
|
|
|
|
|
2022-10-31 10:58:21 +01:00
|
|
|
|
sutProvider.GetDependency<IGroupRepository>()
|
|
|
|
|
.GetByIdAsync(group.Id)
|
|
|
|
|
.Returns(group);
|
|
|
|
|
|
|
|
|
|
var scimPatchModel = new Models.ScimPatchModel
|
|
|
|
|
{
|
|
|
|
|
Operations = new List<ScimPatchModel.OperationModel>
|
|
|
|
|
{
|
|
|
|
|
new ScimPatchModel.OperationModel
|
|
|
|
|
{
|
|
|
|
|
Op = "replace",
|
|
|
|
|
Value = JsonDocument.Parse($"{{\"displayName\":\"{displayName}\"}}").RootElement
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Schemas = new List<string> { ScimConstants.Scim2SchemaUser }
|
|
|
|
|
};
|
|
|
|
|
|
2022-12-12 10:59:48 +01:00
|
|
|
|
await sutProvider.Sut.PatchGroupAsync(organization, group.Id, scimPatchModel);
|
2022-10-31 10:58:21 +01:00
|
|
|
|
|
2022-12-12 10:59:48 +01:00
|
|
|
|
await sutProvider.GetDependency<IUpdateGroupCommand>().Received(1).UpdateGroupAsync(group, organization, EventSystemUser.SCIM);
|
2022-10-31 10:58:21 +01:00
|
|
|
|
Assert.Equal(displayName, group.Name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData]
|
2022-12-12 10:59:48 +01:00
|
|
|
|
public async Task PatchGroup_AddSingleMember_Success(SutProvider<PatchGroupCommand> sutProvider, Organization organization, Group group, ICollection<Guid> existingMembers, Guid userId)
|
2022-10-31 10:58:21 +01:00
|
|
|
|
{
|
2022-12-12 10:59:48 +01:00
|
|
|
|
group.OrganizationId = organization.Id;
|
|
|
|
|
|
2022-10-31 10:58:21 +01:00
|
|
|
|
sutProvider.GetDependency<IGroupRepository>()
|
|
|
|
|
.GetByIdAsync(group.Id)
|
|
|
|
|
.Returns(group);
|
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IGroupRepository>()
|
|
|
|
|
.GetManyUserIdsByIdAsync(group.Id)
|
|
|
|
|
.Returns(existingMembers);
|
|
|
|
|
|
|
|
|
|
var scimPatchModel = new Models.ScimPatchModel
|
|
|
|
|
{
|
|
|
|
|
Operations = new List<ScimPatchModel.OperationModel>
|
|
|
|
|
{
|
|
|
|
|
new ScimPatchModel.OperationModel
|
|
|
|
|
{
|
|
|
|
|
Op = "add",
|
|
|
|
|
Path = $"members[value eq \"{userId}\"]",
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Schemas = new List<string> { ScimConstants.Scim2SchemaUser }
|
|
|
|
|
};
|
|
|
|
|
|
2022-12-12 10:59:48 +01:00
|
|
|
|
await sutProvider.Sut.PatchGroupAsync(organization, group.Id, scimPatchModel);
|
2022-10-31 10:58:21 +01:00
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IGroupRepository>().Received(1).UpdateUsersAsync(group.Id, Arg.Is<IEnumerable<Guid>>(arg => arg.All(id => existingMembers.Append(userId).Contains(id))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData]
|
2022-12-12 10:59:48 +01:00
|
|
|
|
public async Task PatchGroup_AddListMembers_Success(SutProvider<PatchGroupCommand> sutProvider, Organization organization, Group group, ICollection<Guid> existingMembers, ICollection<Guid> userIds)
|
2022-10-31 10:58:21 +01:00
|
|
|
|
{
|
2022-12-12 10:59:48 +01:00
|
|
|
|
group.OrganizationId = organization.Id;
|
|
|
|
|
|
2022-10-31 10:58:21 +01:00
|
|
|
|
sutProvider.GetDependency<IGroupRepository>()
|
|
|
|
|
.GetByIdAsync(group.Id)
|
|
|
|
|
.Returns(group);
|
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IGroupRepository>()
|
|
|
|
|
.GetManyUserIdsByIdAsync(group.Id)
|
|
|
|
|
.Returns(existingMembers);
|
|
|
|
|
|
|
|
|
|
var scimPatchModel = new Models.ScimPatchModel
|
|
|
|
|
{
|
|
|
|
|
Operations = new List<ScimPatchModel.OperationModel>
|
|
|
|
|
{
|
|
|
|
|
new ScimPatchModel.OperationModel
|
|
|
|
|
{
|
|
|
|
|
Op = "add",
|
|
|
|
|
Path = $"members",
|
|
|
|
|
Value = JsonDocument.Parse(JsonSerializer.Serialize(userIds.Select(uid => new { value = uid }).ToArray())).RootElement
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Schemas = new List<string> { ScimConstants.Scim2SchemaUser }
|
|
|
|
|
};
|
|
|
|
|
|
2022-12-12 10:59:48 +01:00
|
|
|
|
await sutProvider.Sut.PatchGroupAsync(organization, group.Id, scimPatchModel);
|
2022-10-31 10:58:21 +01:00
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IGroupRepository>().Received(1).UpdateUsersAsync(group.Id, Arg.Is<IEnumerable<Guid>>(arg => arg.All(id => existingMembers.Concat(userIds).Contains(id))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData]
|
2022-12-12 10:59:48 +01:00
|
|
|
|
public async Task PatchGroup_RemoveSingleMember_Success(SutProvider<PatchGroupCommand> sutProvider, Organization organization, Group group, Guid userId)
|
2022-10-31 10:58:21 +01:00
|
|
|
|
{
|
2022-12-12 10:59:48 +01:00
|
|
|
|
group.OrganizationId = organization.Id;
|
|
|
|
|
|
2022-10-31 10:58:21 +01:00
|
|
|
|
sutProvider.GetDependency<IGroupRepository>()
|
|
|
|
|
.GetByIdAsync(group.Id)
|
|
|
|
|
.Returns(group);
|
|
|
|
|
|
|
|
|
|
var scimPatchModel = new Models.ScimPatchModel
|
|
|
|
|
{
|
|
|
|
|
Operations = new List<ScimPatchModel.OperationModel>
|
|
|
|
|
{
|
|
|
|
|
new ScimPatchModel.OperationModel
|
|
|
|
|
{
|
|
|
|
|
Op = "remove",
|
|
|
|
|
Path = $"members[value eq \"{userId}\"]",
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Schemas = new List<string> { ScimConstants.Scim2SchemaUser }
|
|
|
|
|
};
|
|
|
|
|
|
2022-12-12 10:59:48 +01:00
|
|
|
|
await sutProvider.Sut.PatchGroupAsync(organization, group.Id, scimPatchModel);
|
2022-10-31 10:58:21 +01:00
|
|
|
|
|
2022-11-09 13:13:29 +01:00
|
|
|
|
await sutProvider.GetDependency<IGroupService>().Received(1).DeleteUserAsync(group, userId, EventSystemUser.SCIM);
|
2022-10-31 10:58:21 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData]
|
2022-12-12 10:59:48 +01:00
|
|
|
|
public async Task PatchGroup_RemoveListMembers_Success(SutProvider<PatchGroupCommand> sutProvider, Organization organization, Group group, ICollection<Guid> existingMembers)
|
2022-10-31 10:58:21 +01:00
|
|
|
|
{
|
2022-12-12 10:59:48 +01:00
|
|
|
|
group.OrganizationId = organization.Id;
|
|
|
|
|
|
2022-10-31 10:58:21 +01:00
|
|
|
|
sutProvider.GetDependency<IGroupRepository>()
|
|
|
|
|
.GetByIdAsync(group.Id)
|
|
|
|
|
.Returns(group);
|
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IGroupRepository>()
|
|
|
|
|
.GetManyUserIdsByIdAsync(group.Id)
|
|
|
|
|
.Returns(existingMembers);
|
|
|
|
|
|
|
|
|
|
var scimPatchModel = new Models.ScimPatchModel
|
|
|
|
|
{
|
|
|
|
|
Operations = new List<ScimPatchModel.OperationModel>
|
|
|
|
|
{
|
|
|
|
|
new ScimPatchModel.OperationModel
|
|
|
|
|
{
|
|
|
|
|
Op = "remove",
|
|
|
|
|
Path = $"members",
|
|
|
|
|
Value = JsonDocument.Parse(JsonSerializer.Serialize(existingMembers.Select(uid => new { value = uid }).ToArray())).RootElement
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Schemas = new List<string> { ScimConstants.Scim2SchemaUser }
|
|
|
|
|
};
|
|
|
|
|
|
2022-12-12 10:59:48 +01:00
|
|
|
|
await sutProvider.Sut.PatchGroupAsync(organization, group.Id, scimPatchModel);
|
2022-10-31 10:58:21 +01:00
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IGroupRepository>().Received(1).UpdateUsersAsync(group.Id, Arg.Is<IEnumerable<Guid>>(arg => arg.All(id => existingMembers.Contains(id))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData]
|
2022-12-12 10:59:48 +01:00
|
|
|
|
public async Task PatchGroup_NoAction_Success(SutProvider<PatchGroupCommand> sutProvider, Organization organization, Group group)
|
2022-10-31 10:58:21 +01:00
|
|
|
|
{
|
2022-12-12 10:59:48 +01:00
|
|
|
|
group.OrganizationId = organization.Id;
|
|
|
|
|
|
2022-10-31 10:58:21 +01:00
|
|
|
|
sutProvider.GetDependency<IGroupRepository>()
|
|
|
|
|
.GetByIdAsync(group.Id)
|
|
|
|
|
.Returns(group);
|
|
|
|
|
|
|
|
|
|
var scimPatchModel = new Models.ScimPatchModel
|
|
|
|
|
{
|
|
|
|
|
Operations = new List<ScimPatchModel.OperationModel>(),
|
|
|
|
|
Schemas = new List<string> { ScimConstants.Scim2SchemaUser }
|
|
|
|
|
};
|
|
|
|
|
|
2022-12-12 10:59:48 +01:00
|
|
|
|
await sutProvider.Sut.PatchGroupAsync(organization, group.Id, scimPatchModel);
|
2022-10-31 10:58:21 +01:00
|
|
|
|
|
2022-12-12 10:59:48 +01:00
|
|
|
|
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);
|
2022-10-31 10:58:21 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData]
|
2022-12-12 10:59:48 +01:00
|
|
|
|
public async Task PatchGroup_NotFound_Throws(SutProvider<PatchGroupCommand> sutProvider, Organization organization, Guid groupId)
|
2022-10-31 10:58:21 +01:00
|
|
|
|
{
|
|
|
|
|
var scimPatchModel = new Models.ScimPatchModel
|
|
|
|
|
{
|
|
|
|
|
Operations = new List<ScimPatchModel.OperationModel>(),
|
|
|
|
|
Schemas = new List<string> { ScimConstants.Scim2SchemaUser }
|
|
|
|
|
};
|
|
|
|
|
|
2022-12-12 10:59:48 +01:00
|
|
|
|
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.PatchGroupAsync(organization, groupId, scimPatchModel));
|
2022-10-31 10:58:21 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData]
|
2022-12-12 10:59:48 +01:00
|
|
|
|
public async Task PatchGroup_MismatchingOrganizationId_Throws(SutProvider<PatchGroupCommand> sutProvider, Organization organization, Guid groupId)
|
2022-10-31 10:58:21 +01:00
|
|
|
|
{
|
|
|
|
|
var scimPatchModel = new Models.ScimPatchModel
|
|
|
|
|
{
|
|
|
|
|
Operations = new List<ScimPatchModel.OperationModel>(),
|
|
|
|
|
Schemas = new List<string> { ScimConstants.Scim2SchemaUser }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IGroupRepository>()
|
|
|
|
|
.GetByIdAsync(groupId)
|
|
|
|
|
.Returns(new Group
|
|
|
|
|
{
|
|
|
|
|
Id = groupId,
|
|
|
|
|
OrganizationId = Guid.NewGuid()
|
|
|
|
|
});
|
|
|
|
|
|
2022-12-12 10:59:48 +01:00
|
|
|
|
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.PatchGroupAsync(organization, groupId, scimPatchModel));
|
2022-10-31 10:58:21 +01:00
|
|
|
|
}
|
|
|
|
|
}
|