Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(designer): Prevent propagation of 401 Unauthorized status from Azure DevOps #14378

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace Altinn.Studio.Designer.TypedHttpClients.DelegatingHandlers;

public class AzureDevOpsTokenDelegatingHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

if (response.StatusCode == HttpStatusCode.Unauthorized)
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("Failed to interact with Azure DevOps. Contact system support.")
};
}

return response;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public static class TypedHttpClientRegistration
public static IServiceCollection RegisterTypedHttpClients(this IServiceCollection services, IConfiguration config)
{
services.AddHttpClient();
services.AddTransient<AzureDevOpsTokenDelegatingHandler>();
services.AddTransient<EnsureSuccessHandler>();
services.AddTransient<PlatformBearerTokenHandler>();
services.AddAzureDevOpsTypedHttpClient(config);
Expand Down Expand Up @@ -70,7 +71,7 @@ private static IHttpClientBuilder AddAzureDevOpsTypedHttpClient(this IServiceCol
client.BaseAddress = new Uri($"{azureDevOpsSettings.BaseUri}build/builds/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", token);
}).AddHttpMessageHandler<EnsureSuccessHandler>();
}).AddHttpMessageHandler<AzureDevOpsTokenDelegatingHandler>().AddHttpMessageHandler<EnsureSuccessHandler>();
}

private static IHttpClientBuilder AddKubernetesWrapperTypedHttpClient(this IServiceCollection services)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Altinn.Studio.Designer.TypedHttpClients.DelegatingHandlers;
using Moq;
using Moq.Protected;
using Xunit;

public class AzureDevOpsTokenDelegatingHandlerTests
{
[Fact]
public async Task SendAsync_WhenUnauthorized_ThrowsHttpRequestException()
{
// Arrange
var mockInnerHandler = new Mock<HttpMessageHandler>();

mockInnerHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.Unauthorized
});
Dismissed Show dismissed Hide dismissed

var handler = new AzureDevOpsTokenDelegatingHandler
{
InnerHandler = mockInnerHandler.Object
};

HttpClient httpClient = new HttpClient(handler);
Dismissed Show dismissed Hide dismissed
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://dev.azure.com");
Dismissed Show dismissed Hide dismissed

HttpResponseMessage response = await httpClient.SendAsync(request);
string content = await response.Content.ReadAsStringAsync();

Assert.Equal("Failed to interact with Azure DevOps. Contact system support.", content);
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
}

[Fact]
public async Task SendAsync_WhenSuccessful_ReturnsResponse()
{
var mockInnerHandler = new Mock<HttpMessageHandler>();

mockInnerHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("Success")
});
Dismissed Show dismissed Hide dismissed

var handler = new AzureDevOpsTokenDelegatingHandler
{
InnerHandler = mockInnerHandler.Object
};

HttpClient httpClient = new HttpClient(handler);
Dismissed Show dismissed Hide dismissed
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://dev.azure.com");
Dismissed Show dismissed Hide dismissed

HttpResponseMessage response = await httpClient.SendAsync(request);

Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("Success", await response.Content.ReadAsStringAsync());
}
}
Loading