diff --git a/src/protagonist/API.Tests/Integration/DeliveryChannelTests.cs b/src/protagonist/API.Tests/Integration/DeliveryChannelTests.cs index 65eedd5ff..ce8f3a40f 100644 --- a/src/protagonist/API.Tests/Integration/DeliveryChannelTests.cs +++ b/src/protagonist/API.Tests/Integration/DeliveryChannelTests.cs @@ -108,6 +108,27 @@ public async Task Post_DeliveryChannelPolicy_400_IfChannelInvalid() response.StatusCode.Should().Be(HttpStatusCode.BadRequest); } + [Fact] + public async Task Post_DeliveryChannelPolicy_400_IfNameInvalid() + { + // Arrange + const int customerId = 88; + const string newDeliveryChannelPolicyJson = @"{ + ""name"": ""foo bar"", + ""displayName"": ""Invalid Policy"", + ""policyData"": ""[\""audio-mp3-128\""]"" + }"; + + var path = $"customers/{customerId}/deliveryChannelPolicies/iiif-av"; + + // Act + var content = new StringContent(newDeliveryChannelPolicyJson, Encoding.UTF8, "application/json"); + var response = await httpClient.AsCustomer(customerId).PostAsync(path, content); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.BadRequest); + } + [Theory] [InlineData("")] // No PolicyData specified [InlineData("[]")] // Empty array @@ -218,6 +239,26 @@ public async Task Put_DeliveryChannelPolicy_400_IfChannelInvalid() response.StatusCode.Should().Be(HttpStatusCode.BadRequest); } + [Fact] + public async Task Put_DeliveryChannelPolicy_400_IfNameInvalid() + { + // Arrange + const int customerId = 88; + const string newDeliveryChannelPolicyJson = @"{ + ""displayName"": ""Invalid Policy"", + ""policyData"": ""[\""audio-mp3-128\""]""r + }"; + + var path = $"customers/{customerId}/deliveryChannelPolicies/iiif-av/FooBar"; + + // Act + var content = new StringContent(newDeliveryChannelPolicyJson, Encoding.UTF8, "application/json"); + var response = await httpClient.AsCustomer(customerId).PutAsync(path, content); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.BadRequest); + } + [Theory] [InlineData("")] // No PolicyData specified [InlineData("[]")] // Empty array @@ -303,7 +344,7 @@ public async Task Patch_DeliveryChannelPolicy_201() var policy = new DLCS.Model.Policies.DeliveryChannelPolicy() { Customer = customerId, - Name = "put-av-policy-3", + Name = "put-av-policy", DisplayName = "My IIIF-AV Policy 3", Channel = "iiif-av", PolicyData = "[\"audio-mp3-128\"]" diff --git a/src/protagonist/API/Features/DeliveryChannels/DeliveryChannelPoliciesController.cs b/src/protagonist/API/Features/DeliveryChannels/DeliveryChannelPoliciesController.cs index a5567a482..2866eab09 100644 --- a/src/protagonist/API/Features/DeliveryChannels/DeliveryChannelPoliciesController.cs +++ b/src/protagonist/API/Features/DeliveryChannels/DeliveryChannelPoliciesController.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Text.RegularExpressions; using API.Features.DeliveryChannels.Converters; using API.Features.DeliveryChannels.Requests; using API.Features.DeliveryChannels.Validation; @@ -115,6 +116,12 @@ public async Task PostDeliveryChannelPolicy( 400, "Invalid delivery channel policy"); } + if (!IsValidName(hydraDeliveryChannelPolicy.Name)) + { + return this.HydraProblem($"'The name specified for this delivery channel policy is invalid", null, + 400, "Invalid delivery channel policy"); + } + var validationResult = await validator.ValidateAsync(hydraDeliveryChannelPolicy, policy => policy.IncludeRuleSets("default", "post"), cancellationToken); if (!validationResult.IsValid) @@ -170,14 +177,20 @@ public async Task PutDeliveryChannelPolicy( return this.HydraProblem($"'{deliveryChannelName}' is not a valid/permitted delivery channel", null, 400, "Invalid delivery channel policy"); } - + + if (!IsValidName(deliveryChannelPolicyName)) + { + return this.HydraProblem($"'The name specified for this delivery channel policy is invalid", null, + 400, "Invalid delivery channel policy"); + } + var validationResult = await validator.ValidateAsync(hydraDeliveryChannelPolicy, policy => policy.IncludeRuleSets("default", "put-patch"), cancellationToken); if (!validationResult.IsValid) { return this.ValidationFailed(validationResult); } - + hydraDeliveryChannelPolicy.CustomerId = customerId; hydraDeliveryChannelPolicy.Name = deliveryChannelPolicyName; hydraDeliveryChannelPolicy.Channel = deliveryChannelName; @@ -210,6 +223,12 @@ public async Task PatchDeliveryChannelPolicy( 400, "Invalid delivery channel policy"); } + if (!IsValidName(deliveryChannelPolicyName)) + { + return this.HydraProblem($"The name specified for this delivery channel policy is invalid", null, + 400, "Invalid delivery channel policy"); + } + var validationResult = await validator.ValidateAsync(hydraDeliveryChannelPolicy, policy => policy.IncludeRuleSets("default", "put-patch"), cancellationToken); if (!validationResult.IsValid) @@ -263,4 +282,10 @@ private bool IsPermittedDeliveryChannel(string deliveryChannelPolicyName) { return allowedDeliveryChannels.Contains(deliveryChannelPolicyName); } + + private bool IsValidName(string? inputName) + { + const string regex = "[\\sA-Z]"; // Delivery channel policy names should not contain capital letters or spaces + return !(string.IsNullOrEmpty(inputName) || Regex.IsMatch(inputName, regex)); + } } \ No newline at end of file diff --git a/src/protagonist/API/Features/DeliveryChannels/Validation/HydraDeliveryChannelPolicyValidator.cs b/src/protagonist/API/Features/DeliveryChannels/Validation/HydraDeliveryChannelPolicyValidator.cs index f2acf6335..57d7539d8 100644 --- a/src/protagonist/API/Features/DeliveryChannels/Validation/HydraDeliveryChannelPolicyValidator.cs +++ b/src/protagonist/API/Features/DeliveryChannels/Validation/HydraDeliveryChannelPolicyValidator.cs @@ -1,4 +1,5 @@ -using FluentValidation; +using System.Text.RegularExpressions; +using FluentValidation; namespace API.Features.DeliveryChannels.Validation;