Skip to content

Commit 6b86ab2

Browse files
authored
fix(designer): Prevent propagation of 401 Unauthorized status from Azure DevOps (#14378)
1 parent 3784722 commit 6b86ab2

File tree

3 files changed

+96
-1
lines changed

3 files changed

+96
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System.Net;
2+
using System.Net.Http;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
6+
namespace Altinn.Studio.Designer.TypedHttpClients.DelegatingHandlers;
7+
8+
public class AzureDevOpsTokenDelegatingHandler : DelegatingHandler
9+
{
10+
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
11+
{
12+
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
13+
14+
if (response.StatusCode == HttpStatusCode.Unauthorized)
15+
{
16+
return new HttpResponseMessage(HttpStatusCode.InternalServerError)
17+
{
18+
Content = new StringContent("Failed to interact with Azure DevOps. Contact system support.")
19+
};
20+
}
21+
22+
return response;
23+
}
24+
}

backend/src/Designer/TypedHttpClients/TypedHttpClientRegistration.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public static class TypedHttpClientRegistration
3838
public static IServiceCollection RegisterTypedHttpClients(this IServiceCollection services, IConfiguration config)
3939
{
4040
services.AddHttpClient();
41+
services.AddTransient<AzureDevOpsTokenDelegatingHandler>();
4142
services.AddTransient<EnsureSuccessHandler>();
4243
services.AddTransient<PlatformBearerTokenHandler>();
4344
services.AddAzureDevOpsTypedHttpClient(config);
@@ -70,7 +71,7 @@ private static IHttpClientBuilder AddAzureDevOpsTypedHttpClient(this IServiceCol
7071
client.BaseAddress = new Uri($"{azureDevOpsSettings.BaseUri}build/builds/");
7172
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
7273
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", token);
73-
}).AddHttpMessageHandler<EnsureSuccessHandler>();
74+
}).AddHttpMessageHandler<AzureDevOpsTokenDelegatingHandler>().AddHttpMessageHandler<EnsureSuccessHandler>();
7475
}
7576

7677
private static IHttpClientBuilder AddKubernetesWrapperTypedHttpClient(this IServiceCollection services)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System.Net;
2+
using System.Net.Http;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using Altinn.Studio.Designer.TypedHttpClients.DelegatingHandlers;
6+
using Moq;
7+
using Moq.Protected;
8+
using Xunit;
9+
10+
public class AzureDevOpsTokenDelegatingHandlerTests
11+
{
12+
[Fact]
13+
public async Task SendAsync_WhenUnauthorized_ThrowsHttpRequestException()
14+
{
15+
// Arrange
16+
var mockInnerHandler = new Mock<HttpMessageHandler>();
17+
18+
mockInnerHandler.Protected()
19+
.Setup<Task<HttpResponseMessage>>("SendAsync",
20+
ItExpr.IsAny<HttpRequestMessage>(),
21+
ItExpr.IsAny<CancellationToken>())
22+
.ReturnsAsync(new HttpResponseMessage
23+
{
24+
StatusCode = HttpStatusCode.Unauthorized
25+
});
26+
27+
var handler = new AzureDevOpsTokenDelegatingHandler
28+
{
29+
InnerHandler = mockInnerHandler.Object
30+
};
31+
32+
HttpClient httpClient = new HttpClient(handler);
33+
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://dev.azure.com");
34+
35+
HttpResponseMessage response = await httpClient.SendAsync(request);
36+
string content = await response.Content.ReadAsStringAsync();
37+
38+
Assert.Equal("Failed to interact with Azure DevOps. Contact system support.", content);
39+
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
40+
}
41+
42+
[Fact]
43+
public async Task SendAsync_WhenSuccessful_ReturnsResponse()
44+
{
45+
var mockInnerHandler = new Mock<HttpMessageHandler>();
46+
47+
mockInnerHandler.Protected()
48+
.Setup<Task<HttpResponseMessage>>("SendAsync",
49+
ItExpr.IsAny<HttpRequestMessage>(),
50+
ItExpr.IsAny<CancellationToken>())
51+
.ReturnsAsync(new HttpResponseMessage
52+
{
53+
StatusCode = HttpStatusCode.OK,
54+
Content = new StringContent("Success")
55+
});
56+
57+
var handler = new AzureDevOpsTokenDelegatingHandler
58+
{
59+
InnerHandler = mockInnerHandler.Object
60+
};
61+
62+
HttpClient httpClient = new HttpClient(handler);
63+
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://dev.azure.com");
64+
65+
HttpResponseMessage response = await httpClient.SendAsync(request);
66+
67+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
68+
Assert.Equal("Success", await response.Content.ReadAsStringAsync());
69+
}
70+
}

0 commit comments

Comments
 (0)