Skip to content
Open
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
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<RootNamespace>Allegro.Extensions.Configuration</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
</ItemGroup>

<Import Project="..\..\Package.Build.props" />
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Microsoft.Extensions.Configuration;

namespace Allegro.Extensions.Configuration
{
/// <summary>
/// <see cref="Microsoft.Extensions.Configuration.IConfigurationProvider"/> wrapper, that simplifies traversing the providers' graph
/// by exposing the <see cref="Inner"/> property.
/// </summary>
public interface IConfigurationProviderWrapper : IConfigurationProvider
{
/// <summary>
/// Inner <see cref="IConfigurationProvider"/> being wrapped.
/// </summary>
IConfigurationProvider Inner { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Microsoft.Extensions.Configuration;

namespace Allegro.Extensions.Configuration
{
/// <summary>
/// <see cref="IConfigurationProvider"/> wrapper that marks the inner provider as sensitive
/// (meaning that it may contain secret values).
/// </summary>
public interface ISensitiveConfigurationProviderWrapper : IConfigurationProviderWrapper
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Microsoft.Extensions.Configuration;

namespace Allegro.Extensions.Configuration
{
/// <summary>
/// <see cref="IConfigurationRoot"/> wrapper, that simplifies traversing the providers' graph
/// by exposing the <see cref="ConfigurationRoot"/> property.
/// </summary>
public interface ITraversableChainedConfigurationProviderWrapper : IConfigurationProvider
{
/// <summary>
/// Inner <see cref="IConfigurationRoot"/> being wrapped.
/// </summary>
IConfigurationRoot ConfigurationRoot { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ServerGarbageCollection>false</ServerGarbageCollection>
<Nullable>enable</Nullable>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="Allegro.Extensions.Configuration.Tests.Integration" />
<InternalsVisibleTo Include="Allegro.Extensions.Configuration.Tests.Unit" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Allegro.Extensions.Configuration.DataContracts\Allegro.Extensions.Configuration.DataContracts.csproj" />
<ProjectReference Include="..\Allegro.Extensions.Configuration\Allegro.Extensions.Configuration.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Allegro.Extensions.AspNetCore" Version="1.3.0" />
</ItemGroup>
<Import Project="..\..\Package.Build.props" />
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Allegro.Extensions.Configuration.Api.Configuration;

public class SecretsConfiguration
{
public string SecretsPath { get; set; } = null!;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Allegro.Extensions.Configuration.Api.Services;
using Microsoft.AspNetCore.Mvc;

namespace Allegro.Extensions.Configuration.Api.Controllers
{
[ApiController]
[Route("global-configuration")]
public class GlobalConfigurationController : ControllerBase
{
private readonly IGlobalConfigurationProvider _globalConfigurationProvider;

public GlobalConfigurationController(IGlobalConfigurationProvider globalConfigurationProvider)
{
_globalConfigurationProvider = globalConfigurationProvider;
}

[HttpGet("context-groups")]
public IActionResult GetGlobalConfiguration([FromQuery] string? serviceName)
{
return Ok(_globalConfigurationProvider.GetGlobalConfiguration(serviceName));
}

[HttpGet("context-groups/{contextGroupName}/contexts/{contextName}")]
public IActionResult GetGlobalConfigurationContext(
[FromRoute] string contextGroupName,
[FromRoute] string contextName)
{
return File(
_globalConfigurationProvider.GetContext(contextGroupName, contextName),
"application/json",
contextName);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Allegro.Extensions.AspNetCore.Attributes;
using Allegro.Extensions.Configuration.Api.Services;
using Allegro.Extensions.Configuration.DataContracts;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Allegro.Extensions.Configuration.Api.Controllers
{
[ApiController]
[Authorize]
[Route("secrets")]
[SkipOnProd]
public class SecretsController : ControllerBase
{
private readonly ISecretsProvider _secretsProvider;

public SecretsController(ISecretsProvider secretsProvider)
{
_secretsProvider = secretsProvider;
}

[HttpPost]
public IActionResult GetSecrets([FromBody] GetSecretsRequest request)
{
return Content(
_secretsProvider.GetSecretsAsJson(request.KeyVaultPrefixes),
"application/json");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Allegro.Extensions.Configuration.Api.Exceptions;

public class NotAllowedOutsideTestEnvException : Exception
{
public NotAllowedOutsideTestEnvException() : base("Not allowed outside of a test environment")
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"Vabank.Confeature.Service": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:6001;http://localhost:6000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"GlobalConfiguration__ContextGroups__0__Name": "vabank-configuration",
"GlobalConfiguration__ContextGroups__0__Path": "/Users/szymon.adach/Repos/global-config"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using Allegro.Extensions.Configuration.DataContracts;
using Allegro.Extensions.Configuration.Extensions;
using Allegro.Extensions.Configuration.GlobalConfiguration;
using Microsoft.Extensions.Options;

namespace Allegro.Extensions.Configuration.Api.Services;

public interface IGlobalConfigurationProvider
{
GetGlobalConfigurationResponse GetGlobalConfiguration(string? serviceName = null);
Stream GetContext(string contextGroupName, string contextName);
}

public class GlobalConfigurationProvider : IGlobalConfigurationProvider
{
private readonly ContextGroupsConfiguration _configuration;

public GlobalConfigurationProvider(IOptions<ContextGroupsConfiguration> configuration)
{
_configuration = configuration.Value;
}

public GetGlobalConfigurationResponse GetGlobalConfiguration(string? serviceName = null)
{
#pragma warning disable CSE001
var result = new GetGlobalConfigurationResponse();
#pragma warning restore CSE001

foreach (var contextGroupConfiguration in _configuration.ContextGroups)
{
var contexts = GetContextsList(contextGroupConfiguration, serviceName).OrderBy(c => c).ToList();
if (contexts.Count == 0)
{
continue;
}

result.ContextGroups.Add(
new ContextGroupModel { Name = contextGroupConfiguration.Name, Contexts = contexts });
}

return result;
}

public Stream GetContext(string contextGroupName, string contextName)
{
var contextGroup = _configuration.ContextGroups
.Single(x => x.Name.Equals(contextGroupName, StringComparison.OrdinalIgnoreCase));
return File.OpenRead(Path.Combine(contextGroup.Path, $"{contextName}.json"));
}

private static IEnumerable<string> GetContextsList(
ContextGroupConfiguration contextGroupConfiguration,
string? serviceName)
{
foreach (var file in Directory.EnumerateFiles(contextGroupConfiguration.Path, "*.json"))
{
if (!string.IsNullOrEmpty(serviceName) &&
!ConfigurationContextExtensions.IsServiceListedForContext(File.OpenRead(file), serviceName))
{
continue;
}

yield return Path.GetFileNameWithoutExtension(file);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Allegro.Extensions.Configuration.Api.Configuration;
using Allegro.Extensions.Configuration.Api.Exceptions;
using Allegro.Extensions.Configuration.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;

namespace Allegro.Extensions.Configuration.Api.Services;

public interface ISecretsProvider
{
string GetSecretsAsJson(IEnumerable<string> keyVaultPrefixes);
}

public class SecretsProvider : ISecretsProvider
{
private readonly EnvironmentConfiguration _environmentConfiguration;
private readonly SecretsConfiguration _secretsConfiguration;

public SecretsProvider(
IOptions<EnvironmentConfiguration> environmentConfiguration,
IOptions<SecretsConfiguration> secretsConfiguration)
{
_environmentConfiguration = environmentConfiguration.Value;
_secretsConfiguration = secretsConfiguration.Value;
}

public string GetSecretsAsJson(IEnumerable<string> keyVaultPrefixes)
{
if (!_environmentConfiguration.IsTestEnvironment)
{
throw new NotAllowedOutsideTestEnvException();
}

var secretsConfiguration =
new ConfigurationBuilder()
.AddKeyPerFile(_secretsConfiguration.SecretsPath)
.Build();

var prefixes = NormalizeKeyVaultPrefixes(keyVaultPrefixes);
return JsonConvert.SerializeObject(
secretsConfiguration
.AsEnumerable()
.Where(x => prefixes.Any(p => x.Key.StartsWith(p, StringComparison.InvariantCultureIgnoreCase)))
.ToDictionary(x => x.Key, x => x.Value));
}

private static string[] NormalizeKeyVaultPrefixes(IEnumerable<string> prefixes)
=> prefixes
.Select(p => p.Replace("--", ConfigurationPath.KeyDelimiter))
.ToArray();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Allegro.Extensions.Configuration.Api.Services;
using Allegro.Extensions.Configuration.Configuration;
using Allegro.Extensions.Configuration.Extensions;
using Microsoft.Extensions.DependencyInjection;

// ReSharper disable ConvertClosureToMethodGroup

namespace Allegro.Extensions.Configuration.Api;

public static class StartupExtensions
{
/// <summary>
/// Registers Confeature V2 dependencies for a Fallback Service.
/// </summary>
public static IServiceCollection AddConfeatureFallbackService(
this IServiceCollection services,
ConfeatureOptions confeatureOptions)
{
services.AddConfeature(confeatureOptions);
services.AddSingleton<IGlobalConfigurationProvider, GlobalConfigurationProvider>();
services.AddSingleton<ISecretsProvider, SecretsProvider>();
return services;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Allegro.Extensions.Configuration.DataContracts\Allegro.Extensions.Configuration.DataContracts.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="RestEase" Version="1.6.4" />
</ItemGroup>

<Import Project="..\..\Package.Build.props" />
</Project>
Loading