diff --git a/src/Api/SecretsManager/Models/Request/GetSecretsRequestModel.cs b/src/Api/SecretsManager/Models/Request/GetSecretsRequestModel.cs index 42dbce5232..5eec3a7a6c 100644 --- a/src/Api/SecretsManager/Models/Request/GetSecretsRequestModel.cs +++ b/src/Api/SecretsManager/Models/Request/GetSecretsRequestModel.cs @@ -1,9 +1,22 @@ using System.ComponentModel.DataAnnotations; - namespace Bit.Api.SecretsManager.Models.Request; -public class GetSecretsRequestModel +public class GetSecretsRequestModel : IValidatableObject { [Required] public IEnumerable Ids { get; set; } + public IEnumerable Validate(ValidationContext validationContext) + { + var isDistinct = Ids.Distinct().Count() == Ids.Count(); + if (!isDistinct) + { + var duplicateGuids = Ids.GroupBy(x => x) + .Where(g => g.Count() > 1) + .Select(g => g.Key); + + yield return new ValidationResult( + $"The following GUIDs were duplicated {string.Join(", ", duplicateGuids)} ", + new[] { nameof(GetSecretsRequestModel) }); + } + } } diff --git a/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsControllerTests.cs b/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsControllerTests.cs index d6cbfe9dee..23adbff4ee 100644 --- a/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsControllerTests.cs +++ b/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsControllerTests.cs @@ -781,6 +781,47 @@ public class SecretsControllerTests : IClassFixture, IAsy Assert.Equal(secretIds.Count, result.Data.Count()); } + + [Theory] + [InlineData(PermissionType.RunAsAdmin)] + [InlineData(PermissionType.RunAsUserWithPermission)] + public async Task GetSecretsByIds_DuplicateIds_BadRequest(PermissionType permissionType) + { + var (org, _) = await _organizationHelper.Initialize(true, true, true); + await _loginHelper.LoginAsync(_email); + + var (project, secretIds) = await CreateSecretsAsync(org.Id); + + secretIds.Add(secretIds[0]); + + if (permissionType == PermissionType.RunAsUserWithPermission) + { + var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); + await _loginHelper.LoginAsync(email); + + var accessPolicies = new List + { + new UserProjectAccessPolicy + { + GrantedProjectId = project.Id, OrganizationUserId = orgUser.Id, Read = true, Write = true, + }, + }; + await _accessPolicyRepository.CreateManyAsync(accessPolicies); + } + else + { + var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.Admin, true); + await _loginHelper.LoginAsync(email); + } + + var request = new GetSecretsRequestModel { Ids = secretIds }; + var response = await _client.PostAsJsonAsync("/secrets/get-by-ids", request); + var content = await response.Content.ReadAsStringAsync(); + + Assert.True(response.StatusCode == HttpStatusCode.BadRequest); + Assert.Contains("The following GUIDs were duplicated", content); + } + [Theory] [InlineData(false, false, false)] [InlineData(false, false, true)]