dbMango/Rms.Service.Bootstrap/Security/ServiceCollectionExtensions.cs
Alexander Shabarshov 2a7a24c9e7 Initial contribution
2025-11-03 14:43:26 +00:00

100 lines
3.8 KiB
C#
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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;
}
}
}