100 lines
3.8 KiB
C#
100 lines
3.8 KiB
C#
/*
|
||
* dbMango
|
||
*
|
||
* Copyright 2025 Deutsche Bank AG
|
||
* SPDX-License-Identifier: Apache-2.0
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*/
|
||
using Microsoft.Extensions.Options;
|
||
using Microsoft.Extensions.Primitives;
|
||
|
||
namespace Rms.Service.Bootstrap.Security;
|
||
|
||
public static class ServiceCollectionExtensions
|
||
{
|
||
|
||
public static IServiceCollection ConfigureProtected<TOptions>(this IServiceCollection services, IConfigurationSection section)
|
||
where TOptions : class, new() =>
|
||
services.AddSingleton(provider =>
|
||
{
|
||
var logger = provider.GetRequiredService<ILogger<ProtectedConfigurationSection>>();
|
||
var passwordManager = provider.GetRequiredService<IPasswordManager>();
|
||
var configuration = provider.GetRequiredService<IConfiguration>();
|
||
|
||
section = new ProtectedConfigurationSection(passwordManager, configuration, section, logger);
|
||
|
||
var options = section.Get<TOptions>() ?? new();
|
||
return Options.Create(options);
|
||
});
|
||
|
||
private class ProtectedConfigurationSection(
|
||
IPasswordManager passwordManager,
|
||
IConfiguration config,
|
||
IConfigurationSection section,
|
||
ILogger<ProtectedConfigurationSection> logger)
|
||
: IConfigurationSection
|
||
{
|
||
public IConfigurationSection GetSection(string key) => new ProtectedConfigurationSection(passwordManager, config, section.GetSection(key), logger);
|
||
|
||
public IEnumerable<IConfigurationSection> GetChildren() =>
|
||
section.GetChildren()
|
||
.Select(x => new ProtectedConfigurationSection(passwordManager, config, x, logger));
|
||
|
||
public IChangeToken GetReloadToken() => section.GetReloadToken();
|
||
|
||
public string? this[string key]
|
||
{
|
||
get => GetProtectedValue(section[key], key);
|
||
set => section[key] = value;
|
||
}
|
||
|
||
public string Key => section.Key;
|
||
public string Path => section.Path;
|
||
|
||
public string? Value
|
||
{
|
||
get => GetProtectedValue(section.Value, Key);
|
||
set => section.Value = value;
|
||
}
|
||
|
||
private string? GetProtectedValue(string? value, string key)
|
||
{
|
||
if (value == null)
|
||
return null;
|
||
|
||
// redirect first as parameter value may still be encrypted
|
||
while (value.StartsWith("config:"))
|
||
{
|
||
var paramName = value["config:".Length..];
|
||
if (config[paramName] is { } configValue)
|
||
{
|
||
//logger.LogDebug($"Redirecting configuration item: Key=\"{key}\" Value=\"{configValue}\"");
|
||
value = configValue;
|
||
}
|
||
else
|
||
{
|
||
logger.LogWarning($"Configuration item is not found: Key=\"{key}\" ParamName=\"{paramName}\"");
|
||
break;
|
||
}
|
||
}
|
||
if (value.StartsWith('*') || value.StartsWith('@') || value.StartsWith('#'))
|
||
{
|
||
logger.LogDebug($"Decrypting configuration item: Key=\"{key}\" Length={value.Length}");
|
||
return passwordManager.DecryptPassword(value);
|
||
}
|
||
|
||
return value;
|
||
}
|
||
}
|
||
} |