Skip to content

Commit

Permalink
feat: or-1349 add security; delete views on rebuild
Browse files Browse the repository at this point in the history
  • Loading branch information
koenmetsu committed Dec 15, 2023
1 parent c14ceae commit ce4e714
Show file tree
Hide file tree
Showing 23 changed files with 303 additions and 88 deletions.
6 changes: 3 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ services:
ELASTIC_PASSWORD: local_development
discovery.type: single-node
xpack.security.transport.ssl.enabled: false
cluster.routing.allocation.disk.watermark.low: 95%
cluster.routing.allocation.disk.watermark.high: 96%
cluster.routing.allocation.disk.watermark.flood_stage: 97%
cluster.routing.allocation.disk.watermark.low: 97%
cluster.routing.allocation.disk.watermark.high: 98%
cluster.routing.allocation.disk.watermark.flood_stage: 99%
volumes:
- es-data:/usr/share/elasticsearch/data

Expand Down
15 changes: 14 additions & 1 deletion identityserver/vr.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,20 @@
"accessTokenLifetime": -1,
"identityTokenLifetime": -1,
"clientClaimsPrefix": ""
},
{
"clientId": "superAdminClient",
"clientSecrets": [
"secret"
],
"allowedGrantTypes": "clientCredentials",
"allowedScopes": [
"vo_info",
"dv_verenigingsregister_beheer"
],
"accessTokenLifetime": -1,
"identityTokenLifetime": -1,
"clientClaimsPrefix": ""
}
]
}

Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
namespace AssociationRegistry.Acm.Api.Projections;

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Events;
using Marten;
using Marten.Events;
using Marten.Events.Projections;
using Schema.Constants;
using Schema.VerenigingenPerInsz;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

public class VerenigingenPerInszProjection : EventProjection
{
Expand All @@ -18,6 +18,8 @@ public VerenigingenPerInszProjection()
// the newly persisted VerenigingenPerInszDocument from FeitelijkeVerenigingWerdGeregistreerd is not in the
// Query yet when we handle NaamWerdGewijzigd
Options.BatchSize = 1;
Options.DeleteViewTypeOnTeardown<VerenigingenPerInszDocument>();
Options.DeleteViewTypeOnTeardown<VerenigingDocument>();
}

public async Task Project(FeitelijkeVerenigingWerdGeregistreerd werdGeregistreerd, IDocumentOperations ops)
Expand Down
1 change: 1 addition & 0 deletions src/AssociationRegistry.Admin.Api/Constants/Security.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public static class Security
public static class ClaimTypes
{
public const string Scope = "scope";
public const string ClientId = "client_id";
}

public static class Scopes
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
namespace AssociationRegistry.Admin.Api.Infrastructure.ConfigurationBindings;

using System;

public class AppSettings
{
private string? _baseUrl;
private string? _beheerApiBaseUrl;
private string? _beheerProjectionHostBaseUrl;
private string? _publicApiBaseUrl;
private string? _publicProjectionHostBaseUrl;

public string BeheerApiBaseUrl
{
get => _beheerApiBaseUrl?.TrimEnd(trimChar: '/') ?? string.Empty;
set => _beheerApiBaseUrl = value;
}

public string BeheerProjectionHostBaseUrl
{
get => _beheerProjectionHostBaseUrl?.TrimEnd(trimChar: '/') ?? string.Empty;
Expand All @@ -38,6 +33,7 @@ public string BaseUrl
set => _baseUrl = value;
}

public string[] SuperAdminClientIds { get; set; } = Array.Empty<string>();
public string Salt { get; set; } = null!;
public ApiDocsSettings ApiDocs { get; set; } = new();
public SearchSettings Search { get; set; } = new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@ public AdminProjectionHostHttpClient(HttpClient httpClient)
}

public async Task<HttpResponseMessage> RebuildAllProjections(CancellationToken cancellationToken)
=> await _httpClient.PostAsync(requestUri: "/projections/all/rebuild", content: null, cancellationToken);
=> await _httpClient.PostAsync(requestUri: "/v1/projections/all/rebuild", content: null, cancellationToken);

public async Task<HttpResponseMessage> RebuildDetailProjection(CancellationToken cancellationToken)
=> await _httpClient.PostAsync(requestUri: "/projections/detail/rebuild", content: null, cancellationToken);
=> await _httpClient.PostAsync(requestUri: "/v1/projections/detail/rebuild", content: null, cancellationToken);

public async Task<HttpResponseMessage> RebuildHistoriekProjection(CancellationToken cancellationToken)
=> await _httpClient.PostAsync(requestUri: "/projections/historiek/rebuild", content: null, cancellationToken);
=> await _httpClient.PostAsync(requestUri: "/v1/projections/historiek/rebuild", content: null, cancellationToken);

public async Task<HttpResponseMessage> RebuildZoekenProjection(CancellationToken cancellationToken)
=> await _httpClient.PostAsync(requestUri: "/projections/search/rebuild", content: null, cancellationToken);
=> await _httpClient.PostAsync(requestUri: "/v1/projections/search/rebuild", content: null, cancellationToken);

public async Task<HttpResponseMessage> GetStatus(CancellationToken cancellationToken)
{
var request = new HttpRequestMessage(HttpMethod.Get, requestUri: "/projections/status");
var request = new HttpRequestMessage(HttpMethod.Get, requestUri: "/v1/projections/status");
request.Headers.Add(name: "X-Correlation-Id", Guid.NewGuid().ToString());

return await _httpClient.SendAsync(request, cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ public PublicProjectionHostHttpClient(HttpClient httpClient)
}

public async Task<HttpResponseMessage> RebuildDetailProjection(CancellationToken cancellationToken)
=> await _httpClient.PostAsync(requestUri: "/projections/detail/rebuild", content: null, cancellationToken);
=> await _httpClient.PostAsync(requestUri: "/v1/projections/detail/rebuild", content: null, cancellationToken);

public async Task<HttpResponseMessage> RebuildZoekenProjection(CancellationToken cancellationToken)
=> await _httpClient.PostAsync(requestUri: "/projections/search/rebuild", content: null, cancellationToken);
=> await _httpClient.PostAsync(requestUri: "/v1/projections/search/rebuild", content: null, cancellationToken);

public async Task<HttpResponseMessage> GetStatus(CancellationToken cancellationToken)
=> await _httpClient.GetAsync(requestUri: "/projections/status", cancellationToken);
=> await _httpClient.GetAsync(requestUri: "/v1/projections/status", cancellationToken);

public void Dispose()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

using Be.Vlaanderen.Basisregisters.Api;
using HttpClients;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading;
Expand All @@ -12,6 +14,7 @@
[AdvertiseApiVersions("1.0")]
[ApiRoute("projections")]
[ApiExplorerSettings(IgnoreApi = true)]
[Authorize(Policy = Program.SuperAdminPolicyName)]
public class ProjectionHostController : ApiController
{
private readonly AdminProjectionHostHttpClient _adminHttpClient;
Expand All @@ -34,82 +37,86 @@ public async Task<IActionResult> RebuildAdminProjectionAll(CancellationToken can
{
var response = await _adminHttpClient.RebuildAllProjections(cancellationToken);

return response.IsSuccessStatusCode
? Ok()
: UnprocessableEntity();
return await OkOrForwardedResponse(cancellationToken, response);
}

[HttpPost("admin/detail/rebuild")]
public async Task<IActionResult> RebuildAdminProjectionDetail(CancellationToken cancellationToken)
{
var response = await _adminHttpClient.RebuildDetailProjection(cancellationToken);

return response.IsSuccessStatusCode
? Ok()
: UnprocessableEntity();
return await OkOrForwardedResponse(cancellationToken, response);
}

[HttpPost("admin/historiek/rebuild")]
public async Task<IActionResult> RebuildAdminProjectionHistoriek(CancellationToken cancellationToken)
{
var response = await _adminHttpClient.RebuildHistoriekProjection(cancellationToken);

return response.IsSuccessStatusCode
? Ok()
: UnprocessableEntity();
return await OkOrForwardedResponse(cancellationToken, response);
}

[HttpPost("admin/search/rebuild")]
public async Task<IActionResult> RebuildAdminProjectionZoeken(CancellationToken cancellationToken)
{
var response = await _adminHttpClient.RebuildZoekenProjection(cancellationToken);

return response.IsSuccessStatusCode
? Ok()
: UnprocessableEntity();
return await OkOrForwardedResponse(cancellationToken, response);
}

[HttpGet("admin/status")]
public async Task<IActionResult> GetAdminProjectionStatus(CancellationToken cancellationToken)
{
var response = await _adminHttpClient.GetStatus(cancellationToken);

if (!response.IsSuccessStatusCode) return BadRequest();

var projectionProgress = await response.Content.ReadFromJsonAsync<ProjectionStatus[]>(_jsonSerializerOptions, cancellationToken);

return new OkObjectResult(projectionProgress);
return await OkObjectOrForwardedResponse(cancellationToken, response);
}

[HttpPost("public/detail/rebuild")]
public async Task<IActionResult> RebuildPublicProjectionDetail(CancellationToken cancellationToken)
{
var response = await _publicHttpClient.RebuildDetailProjection(cancellationToken);

return response.IsSuccessStatusCode
? Ok()
: UnprocessableEntity();
return await OkOrForwardedResponse(cancellationToken, response);
}

[HttpPost("public/search/rebuild")]
public async Task<IActionResult> RebuildPublicProjectionZoeken(CancellationToken cancellationToken)
{
var response = await _publicHttpClient.RebuildZoekenProjection(cancellationToken);

return response.IsSuccessStatusCode
? Ok()
: UnprocessableEntity();
return await OkOrForwardedResponse(cancellationToken, response);
}

[HttpGet("public/status")]
public async Task<IActionResult> GetPublicProjectionStatus(CancellationToken cancellationToken)
{
var response = await _publicHttpClient.GetStatus(cancellationToken);

if (!response.IsSuccessStatusCode) return BadRequest();
return await OkObjectOrForwardedResponse(cancellationToken, response);
}

var projectionProgress = await response.Content.ReadFromJsonAsync<ProjectionStatus[]>(_jsonSerializerOptions, cancellationToken);
private async Task<IActionResult> OkOrForwardedResponse(CancellationToken cancellationToken, HttpResponseMessage response)
{
if (response.IsSuccessStatusCode) return Ok();

return new OkObjectResult(projectionProgress);
return Problem(
title: response.ReasonPhrase,
statusCode: (int)response.StatusCode,
detail: await response.Content.ReadAsStringAsync(cancellationToken)
);
}

private async Task<IActionResult> OkObjectOrForwardedResponse(CancellationToken cancellationToken, HttpResponseMessage response)
{
if (response.IsSuccessStatusCode)
return new OkObjectResult(
await response.Content.ReadFromJsonAsync<ProjectionStatus[]>(_jsonSerializerOptions, cancellationToken));

return Problem(
title: response.ReasonPhrase,
statusCode: (int)response.StatusCode,
detail: await response.Content.ReadAsStringAsync(cancellationToken)
);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace AssociationRegistry;
namespace AssociationRegistry.Admin.Api.Infrastructure;

using System;

public class ProjectionStatus
{
Expand Down
18 changes: 15 additions & 3 deletions src/AssociationRegistry.Admin.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ namespace AssociationRegistry.Admin.Api;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
Expand Down Expand Up @@ -75,6 +74,7 @@ namespace AssociationRegistry.Admin.Api;
public class Program
{
private const string AdminGlobalPolicyName = "Admin Global";
public const string SuperAdminPolicyName = "Super Admin";

public static async Task Main(string[] args)
{
Expand Down Expand Up @@ -133,7 +133,10 @@ public static async Task Main(string[] args)
app.UseRouting()
.UseAuthentication()
.UseAuthorization()
.UseEndpoints(routeBuilder => routeBuilder.MapControllers().RequireAuthorization(AdminGlobalPolicyName));
.UseEndpoints(routeBuilder =>
{
routeBuilder.MapControllers().RequireAuthorization(AdminGlobalPolicyName);
});

ConfigureLifetimeHooks(app);

Expand Down Expand Up @@ -378,11 +381,20 @@ private static void ConfigureServices(WebApplicationBuilder builder)
.AddControllersAsServices()
.AddAuthorization(
options =>
{
options.AddPolicy(
AdminGlobalPolicyName,
new AuthorizationPolicyBuilder()
.RequireClaim(Security.ClaimTypes.Scope, Security.Scopes.Admin)
.Build()))
.Build());

options.AddPolicy(
SuperAdminPolicyName,
new AuthorizationPolicyBuilder()
.RequireClaim(Security.ClaimTypes.Scope, Security.Scopes.Admin)
.RequireClaim(Security.ClaimTypes.ClientId, appSettings.SuperAdminClientIds)
.Build());
})
.AddNewtonsoftJson(
opt =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,14 @@

"BaseUrl": "http://127.0.0.1:11004/",

"BeheerApiBaseUrl": "http://127.0.0.1:11004/",
"BeheerProjectionHostBaseUrl": "http://127.0.0.1:11006/",
"PublicApiBaseUrl": "http://127.0.0.1:11003/",
"PublicProjectionHostBaseUrl": "http://127.0.0.1:11005/",

"SuperAdminClientIds": [
"superAdminClient"
],

"MagdaOptions": {
"Afzender": "1234",
"Hoedanigheid": "1234",
Expand Down
Loading

0 comments on commit ce4e714

Please sign in to comment.