feat(release): prepare 1.1.0 candidate
This commit is contained in:
parent
5a34f66981
commit
5205dcdfa9
736 changed files with 115667 additions and 12203 deletions
119
apps/api-server.Tests/AdminAuthorizationE2ETests.cs
Normal file
119
apps/api-server.Tests/AdminAuthorizationE2ETests.cs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Xunit;
|
||||
|
||||
namespace D3ROVoice.Api.Tests;
|
||||
|
||||
[CollectionDefinition("Api server integration", DisableParallelization = true)]
|
||||
public sealed class ApiServerIntegrationCollection;
|
||||
|
||||
[Collection("Api server integration")]
|
||||
public sealed class AdminAuthorizationE2ETests : IClassFixture<AdminAuthorizationE2ETests.ApiFactory>
|
||||
{
|
||||
private const string Secret = "admin-e2e-jwt-secret-0123456789-abcdef";
|
||||
private const string Issuer = "https://admin-e2e.test";
|
||||
private const string Audience = "d3ro-admin-e2e";
|
||||
private readonly ApiFactory _factory;
|
||||
|
||||
public AdminAuthorizationE2ETests(ApiFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AdminReadEndpointsRejectAnonymousAndNonManager()
|
||||
{
|
||||
using var client = _factory.CreateClient();
|
||||
Assert.Equal(HttpStatusCode.Unauthorized, (await client.GetAsync("/api/admin/stats")).StatusCode);
|
||||
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token("User"));
|
||||
Assert.Equal(HttpStatusCode.Forbidden, (await client.GetAsync("/api/admin/stats")).StatusCode);
|
||||
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token("manager"));
|
||||
Assert.Equal(HttpStatusCode.OK, (await client.GetAsync("/api/admin/stats")).StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AdminMutationRejectsManagerAndAcceptsNormalizedSuperAdmin()
|
||||
{
|
||||
using var client = _factory.CreateClient();
|
||||
using var emptyPayload = new StringContent("{}", Encoding.UTF8, "application/json");
|
||||
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token("Manager"));
|
||||
Assert.Equal(HttpStatusCode.Forbidden,
|
||||
(await client.PostAsync("/api/admin/endpoints", emptyPayload)).StatusCode);
|
||||
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token("super_admin"));
|
||||
using var secondPayload = new StringContent("{}", Encoding.UTF8, "application/json");
|
||||
Assert.Equal(HttpStatusCode.BadRequest,
|
||||
(await client.PostAsync("/api/admin/endpoints", secondPayload)).StatusCode);
|
||||
}
|
||||
|
||||
private static string Token(string role)
|
||||
{
|
||||
var credentials = new SigningCredentials(
|
||||
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Secret)),
|
||||
SecurityAlgorithms.HmacSha256);
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: Issuer,
|
||||
audience: Audience,
|
||||
claims:
|
||||
[
|
||||
new Claim(ClaimTypes.NameIdentifier, "1"),
|
||||
new Claim(ClaimTypes.Email, "admin@example.com"),
|
||||
new Claim(ClaimTypes.Role, role)
|
||||
],
|
||||
expires: DateTime.UtcNow.AddMinutes(5),
|
||||
signingCredentials: credentials);
|
||||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||
}
|
||||
|
||||
public sealed class ApiFactory : WebApplicationFactory<Program>
|
||||
{
|
||||
private readonly string _databasePath = Path.Combine(
|
||||
Path.GetTempPath(), $"d3ro-admin-auth-e2e-{Guid.NewGuid():N}.db");
|
||||
private readonly Dictionary<string, string?> _previousEnvironment = new();
|
||||
|
||||
public ApiFactory()
|
||||
{
|
||||
SetEnvironment("JWT_SECRET", Secret);
|
||||
SetEnvironment("JWT_ISSUER", Issuer);
|
||||
SetEnvironment("JWT_AUDIENCE", Audience);
|
||||
SetEnvironment("D3RO_API_TOKEN", "fixture-internal-gateway-token-32-bytes-minimum");
|
||||
SetEnvironment("DB_PATH", _databasePath);
|
||||
SetEnvironment("CORS_ALLOWED_ORIGINS", "http://localhost:3001");
|
||||
SetEnvironment("ALLOWED_HOSTS", "localhost;127.0.0.1");
|
||||
}
|
||||
|
||||
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
||||
{
|
||||
builder.UseEnvironment("Development");
|
||||
builder.ConfigureLogging(logging => logging.ClearProviders());
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
SqliteConnection.ClearAllPools();
|
||||
if (File.Exists(_databasePath)) File.Delete(_databasePath);
|
||||
foreach (var (name, value) in _previousEnvironment)
|
||||
{
|
||||
Environment.SetEnvironmentVariable(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetEnvironment(string name, string value)
|
||||
{
|
||||
_previousEnvironment[name] = Environment.GetEnvironmentVariable(name);
|
||||
Environment.SetEnvironmentVariable(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue