-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
34db4a3
commit acecd9c
Showing
15 changed files
with
416 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
Defra.Cdp.Backend.Api.Tests/Resources/payload-get-all-secrets.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"source": "cdp-secret-manager-lambda", | ||
"statusCode": 200, | ||
"action": "get_all_secret_keys", | ||
"body": { | ||
"get_all_secret_keys": true, | ||
"keys": { | ||
"cdp/services/test-service-one": [ | ||
"placeholder", | ||
"EXAMPLE_ONE" | ||
], | ||
"cdp/services/test-service-two": [ | ||
"placeholder", | ||
"EXAMPLE_TWO" | ||
] | ||
}, | ||
"exception": "", | ||
"environment": "dev" | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
Defra.Cdp.Backend.Api.Tests/Services/Secrets/SecretEventHandlerTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
using System.Text.Json; | ||
using Defra.Cdp.Backend.Api.Models; | ||
using Defra.Cdp.Backend.Api.Services.Secrets; | ||
using Defra.Cdp.Backend.Api.Services.Secrets.events; | ||
using NSubstitute; | ||
|
||
namespace Defra.Cdp.Backend.Api.Tests.Services.Secrets; | ||
|
||
public class SecretEventHandlerTest | ||
{ | ||
|
||
[Fact] | ||
public async void WillProcessGetAllSecretsPayload() | ||
{ | ||
var service = Substitute.For<ISecretsService>(); | ||
var eventHandler = new SecretEventHandler(service, ConsoleLogger.CreateLogger<SecretEventHandler>()); | ||
|
||
var mockPayload = SecretEventHandler.TryParseMessageHeader(await File.ReadAllTextAsync("Resources/payload-get-all-secrets.json")); | ||
|
||
Assert.NotNull(mockPayload); | ||
service | ||
.UpdateSecrets(Arg.Any<List<TenantSecrets>>(), Arg.Any<CancellationToken>()) | ||
.Returns(Task.CompletedTask); | ||
await eventHandler.Handle(mockPayload, new CancellationToken()); | ||
|
||
await service.ReceivedWithAnyArgs().UpdateSecrets(Arg.Any<List<TenantSecrets>>(), Arg.Any<CancellationToken>()); | ||
|
||
} | ||
|
||
[Fact] | ||
public void TryParseMessageHeaderWithValidPayload() | ||
{ | ||
var body = "{\"source\": \"cdp-secret-manager-lambda\", \"statusCode\": 200, \"action\": \"get_all_secret_keys\", \"body\": {}}"; | ||
var res = SecretEventHandler.TryParseMessageHeader(body); | ||
Assert.NotNull(res); | ||
} | ||
|
||
[Fact] | ||
public void TryParseMessageHeaderInvalid() | ||
{ | ||
var otherLambda = "{\"source\": \"cdp-some-other-lambda\", \"statusCode\": 200, \"action\": \"get_all_secret_keys\", \"body\": {}}"; | ||
var res = SecretEventHandler.TryParseMessageHeader(otherLambda); | ||
Assert.Null(res); | ||
|
||
var otherMessage = "{\"foo\": \"bar\"}"; | ||
res = SecretEventHandler.TryParseMessageHeader(otherMessage); | ||
Assert.Null(res); | ||
|
||
var invalidJson = "<tag>foo</tag>"; | ||
res = SecretEventHandler.TryParseMessageHeader(invalidJson); | ||
Assert.Null(res); | ||
} | ||
|
||
[Fact] | ||
public void CanExtractBody() | ||
{ | ||
var body = "{\"source\": \"cdp-secret-manager-lambda\", \"statusCode\": 200, \"action\": \"get_all_secret_keys\", \"body\": " + | ||
"{ \"get_all_secret_keys\": true, " + | ||
"\"exception\": \"\", " + | ||
"\"environment\": \"dev\", " + | ||
"\"keys\": {\"cdp/service/foo\": [\"FOO\"]}" + | ||
"}}"; | ||
var res = SecretEventHandler.TryParseMessageHeader(body); | ||
Assert.NotNull(res); | ||
|
||
var parsedBody = res.Body.Deserialize<BodyGetAllSecretKeys>(); | ||
Assert.Equal("dev", parsedBody?.Environment); | ||
Assert.Equal("", parsedBody?.Exception); | ||
Assert.Equal(1, parsedBody?.Keys.Count); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace Defra.Cdp.Backend.Api.Config; | ||
|
||
public class SecretEventListenerOptions | ||
{ | ||
public const string Prefix = "SecretManagerEvents"; | ||
public string QueueUrl { get; set; } = null!; | ||
public bool Enabled { get; set; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
using Defra.Cdp.Backend.Api.Models; | ||
using Defra.Cdp.Backend.Api.Services.Secrets; | ||
using Microsoft.AspNetCore.Mvc; | ||
|
||
namespace Defra.Cdp.Backend.Api.Endpoints; | ||
|
||
public static class TenantSecretsEndpoint | ||
{ | ||
public static IEndpointRouteBuilder MapTenantSecretsEndpoint(this IEndpointRouteBuilder app) | ||
{ | ||
app.MapGet("secrets/{environment}/{service}", FindTenantSecrets); | ||
return app; | ||
} | ||
|
||
static async Task<IResult> FindTenantSecrets([FromServices] ISecretsService secretsService, string environment, | ||
string service, CancellationToken cancellationToken) | ||
{ | ||
var secrets = await secretsService.FindSecrets(environment, service, cancellationToken); | ||
return secrets == null ? Results.NotFound(new ApiError("secrets not found")) : Results.Ok(secrets); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using System.Text.Json.Serialization; | ||
using MongoDB.Bson; | ||
using MongoDB.Bson.Serialization.Attributes; | ||
using MongoDB.Bson.Serialization.IdGenerators; | ||
|
||
namespace Defra.Cdp.Backend.Api.Models; | ||
|
||
public class TenantSecrets | ||
{ | ||
[BsonId(IdGenerator = typeof(ObjectIdGenerator))] | ||
[BsonIgnoreIfDefault] | ||
[JsonIgnore(Condition = JsonIgnoreCondition.Always)] | ||
public ObjectId? Id { get; init; } = default; | ||
|
||
[property: JsonPropertyName("service")] | ||
public string Service { get; init; } = default!; | ||
|
||
[property: JsonPropertyName("environment")] | ||
public string Environment { get; init; } = default!; | ||
|
||
[property: JsonPropertyName("secrets")] | ||
public List<string> Secrets { get; init; } = new(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
Defra.Cdp.Backend.Api/Services/Secrets/SecretEventHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
using System.Text.Json; | ||
using Defra.Cdp.Backend.Api.Models; | ||
using Defra.Cdp.Backend.Api.Services.Secrets.events; | ||
|
||
namespace Defra.Cdp.Backend.Api.Services.Secrets; | ||
|
||
public interface ISecretEventHandler | ||
{ | ||
Task Handle(MessageHeader header, CancellationToken cancellationToken); | ||
} | ||
|
||
/** | ||
* Handles specific payloads sent by the secret manager lambda. | ||
* All messages have the same outer body detailing the source & action. | ||
*/ | ||
public class SecretEventHandler : ISecretEventHandler | ||
{ | ||
|
||
private readonly ISecretsService _secretsService; | ||
private readonly ILogger<SecretEventHandler> _logger; | ||
|
||
public SecretEventHandler(ISecretsService secretsService, ILogger<SecretEventHandler> logger) | ||
{ | ||
_logger = logger; | ||
_secretsService = secretsService; | ||
} | ||
|
||
public async Task Handle(MessageHeader header, CancellationToken cancellationToken) | ||
{ | ||
switch (header.Action) | ||
{ | ||
case "get_all_secret_keys": | ||
await HandleGetAllSecrets(header, cancellationToken); | ||
break; | ||
default: | ||
_logger.LogDebug("Ignoring action: {Action} not handled", header.Action); | ||
return; | ||
} | ||
} | ||
|
||
/** | ||
* Handler for get_all_secret_keys action. Contains a dict of all the services in an environment that have | ||
* secret values set along with a list of the key/environment variable the secret is bound to, | ||
* but NOT the actual secret itself. | ||
*/ | ||
public async Task HandleGetAllSecrets(MessageHeader header, CancellationToken cancellationToken) | ||
{ | ||
var body = header.Body?.Deserialize<BodyGetAllSecretKeys>(); | ||
if (body == null) | ||
{ | ||
_logger.LogInformation("Failed to parse body of 'get_all_secret_keys' message"); | ||
return; | ||
} | ||
|
||
if (body.Exception != "") | ||
{ | ||
_logger.LogError("get_all_secret_keys message contained exception {}", body.Exception); | ||
return; | ||
} | ||
|
||
_logger.LogInformation("Updating secrets in {Environment}", body.Environment); | ||
var secrets = new List<TenantSecrets>(); | ||
|
||
foreach (var kv in body.Keys) | ||
{ | ||
var service = kv.Key.Replace("cdp/services/", ""); | ||
secrets.Add(new TenantSecrets | ||
{ | ||
Service = service, Environment = body.Environment, Secrets = kv.Value | ||
}); | ||
} | ||
|
||
await _secretsService.UpdateSecrets(secrets, cancellationToken); | ||
_logger.LogInformation("Updated secrets for {Environment}", body.Environment); | ||
} | ||
|
||
public static MessageHeader? TryParseMessageHeader(string body) | ||
{ | ||
try | ||
{ | ||
var header = JsonSerializer.Deserialize<MessageHeader>(body); | ||
return header?.Source != "cdp-secret-manager-lambda" ? null : header; | ||
} | ||
catch(Exception e) | ||
{ | ||
return null; | ||
} | ||
} | ||
} |
Oops, something went wrong.