Skip to content

Commit

Permalink
Add GCP Secret Manager
Browse files Browse the repository at this point in the history
  • Loading branch information
mjhoffmeister committed Feb 15, 2022
1 parent 73a15fb commit 40d457b
Show file tree
Hide file tree
Showing 19 changed files with 209 additions and 34 deletions.
12 changes: 12 additions & 0 deletions .gcp/asaph-dev.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"type": "service_account",
"project_id": "asaph-dev",
"private_key_id": "8233a3f09f026303d76949bbafa14f61d924e14e",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDbForo+GnsGJqk\nRrdTGcDgsGJu2SULEyu303gRFBtvHjSWddBpYyvIXGOdT+ph/0T3KcytFKmI9Y2M\n4um97+55IgV0SBKb1W6s0Eejl2ofxoGPjRHcIdTtokHmr+WPpYFV/7+xha0Rf4Z3\n2QmBk7UfmN/awXb7MxTKGC4b2ZFlcwINdXXQIf0i8D474uhpNYbeTEQ4kR29WshW\nGSApFy4frbdEjUl0u06JWcfI0EMCPMzD1GbOQFtb6HnU0lD//U0Ett5H3npMbWY+\n4Nya/qGSB+gmeQGrVu/kVQhZowuYh6pReCPGBcj8Wy4DKI510IL/875g4x6I40Ju\nXHvNhS7ZAgMBAAECggEAA6Up+OfvlNOj+KLxXrSWMCbYvGqbryxJrrlNM7UkVhwo\nJW8ex8iBBfuWvLTqFWjsRzoVNoJnGU92MQ3ldBkpD2Lj7FddCxkcY+VJpXMmrnip\ncMkye6y+Bv++hCdEQbhjOoCsbWu0wXT5uJMn3iJzAdLG7RpvvFlq0yH4qNLDeu7/\n2H5dYLKEtIs20GxLXRXmlAk8UtmuOkL3ThejLemEO9U5DExOIzQ9lkD4OhRDdI9L\nrY2Q7XgJnkjZN0Fln/Xq2p56v34mZ+5HRcCc6mtSDXZbmZX8LZ/Pk0a/BP8ZvkW3\nRnL6jTxO5aqma9KnSO4M1OT5e/vqO/fZ748Tci62wQKBgQDynX1GEWP6LgxkF9Ox\ns1GY/ZFWPfk8Fj3wWgRC5uIjYI0JoFOps69Uij3n2AxJypInIM5so4zIKWus7k64\nrWoHGQnQKiCLbuHRpa2N4EeUYhjUIwoAmi0pVlIlGfzs6N8Lab7/bfzqj9D5Vgq+\ngLl/RWa3NNBnhkmKmLeepu5JjQKBgQDnLMYplFGvaJtQ7cz2hwOP42dDyvrMQTim\nJsb+4/IMrwI7dwSmcWpz3sRUzPbjbLzPUlgJOzDgyRqv85yObMo1FB691EfpTkPV\nA4i0ARerJhIjnaNeIVF/9eUMcRyOHglcYLbXSXbmmaEZbNEz+dj+58RwsLjKkitl\nVO/ZqY6ZfQKBgQCbKAzNmqGNhZV3DaXcpwkwBjnEJa4Wt0K1S1weTPmiFkUcOuRG\nSxt9vUsJ0ilJp7sAOwLIh2+pMpQh6+V8RarhDyovbkGR6j+Qi5wKd7xPMM0gHahv\n6imnngS6pXwTJno+GkqDoBt3BrJmQphsbHY05nViBOyiyEaP1ErZs3gAoQKBgGEH\nLuk2wo4/9qiiFtwGUR1skeQnZtqiKVe7gNxs6iQetG4nB0Gg6tBVWMxK9vj/o8PU\nyPSe7mX6ooPlWPmCeeCLYFfGqKQo4Fmg0RjUOI3yPbzLJk2U6HMvzwJI23Ze7wjh\n4vw7bnddVfuo66nIHSboOlAeLIGBlktCuiT+gMa1AoGAB4R6o4Y+Ue+i173G6AbC\nsTODEpXOnBNlgt+RQ9900qc1sBU7518QSJQOe645a4+BIIQ6WwUZgPRoN2qr1Eec\nmazXdT+xDFi8OjhlF1j/RZsp6f5bMVuQkAqadPgtN7NjA7xyE/YFTSu1OMYseyXn\ngfDA5SUUy6LKPDfEclZkidA=\n-----END PRIVATE KEY-----\n",
"client_email": "[email protected]",
"client_id": "117527350958472163100",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/secret-accessor%40asaph-dev.iam.gserviceaccount.com"
}
2 changes: 1 addition & 1 deletion Asaph.Bootstrapper/Asaph.Bootstrapper.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.33.0.40503">
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.35.0.42613">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
8 changes: 4 additions & 4 deletions Asaph.Core.UnitTests/Asaph.Core.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentResults" Version="3.1.0" />
<PackageReference Include="Microsoft.Graph" Version="4.12.0" />
<PackageReference Include="FluentResults" Version="3.2.0" />
<PackageReference Include="Microsoft.Graph" Version="4.17.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.33.0.40503">
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.35.0.42613">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand All @@ -28,7 +28,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.0">
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
4 changes: 2 additions & 2 deletions Asaph.Core/Asaph.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentResults" Version="3.1.0" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.33.0.40503">
<PackageReference Include="FluentResults" Version="3.2.0" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.35.0.42613">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,13 @@ public static async Task TryFindPropertyById_Rank_ReturnsExpectedRankName(

/// <summary>
/// Tests getting all song directors.
/// </summary>
/// </summary>
/// <param name="awsRegionSystemName">AWS region system name.</param>
/// <param name="useDynamoDBLocal">Indicates whether to use Dynamo DB local.</param>
/// <param name="expectedSongDirectorCount">Expected song director count.</param>
/// <returns>The async operation.</returns>
[Theory]
[InlineData("us-east-2", true, 1)]
[InlineData("us-east-2", false, 1)]
public static async Task TryGetAllAsync(
string awsRegionSystemName,
bool useDynamoDBLocal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.33.0.40503">
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.35.0.42613">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand All @@ -26,7 +26,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.0">
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public static async Task TryFindPropertyById_Rank_ReturnsExpectedRankName(
/// <param name="expectedSongDirectorDataModelCount">Expected song director count.</param>
/// <returns>The async operation.</returns>
[Theory]
[InlineData(2)]
[InlineData(1)]
public static async Task TryGetAllAsync_ExistingSongDirectors_ReturnsExpectedCount(
int expectedSongDirectorDataModelCount)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class DynamoDBSongDirectorRepositoryTests
/// <param name="isActive">Is active indicator to add.</param>
/// <returns>The async operation.</returns>
[Theory]
[InlineData("us-east-2", true, "d7a068f8-461d-42f2-a561-5ea2f843c2b3", true)]
[InlineData("us-east-2", false, "d7a068f8-461d-42f2-a561-5ea2f843c2b3", true)]
public static async Task TryAddAsync_ValidSongDirector_Succeeds(
string awsRegionSystemName, bool useDynamoDBLocal, string songDirectorId, bool isActive)
{
Expand Down Expand Up @@ -95,7 +95,7 @@ public static async Task TryFindPropertyByIdAsync_Rank_ReturnsFailedResult(
/// <param name="expectedSongDirectorDataModelCount">Expected song director count.</param>
/// <returns>The async operation.</returns>
[Theory]
[InlineData("us-east-2", true, 2)]
[InlineData("us-east-2", false, 1)]
public static async Task TryGetAllAsync(
string awsRegionSystemName,
bool useDynamoDBLocal,
Expand Down
8 changes: 4 additions & 4 deletions Asaph.Infrastructure/Asaph.Infrastructure.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AWSSDK.DynamoDBv2" Version="3.7.2.2" />
<PackageReference Include="AWSSDK.DynamoDBv2" Version="3.7.2.17" />
<PackageReference Include="AWSSDK.Extensions.NETCore.Setup" Version="3.7.1" />
<PackageReference Include="Azure.Identity" Version="1.5.0" />
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.23.0" />
<PackageReference Include="Microsoft.Graph" Version="4.12.0" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.33.0.40503">
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.24.0" />
<PackageReference Include="Microsoft.Graph" Version="4.17.0" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.35.0.42613">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.33.0.40503">
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.35.0.42613">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand All @@ -21,7 +21,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.0">
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
4 changes: 2 additions & 2 deletions Asaph.WebApi.UnitTests/Asaph.WebApi.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.33.0.40503">
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.35.0.42613">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand All @@ -21,7 +21,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.0">
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
5 changes: 3 additions & 2 deletions Asaph.WebApi/Asaph.WebApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Google.Cloud.SecretManager.V1" Version="1.8.0" />
<PackageReference Include="Hydra.NET" Version="1.0.0-preview.1.14" />
<PackageReference Include="Microsoft.Identity.Web" Version="1.21.1" />
<PackageReference Include="Microsoft.Identity.Web" Version="1.22.3" />
<PackageReference Include="Microsoft.OpenApi" Version="1.2.3" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.14.0" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.33.0.40503">
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.35.0.42613">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
12 changes: 6 additions & 6 deletions Asaph.WebApi/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["Asaph.WebApi.Next/Asaph.WebApi.Next.csproj", "Asaph.WebApi.Next/"]
RUN dotnet restore "Asaph.WebApi.Next/Asaph.WebApi.Next.csproj"
COPY ["Asaph.WebApi/Asaph.WebApi.csproj", "Asaph.WebApi/"]
RUN dotnet restore "Asaph.WebApi/Asaph.WebApi.csproj"
COPY . .
WORKDIR "/src/Asaph.WebApi.Next"
RUN dotnet build "Asaph.WebApi.Next.csproj" -c Release -o /app/build
WORKDIR "/src/Asaph.WebApi"
RUN dotnet build "Asaph.WebApi.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "Asaph.WebApi.Next.csproj" -c Release -o /app/publish
RUN dotnet publish "Asaph.WebApi.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Asaph.WebApi.Next.dll"]
ENTRYPOINT ["dotnet", "Asaph.WebApi.dll"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace Asaph.WebApi.GcpSecretManagerConfigurationProvider;

/// <summary>
/// Provides extensions for adding GCP Secret Manager secrets to configuration.
/// </summary>
public static class GcpSecretManagerConfigurationExtensions
{
/// <summary>
/// Adds GCP Secret Manager as a configuration source.
/// </summary>
/// <param name="builder">Configuration builder.</param>
/// <param name="configuration">Configuration. "Gcp" is the assumed section.</param>
/// <returns>The updated configuration builder.</returns>
public static IConfigurationBuilder AddGcpSecretManager(
this IConfigurationBuilder builder, IConfiguration configuration)
{
string? projectId = configuration["Gcp:ProjectId"];
string? secretManagerCredentialsPath = configuration["Gcp:SecretManagerCredentialsPath"];

builder.Add(new GcpSecretManagerConfigurationSource(
projectId, secretManagerCredentialsPath));

return builder;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using Google.Api.Gax;
using Google.Api.Gax.ResourceNames;
using Google.Cloud.SecretManager.V1;

namespace Asaph.WebApi.GcpSecretManagerConfigurationProvider;

/// <summary>
/// Configuration provider for GCP Secret Manager.
/// </summary>
public class GcpSecretManagerConfigurationProvider : ConfigurationProvider
{
private readonly SecretManagerServiceClient _client;
private readonly string _projectId;

/// <summary>
/// Initializes a new instance of the <see cref="GcpSecretManagerConfigurationProvider"/> class.
/// </summary>
/// <param name="projectId">GCP project id.</param>
/// <param name="secretManagerCredentialsPath">Secret Manager credentials path.</param>
public GcpSecretManagerConfigurationProvider(
string? projectId, string? secretManagerCredentialsPath)
{
if (projectId != null && secretManagerCredentialsPath != null)
{
SecretManagerServiceClientBuilder secretManagerServiceClientBuilder = new();
secretManagerServiceClientBuilder.CredentialsPath = secretManagerCredentialsPath;
_client = secretManagerServiceClientBuilder.Build();
}
else
{
_client = SecretManagerServiceClient.Create();
}

_projectId = string.IsNullOrWhiteSpace(projectId) ? GetGcpProjectId() : projectId;
}

/// <inheritdoc/>
public override void Load()
{
IEnumerable<SecretName>? secretNames = _client
.ListSecrets(new ProjectName(_projectId))?
.Select(i => i.SecretName);

if (secretNames?.Any() == false)
return;

foreach (SecretName secretName in secretNames!)
{
try
{
SecretVersionName secretVersionName = new(
secretName.ProjectId, secretName.SecretId, "latest");

AccessSecretVersionResponse secretVersion = _client
.AccessSecretVersion(secretVersionName);

Set(
NormalizeDelimiter(secretName.SecretId),
secretVersion.Payload.Data.ToStringUtf8());
}
catch (Grpc.Core.RpcException)
{
// Ignore. This might happen if the secret has no versions available.
}
}
}

/// <summary>
/// Gets the GCP project id from the execution platform.
/// </summary>
/// <returns>Project id.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown if GCP execution platform information couldn't be retrieved. This is most likely due
/// to the service not running on GCP (e.g. local testing.)
/// </exception>
private static string GetGcpProjectId()
{
string? projectId = Platform.Instance()?.ProjectId;

if (projectId == null)
{
throw new InvalidOperationException(
"Could not retrieve GCP project id for GcpSecretManagerProvider.");
}

return projectId;
}

/// <summary>
/// Normalizes the "__" (double underscore) key delimeter.
/// </summary>
/// <param name="key">Key.</param>
/// <returns>The normalized key.</returns>
private static string NormalizeDelimiter(string key)
{
return key.Replace("__", ConfigurationPath.KeyDelimiter);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace Asaph.WebApi.GcpSecretManagerConfigurationProvider;

/// <summary>
/// GCP Secret Manager configurtion source.
/// </summary>
public class GcpSecretManagerConfigurationSource : IConfigurationSource
{
// Project id
private readonly string? _projectId;

// Secret Manager credentials path
private readonly string? _secretManagerCredentialsPath;

/// <summary>
/// Initializes a new instance of the <see cref="GcpSecretManagerConfigurationSource"/> class.
/// </summary>
/// <param name="projectId">Project id.</param>
/// <param name="secretManagerCredentialsPath">Secret Manager credentials path.</param>
public GcpSecretManagerConfigurationSource(
string? projectId, string? secretManagerCredentialsPath)
{
_projectId = projectId;
_secretManagerCredentialsPath = secretManagerCredentialsPath;
}

/// <inheritdoc/>
public IConfigurationProvider Build(IConfigurationBuilder builder) =>
new GcpSecretManagerConfigurationProvider(_projectId, _secretManagerCredentialsPath);
}
4 changes: 4 additions & 0 deletions Asaph.WebApi/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Asaph.Core.UseCases;
using Asaph.Core.UseCases.AddSongDirector;
using Asaph.Core.UseCases.GetSongDirectors;
using Asaph.WebApi.GcpSecretManagerConfigurationProvider;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Identity.Web;
using Microsoft.OpenApi;
Expand All @@ -10,7 +11,10 @@

WebApplicationBuilder? builder = WebApplication.CreateBuilder(args);

builder.Configuration.AddGcpSecretManager(builder.Configuration);

string baseUri = builder.Configuration["BaseUri"];

string hydraContextUri = builder.Configuration["HydraContextUri"];

string songDirectorsBaseUri = @$"{baseUri.TrimEnd('/')}/song-directors/";
Expand Down
Loading

0 comments on commit 40d457b

Please sign in to comment.