Skip to content
Merged
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
@@ -1,14 +1,17 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.VisualStudio.TestPlatform.TestHost;
using OrderProcessing.Api.Data;
using OrderProcessing.Api.Features.Orders.Queries.ReadModel;
using OrderProcessing.Api.Security;
using System.Data.Common;

namespace OrderProcessing.Api.Tests.Infrastructure;
Expand All @@ -27,6 +30,23 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Testing");

builder.ConfigureAppConfiguration(
(_, configuration) =>
{
configuration.AddInMemoryCollection(
new Dictionary<string, string?>
{
["AzureAd:Instance"] =
"https://login.microsoftonline.com/",

["AzureAd:TenantId"] =
"00000000-0000-0000-0000-000000000001",

["AzureAd:ClientId"] =
"00000000-0000-0000-0000-000000000002"
});
});

builder.ConfigureServices(services =>
{
// Remove the SQL Server DbContext registration
Expand All @@ -53,7 +73,18 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
services.RemoveAll<IOrderReadModelReader>();

services.AddSingleton<IOrderReadModelReader, TestOrderReadModelReader>();
});

services
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = TestAuthenticationHandler.SchemeName;

options.DefaultChallengeScheme = TestAuthenticationHandler.SchemeName;
})
.AddScheme<AuthenticationSchemeOptions, TestAuthenticationHandler>(
TestAuthenticationHandler.SchemeName, _ => { });
});

}

protected override IHost CreateHost(
Expand Down Expand Up @@ -82,4 +113,52 @@ protected override void Dispose(bool disposing)
_connection.Dispose();
}
}

public HttpClient CreateAuthenticatedClient()
{
return CreateClientWithScopes(ApiScopes.Read, ApiScopes.Write);
}

public HttpClient CreateClientWithScopes(
params string[] scopes)
{
var client = CreateClient();

AddTestUser(client);

if (scopes.Length > 0)
{
client.DefaultRequestHeaders.Add(TestAuthenticationHandler.ScopesHeaderName, string.Join(' ', scopes));
}

return client;
}

public HttpClient CreateClientWithRoles(params string[] roles)
{
var client = CreateClient();

AddTestUser(client);

if (roles.Length > 0)
{
client.DefaultRequestHeaders.Add(TestAuthenticationHandler.RolesHeaderName, string.Join(' ', roles));
}

return client;
}

public HttpClient CreateAuthenticatedClientWithoutPermissions()
{
var client = CreateClient();

AddTestUser(client);

return client;
}

private static void AddTestUser(HttpClient client)
{
client.DefaultRequestHeaders.Add(TestAuthenticationHandler.UserHeaderName, "integration-test-user");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public abstract class IntegrationTestBase : IDisposable
protected IntegrationTestBase()
{
Factory = new CustomWebApplicationFactory();
Client = Factory.CreateClient();
Client = Factory.CreateAuthenticatedClient();
}

public void Dispose()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace OrderProcessing.Api.Tests.Infrastructure;

public sealed class TestAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public const string SchemeName = "TestAuthentication";

public const string UserHeaderName = "X-Test-User";

public const string ScopesHeaderName = "X-Test-Scopes";

public const string RolesHeaderName = "X-Test-Roles";

public TestAuthenticationHandler( IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder)
: base(options, logger, encoder)
{
}

protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.TryGetValue(
UserHeaderName,
out var userHeader) ||
string.IsNullOrWhiteSpace(userHeader.ToString()))
{
return Task.FromResult(
AuthenticateResult.NoResult());
}

var claims = new List<Claim>
{
new(
ClaimTypes.NameIdentifier,
userHeader.ToString()),

new(
ClaimTypes.Name,
"Integration Test User")
};

if (Request.Headers.TryGetValue(
ScopesHeaderName,
out var scopesHeader) &&
!string.IsNullOrWhiteSpace(scopesHeader.ToString()))
{
claims.Add(
new Claim(
"scp",
scopesHeader.ToString()));
}

if (Request.Headers.TryGetValue(
RolesHeaderName,
out var rolesHeader))
{
var roles = rolesHeader
.ToString()
.Split(
' ',
StringSplitOptions.RemoveEmptyEntries |
StringSplitOptions.TrimEntries);

claims.AddRange(
roles.Select(role =>
new Claim("roles", role)));
}

var identity = new ClaimsIdentity(
claims,
SchemeName,
ClaimTypes.Name,
"roles");

var principal = new ClaimsPrincipal(identity);

var ticket = new AuthenticationTicket(
principal,
SchemeName);

return Task.FromResult(
AuthenticateResult.Success(ticket));
}
}
Loading
Loading