Compare commits
50 Commits
feat/clien
...
66ed34b664
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66ed34b664 | ||
|
|
d7b4c382cd | ||
|
|
4f6ca3524a | ||
|
|
bd1ae4246d | ||
|
|
d92475c230 | ||
|
|
15705cccc4 | ||
|
|
a8403087f6 | ||
|
|
0235c83075 | ||
|
|
63aeba982f | ||
|
|
514495fc8d | ||
|
|
9752fb14ec | ||
|
|
b3629661a1 | ||
|
|
f38bad8531 | ||
|
|
154478c318 | ||
|
|
155eb563d1 | ||
|
|
4aacc3f650 | ||
|
|
f40c86ed63 | ||
|
|
b32f0df125 | ||
|
|
324a5bdb1e | ||
|
|
e0a6787a87 | ||
|
|
c6a4038eab | ||
|
|
58c8520c08 | ||
|
|
eced1a5afc | ||
|
|
7da93c6719 | ||
|
|
6a92466490 | ||
|
|
5d9d756b91 | ||
|
|
f14aaa75e1 | ||
|
|
249f5a0ae5 | ||
|
|
30177cf0c7 | ||
|
|
68ef0a7537 | ||
|
|
fe2ee78d14 | ||
|
|
53e6f37a09 | ||
|
|
7ec85b4e30 | ||
|
|
a9ebc406f3 | ||
|
|
d013d3edfa | ||
|
|
f267fe955b | ||
|
|
644283cf8f | ||
|
|
82aa8d1143 | ||
|
|
7459f05748 | ||
|
|
36f75d003a | ||
|
|
76ce64691a | ||
|
|
7c03282066 | ||
|
|
7ae95b729f | ||
|
|
9ee8a51664 | ||
|
|
b1d1a898b8 | ||
|
|
4ed3e79565 | ||
|
|
8d9de4502e | ||
|
|
7dd91c73c4 | ||
|
|
65c64a3f9a | ||
|
|
1d600aa453 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -410,3 +410,4 @@ FodyWeavers.xsd
|
|||||||
/DigitalData.Core.ConsoleApp/FooHttpOptions.cs
|
/DigitalData.Core.ConsoleApp/FooHttpOptions.cs
|
||||||
/DigitalData.Core.Tests/obj/
|
/DigitalData.Core.Tests/obj/
|
||||||
/DigitalData.Core.Terminal
|
/DigitalData.Core.Terminal
|
||||||
|
/DigitalData.Core.Tests.API
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace DigitalData.Core.API
|
|||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class BasicCRUDControllerBase<TCRUDService, TDto, TEntity, TId> : CRUDControllerBase<TCRUDService, TDto, TDto, TDto, TEntity, TId>
|
public class BasicCRUDControllerBase<TCRUDService, TDto, TEntity, TId> : CRUDControllerBase<TCRUDService, TDto, TDto, TDto, TEntity, TId>
|
||||||
where TCRUDService : ICRUDService<TDto, TDto, TDto, TEntity, TId>
|
where TCRUDService : ICRUDService<TDto, TDto, TEntity, TId>
|
||||||
where TDto : class, IUnique<TId>
|
where TDto : class, IUnique<TId>
|
||||||
where TEntity : class, IUnique<TId>
|
where TEntity : class, IUnique<TId>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,13 +11,12 @@ namespace DigitalData.Core.API
|
|||||||
/// <typeparam name="TCRUDService">The derived CRUD service type implementing ICRUDService<TCreateDto, TReadDto, TUpdateDto, TEntity, TId>.</typeparam>
|
/// <typeparam name="TCRUDService">The derived CRUD service type implementing ICRUDService<TCreateDto, TReadDto, TUpdateDto, TEntity, TId>.</typeparam>
|
||||||
/// <typeparam name="TCreateDto">The Data Transfer Object type for create operations.</typeparam>
|
/// <typeparam name="TCreateDto">The Data Transfer Object type for create operations.</typeparam>
|
||||||
/// <typeparam name="TReadDto">The Data Transfer Object type for read operations.</typeparam>
|
/// <typeparam name="TReadDto">The Data Transfer Object type for read operations.</typeparam>
|
||||||
/// <typeparam name="TUpdateDto">The Data Transfer Object type for update operations.</typeparam>
|
|
||||||
/// <typeparam name="TEntity">The entity type CRUD operations will be performed on.</typeparam>
|
/// <typeparam name="TEntity">The entity type CRUD operations will be performed on.</typeparam>
|
||||||
/// <typeparam name="TId">The type of the entity's identifier.</typeparam>
|
/// <typeparam name="TId">The type of the entity's identifier.</typeparam>
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class CRUDControllerBase<TCRUDService, TCreateDto, TReadDto, TUpdateDto, TEntity, TId> : ControllerBase
|
public class CRUDControllerBase<TCRUDService, TCreateDto, TReadDto, TUpdateDto, TEntity, TId> : ControllerBase
|
||||||
where TCRUDService : ICRUDService<TCreateDto, TReadDto, TUpdateDto, TEntity, TId>
|
where TCRUDService : ICRUDService<TCreateDto, TReadDto, TEntity, TId>
|
||||||
where TCreateDto : class
|
where TCreateDto : class
|
||||||
where TReadDto : class
|
where TReadDto : class
|
||||||
where TUpdateDto : class, IUnique<TId>
|
where TUpdateDto : class, IUnique<TId>
|
||||||
|
|||||||
@@ -12,13 +12,12 @@ namespace DigitalData.Core.API
|
|||||||
/// <typeparam name="TCRUDService">The derived CRUD service type implementing ICRUDService<TCreateDto, TReadDto, TUpdateDto, TEntity, TId>.</typeparam>
|
/// <typeparam name="TCRUDService">The derived CRUD service type implementing ICRUDService<TCreateDto, TReadDto, TUpdateDto, TEntity, TId>.</typeparam>
|
||||||
/// <typeparam name="TCreateDto">The Data Transfer Object type for create operations.</typeparam>
|
/// <typeparam name="TCreateDto">The Data Transfer Object type for create operations.</typeparam>
|
||||||
/// <typeparam name="TReadDto">The Data Transfer Object type for read operations.</typeparam>
|
/// <typeparam name="TReadDto">The Data Transfer Object type for read operations.</typeparam>
|
||||||
/// <typeparam name="TUpdateDto">The Data Transfer Object type for update operations.</typeparam>
|
|
||||||
/// <typeparam name="TEntity">The entity type CRUD operations will be performed on.</typeparam>
|
/// <typeparam name="TEntity">The entity type CRUD operations will be performed on.</typeparam>
|
||||||
/// <typeparam name="TId">The type of the entity's identifier.</typeparam>
|
/// <typeparam name="TId">The type of the entity's identifier.</typeparam>
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class CRUDControllerBaseWithErrorHandling<TCRUDService, TCreateDto, TReadDto, TUpdateDto, TEntity, TId> : ControllerBase
|
public class CRUDControllerBaseWithErrorHandling<TCRUDService, TCreateDto, TReadDto, TUpdateDto, TEntity, TId> : ControllerBase
|
||||||
where TCRUDService : ICRUDService<TCreateDto, TReadDto, TUpdateDto, TEntity, TId>
|
where TCRUDService : ICRUDService<TCreateDto, TReadDto, TEntity, TId>
|
||||||
where TCreateDto : class
|
where TCreateDto : class
|
||||||
where TReadDto : class
|
where TReadDto : class
|
||||||
where TUpdateDto : class, IUnique<TId>
|
where TUpdateDto : class, IUnique<TId>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace DigitalData.Core.API
|
|||||||
/// <param name="index">The key in the ViewData dictionary where the value will be stored.</param>
|
/// <param name="index">The key in the ViewData dictionary where the value will be stored.</param>
|
||||||
/// <param name="value">The value to be stored in the ViewData dictionary.</param>
|
/// <param name="value">The value to be stored in the ViewData dictionary.</param>
|
||||||
/// <returns>The same ViewResult object with updated ViewData, allowing for additional chained operations.</returns>
|
/// <returns>The same ViewResult object with updated ViewData, allowing for additional chained operations.</returns>
|
||||||
public static ViewResult WithData(this ViewResult viewResult, string index, object value)
|
public static ViewResult WithData(this ViewResult viewResult, string index, object? value)
|
||||||
{
|
{
|
||||||
viewResult.ViewData[index] = value;
|
viewResult.ViewData[index] = value;
|
||||||
return viewResult;
|
return viewResult;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<Description>This package provides a comprehensive set of API controllers and related utilities for the DigitalData.Core library. It includes generic CRUD controllers, localization extensions, middleware for security policies, and application model conventions.</Description>
|
<Description>This package provides a comprehensive set of API controllers and related utilities for the DigitalData.Core library. It includes generic CRUD controllers, localization extensions, middleware for security policies, and application model conventions.</Description>
|
||||||
<PackageId>DigitalData.Core.API</PackageId>
|
<PackageId>DigitalData.Core.API</PackageId>
|
||||||
<Version>2.0.0.0</Version>
|
<Version>2.1.1</Version>
|
||||||
<Authors>Digital Data GmbH</Authors>
|
<Authors>Digital Data GmbH</Authors>
|
||||||
<Company>Digital Data GmbH</Company>
|
<Company>Digital Data GmbH</Company>
|
||||||
<Product>DigitalData.Core.API</Product>
|
<Product>DigitalData.Core.API</Product>
|
||||||
@@ -16,6 +16,8 @@
|
|||||||
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
|
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
|
||||||
<PackageTags>digital data core api</PackageTags>
|
<PackageTags>digital data core api</PackageTags>
|
||||||
<PackageIcon>core_icon.png</PackageIcon>
|
<PackageIcon>core_icon.png</PackageIcon>
|
||||||
|
<AssemblyVersion>2.1.1</AssemblyVersion>
|
||||||
|
<FileVersion>2.1.1</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace DigitalData.Core.Abstractions.Application
|
|||||||
/// This interface is useful for entities that do not require different DTOs for different operations,
|
/// This interface is useful for entities that do not require different DTOs for different operations,
|
||||||
/// allowing for a more concise and maintainable codebase when implementing services for such entities.
|
/// allowing for a more concise and maintainable codebase when implementing services for such entities.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public interface IBasicCRUDService<TDto, TEntity, TId> : ICRUDService<TDto, TDto, TDto, TEntity, TId>
|
public interface IBasicCRUDService<TDto, TEntity, TId> : ICRUDService<TDto, TDto, TEntity, TId>
|
||||||
where TDto : class, IUnique<TId> where TEntity : class, IUnique<TId>
|
where TDto : class, IUnique<TId> where TEntity : class, IUnique<TId>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace DigitalData.Core.Abstractions.Application
|
namespace DigitalData.Core.Abstractions.Application
|
||||||
{
|
{
|
||||||
public interface ICRUDService<TCreateDto, TReadDto, TUpdateDto, TEntity, TId> : IReadService<TReadDto, TEntity, TId>
|
public interface ICRUDService<TCreateDto, TReadDto, TEntity, TId> : IReadService<TReadDto, TEntity, TId>
|
||||||
where TCreateDto : class where TReadDto : class where TUpdateDto : IUnique<TId> where TEntity : class, IUnique<TId>
|
where TCreateDto : class where TReadDto : class where TEntity : class, IUnique<TId>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Asynchronously creates a new entity based on the provided <paramref name="createDto"/> and returns the identifier of the created entity wrapped in a <see cref="DataResult{TId}"/>.
|
/// Asynchronously creates a new entity based on the provided <paramref name="createDto"/> and returns the identifier of the created entity wrapped in a <see cref="DataResult{TId}"/>.
|
||||||
@@ -20,6 +20,6 @@ namespace DigitalData.Core.Abstractions.Application
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="updateDto">The updateDTO with updated values for the entity.</param>
|
/// <param name="updateDto">The updateDTO with updated values for the entity.</param>
|
||||||
/// <returns>An Result indicating the outcome of the update operation, with an appropriate message.</returns>
|
/// <returns>An Result indicating the outcome of the update operation, with an appropriate message.</returns>
|
||||||
Task<Result> UpdateAsync(TUpdateDto updateDto);
|
Task<Result> UpdateAsync<TUpdateDto>(TUpdateDto updateDto) where TUpdateDto : IUnique<TId>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,9 +17,9 @@
|
|||||||
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
|
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
|
||||||
<PackAsTool>False</PackAsTool>
|
<PackAsTool>False</PackAsTool>
|
||||||
<PackageIcon>core_icon.png</PackageIcon>
|
<PackageIcon>core_icon.png</PackageIcon>
|
||||||
<Version>2.2.1</Version>
|
<Version>3.0.0</Version>
|
||||||
<AssemblyVersion>2.2.1</AssemblyVersion>
|
<AssemblyVersion>3.0.0</AssemblyVersion>
|
||||||
<FileVersion>2.2.1</FileVersion>
|
<FileVersion>3.0.0</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
namespace DigitalData.Core.Abstractions.Security
|
namespace DigitalData.Core.Abstractions.Security
|
||||||
{
|
{
|
||||||
public interface IAsymCryptService<TParams> : IRSAFactory<TParams>
|
public interface IAsymCryptService : IRSAFactory, IEnumerable<IRSADecryptor>
|
||||||
{
|
{
|
||||||
public IEnumerable<IRSADecryptor> Decryptors { get; }
|
IEnumerable<IRSADecryptor> Decryptors { get; }
|
||||||
|
|
||||||
public IEnumerable<IRSAEncryptor> Encryptors { get; }
|
IRSADecryptor Vault { get; }
|
||||||
|
|
||||||
|
IRSADecryptor this[string key] { get; }
|
||||||
|
|
||||||
|
IEnumerable<IRSAEncryptor> Encryptors { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface IAsymCryptService<TParams> : IAsymCryptService, IRSAFactory<TParams> { }
|
||||||
}
|
}
|
||||||
@@ -8,14 +8,8 @@ namespace DigitalData.Core.Abstractions.Security
|
|||||||
|
|
||||||
public RSAEncryptionPadding Padding { get; init; }
|
public RSAEncryptionPadding Padding { get; init; }
|
||||||
|
|
||||||
public string? Directory { get; set; }
|
|
||||||
|
|
||||||
public string? FileName { get; set; }
|
|
||||||
|
|
||||||
public string Issuer { get; init; }
|
public string Issuer { get; init; }
|
||||||
|
|
||||||
public string Audience { get; init; }
|
public string Audience { get; init; }
|
||||||
|
|
||||||
public void Init();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
public interface IRSADecryptor : IRSACryptographer
|
public interface IRSADecryptor : IRSACryptographer
|
||||||
{
|
{
|
||||||
public bool Encrypt { get; init; }
|
public bool IsEncrypted { get; init; }
|
||||||
|
|
||||||
IRSAEncryptor Encryptor { get; }
|
IRSAEncryptor Encryptor { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -2,15 +2,24 @@
|
|||||||
|
|
||||||
namespace DigitalData.Core.Abstractions.Security
|
namespace DigitalData.Core.Abstractions.Security
|
||||||
{
|
{
|
||||||
public interface IRSAFactory<TParams>
|
public interface IRSAFactory
|
||||||
{
|
{
|
||||||
string CreateRSAPrivateKeyPem(int? keySizeInBits = null);
|
string CreatePrivateKeyPem(int? keySizeInBits = null, bool encrypt = false);
|
||||||
|
|
||||||
string CreateEncryptedPrivateKeyPem(
|
string CreateEncryptedPrivateKeyPem(
|
||||||
int? keySizeInBits = null,
|
|
||||||
string? password = null,
|
|
||||||
PbeEncryptionAlgorithm? pbeEncryptionAlgorithm = null,
|
PbeEncryptionAlgorithm? pbeEncryptionAlgorithm = null,
|
||||||
HashAlgorithmName? hashAlgorithmName = null,
|
HashAlgorithmName? hashAlgorithmName = null,
|
||||||
int? iterationCount = null);
|
int? iterationCount = null,
|
||||||
|
int? keySizeInBits = null,
|
||||||
|
string? password = null);
|
||||||
|
|
||||||
|
string CreateEncryptedPrivateKeyPem(
|
||||||
|
PbeParameters pbeParameters,
|
||||||
|
int? keySizeInBits = null,
|
||||||
|
string? password = null);
|
||||||
|
|
||||||
|
IRSADecryptor CreateDecryptor(string pem, string? issuer = null, string? audience = null, bool encrypt = false, RSAEncryptionPadding? padding = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface IRSAFactory<TParams> : IRSAFactory { }
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ namespace DigitalData.Core.Application
|
|||||||
/// and a culture-specific translation service for any necessary text translations, ensuring a versatile and internationalized approach to CRUD operations.
|
/// and a culture-specific translation service for any necessary text translations, ensuring a versatile and internationalized approach to CRUD operations.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class BasicCRUDService<TCRUDRepository, TDto, TEntity, TId> :
|
public class BasicCRUDService<TCRUDRepository, TDto, TEntity, TId> :
|
||||||
CRUDService<TCRUDRepository, TDto, TDto, TDto, TEntity, TId>, IBasicCRUDService<TDto, TEntity, TId>
|
CRUDService<TCRUDRepository, TDto, TDto, TEntity, TId>, IBasicCRUDService<TDto, TEntity, TId>
|
||||||
where TCRUDRepository : ICRUDRepository<TEntity, TId> where TDto : class, IUnique<TId> where TEntity : class, IUnique<TId>
|
where TCRUDRepository : ICRUDRepository<TEntity, TId> where TDto : class, IUnique<TId> where TEntity : class, IUnique<TId>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -12,11 +12,10 @@ namespace DigitalData.Core.Application
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TCreateDto">The DTO type for create operations.</typeparam>
|
/// <typeparam name="TCreateDto">The DTO type for create operations.</typeparam>
|
||||||
/// <typeparam name="TReadDto">The DTO type for read operations.</typeparam>
|
/// <typeparam name="TReadDto">The DTO type for read operations.</typeparam>
|
||||||
/// <typeparam name="TUpdateDto">The DTO type for update operations.</typeparam>
|
|
||||||
/// <typeparam name="TEntity">The entity type.</typeparam>
|
/// <typeparam name="TEntity">The entity type.</typeparam>
|
||||||
/// <typeparam name="TId">The type of the identifier for the entity.</typeparam>
|
/// <typeparam name="TId">The type of the identifier for the entity.</typeparam>
|
||||||
public class CRUDService<TCRUDRepository, TCreateDto, TReadDto, TUpdateDto, TEntity, TId> : ReadService<TCRUDRepository, TReadDto, TEntity, TId>, ICRUDService<TCreateDto, TReadDto, TUpdateDto, TEntity, TId>
|
public class CRUDService<TCRUDRepository, TCreateDto, TReadDto, TEntity, TId> : ReadService<TCRUDRepository, TReadDto, TEntity, TId>, ICRUDService<TCreateDto, TReadDto, TEntity, TId>
|
||||||
where TCRUDRepository : ICRUDRepository<TEntity, TId> where TCreateDto : class where TReadDto : class where TUpdateDto : IUnique<TId> where TEntity : class, IUnique<TId>
|
where TCRUDRepository : ICRUDRepository<TEntity, TId> where TCreateDto : class where TReadDto : class where TEntity : class, IUnique<TId>
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -45,7 +44,7 @@ namespace DigitalData.Core.Application
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="updateDto">The DTO to update an entity from.</param>
|
/// <param name="updateDto">The DTO to update an entity from.</param>
|
||||||
/// <returns>A service message indicating success or failure.</returns>
|
/// <returns>A service message indicating success or failure.</returns>
|
||||||
public virtual async Task<Result> UpdateAsync(TUpdateDto updateDto)
|
public virtual async Task<Result> UpdateAsync<TUpdateDto>(TUpdateDto updateDto) where TUpdateDto : IUnique<TId>
|
||||||
{
|
{
|
||||||
var currentEntitiy = await _repository.ReadByIdAsync(updateDto.Id);
|
var currentEntitiy = await _repository.ReadByIdAsync(updateDto.Id);
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ namespace DigitalData.Core.Application
|
|||||||
/// <typeparam name="TCRUDRepository">The repository type that provides CRUD operations for entities of type TEntity.</typeparam>
|
/// <typeparam name="TCRUDRepository">The repository type that provides CRUD operations for entities of type TEntity.</typeparam>
|
||||||
/// <typeparam name="TCreateDto">The DTO type used for create operations.</typeparam>
|
/// <typeparam name="TCreateDto">The DTO type used for create operations.</typeparam>
|
||||||
/// <typeparam name="TReadDto">The DTO type used for read operations.</typeparam>
|
/// <typeparam name="TReadDto">The DTO type used for read operations.</typeparam>
|
||||||
/// <typeparam name="TUpdateDto">The DTO type used for update operations.</typeparam>
|
|
||||||
/// <typeparam name="TEntity">The entity type corresponding to the DTOs.</typeparam>
|
/// <typeparam name="TEntity">The entity type corresponding to the DTOs.</typeparam>
|
||||||
/// <typeparam name="TId">The type of the entity's identifier.</typeparam>
|
/// <typeparam name="TId">The type of the entity's identifier.</typeparam>
|
||||||
/// <typeparam name="TProfile">The AutoMapper profile type for configuring mappings between the DTOs and the entity.</typeparam>
|
/// <typeparam name="TProfile">The AutoMapper profile type for configuring mappings between the DTOs and the entity.</typeparam>
|
||||||
@@ -48,9 +47,9 @@ namespace DigitalData.Core.Application
|
|||||||
/// <param name="configureService">An optional action to configure additional services for the CRUD service.</param>
|
/// <param name="configureService">An optional action to configure additional services for the CRUD service.</param>
|
||||||
/// <returns>The original <see cref="IServiceCollection"/> instance, allowing further configuration.</returns>
|
/// <returns>The original <see cref="IServiceCollection"/> instance, allowing further configuration.</returns>
|
||||||
public static IServiceCollection AddCleanCRUDService<TCRUDRepository, TCreateDto, TReadDto, TUpdateDto, TEntity, TId, TProfile>(this IServiceCollection services, Action<IServiceCollection>? configureService = null)
|
public static IServiceCollection AddCleanCRUDService<TCRUDRepository, TCreateDto, TReadDto, TUpdateDto, TEntity, TId, TProfile>(this IServiceCollection services, Action<IServiceCollection>? configureService = null)
|
||||||
where TCRUDRepository : ICRUDRepository<TEntity, TId> where TCreateDto : class where TReadDto : class where TUpdateDto : class, IUnique<TId> where TEntity : class, IUnique<TId> where TProfile : Profile
|
where TCRUDRepository : ICRUDRepository<TEntity, TId> where TCreateDto : class where TReadDto : class, IUnique<TId> where TEntity : class, IUnique<TId> where TProfile : Profile
|
||||||
{
|
{
|
||||||
services.AddScoped<ICRUDService<TCreateDto, TReadDto, TUpdateDto, TEntity, TId>, CRUDService<TCRUDRepository, TCreateDto, TReadDto, TUpdateDto, TEntity, TId>>();
|
services.AddScoped<ICRUDService<TCreateDto, TReadDto, TEntity, TId>, CRUDService<TCRUDRepository, TCreateDto, TReadDto, TEntity, TId>>();
|
||||||
configureService?.Invoke(services);
|
configureService?.Invoke(services);
|
||||||
|
|
||||||
services.AddAutoMapper(typeof(TProfile).Assembly);
|
services.AddAutoMapper(typeof(TProfile).Assembly);
|
||||||
|
|||||||
@@ -14,7 +14,9 @@
|
|||||||
<PackageIcon>core_icon.png</PackageIcon>
|
<PackageIcon>core_icon.png</PackageIcon>
|
||||||
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
|
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
|
||||||
<PackageTags>digital data core application clean architecture</PackageTags>
|
<PackageTags>digital data core application clean architecture</PackageTags>
|
||||||
<Version>2.0.0.0</Version>
|
<Version>3.0.1</Version>
|
||||||
|
<AssemblyVersion>3.0.1</AssemblyVersion>
|
||||||
|
<FileVersion>3.0.1</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\DigitalData.Core.Abstractions\DigitalData.Core.Abstractions.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace DigitalData.Core.Security.Extensions
|
|
||||||
{
|
|
||||||
public static class Extensions
|
|
||||||
{
|
|
||||||
public static string ToBase64String(this byte[] bytes) => Convert.ToBase64String(bytes);
|
|
||||||
|
|
||||||
public static byte[] Base64ToByte(this string base64String) => Convert.FromBase64String(base64String);
|
|
||||||
|
|
||||||
public static byte[] ToBytes(this string str) => System.Text.Encoding.UTF8.GetBytes(str);
|
|
||||||
|
|
||||||
public static string BytesToString(this byte[] bytes) => System.Text.Encoding.UTF8.GetString(bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
using DigitalData.Core.Abstractions.Security;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
|
|
||||||
namespace DigitalData.Core.Security.Extensions
|
|
||||||
{
|
|
||||||
public static class RSAExtensions
|
|
||||||
{
|
|
||||||
public static RSA ToRSA(this string pem)
|
|
||||||
{
|
|
||||||
var rsa = RSA.Create();
|
|
||||||
rsa.ImportFromPem(pem);
|
|
||||||
return rsa;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string CreatePath(string filename, string? directory = null)
|
|
||||||
{
|
|
||||||
directory ??= Environment.CurrentDirectory;
|
|
||||||
|
|
||||||
if (!Directory.Exists(directory))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Path.Combine(directory, $"{filename}.pem");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<string, SemaphoreSlim> FileLocks = new();
|
|
||||||
|
|
||||||
public static void SavePem(this IRSACryptographer decryptor, string key, string? directory = null)
|
|
||||||
{
|
|
||||||
var filePath = CreatePath(filename: key, directory : directory);
|
|
||||||
var fileLock = FileLocks.GetOrAdd(filePath, _ => new (1, 1));
|
|
||||||
fileLock.Wait();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
File.WriteAllText(filePath, decryptor.Pem);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
fileLock.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task SavePemAsync(this IRSACryptographer decryptor, string key, string? directory = null)
|
|
||||||
{
|
|
||||||
var filePath = CreatePath(filename: key, directory: directory);
|
|
||||||
var fileLock = FileLocks.GetOrAdd(filePath, _ => new (1, 1));
|
|
||||||
await fileLock.WaitAsync();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await File.WriteAllTextAsync(filePath, decryptor.Pem);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
fileLock.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,18 +3,68 @@ using DigitalData.Core.Security.Config;
|
|||||||
using DigitalData.Core.Security.Cryptographer;
|
using DigitalData.Core.Security.Cryptographer;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
namespace DigitalData.Core.Security
|
namespace DigitalData.Core.Security
|
||||||
{
|
{
|
||||||
public class AsymCryptService<TAsymCryptParams> : RSAFactory<TAsymCryptParams>, IAsymCryptService<TAsymCryptParams>, IRSAFactory<TAsymCryptParams> where TAsymCryptParams : AsymCryptParams
|
public class AsymCryptService<TAsymCryptParams> : RSAFactory<TAsymCryptParams>, IAsymCryptService<TAsymCryptParams>, IRSAFactory<TAsymCryptParams>, IEnumerable<IRSADecryptor>
|
||||||
|
where TAsymCryptParams : AsymCryptParams
|
||||||
{
|
{
|
||||||
public IEnumerable<IRSADecryptor> Decryptors => _params.Decryptors;
|
public IEnumerable<IRSADecryptor> Decryptors { get; }
|
||||||
|
|
||||||
public IEnumerable<IRSAEncryptor> Encryptors => _params.Encryptors;
|
/// <summary>
|
||||||
|
/// It is a separate decryptor for permanently stored encrypted data. It is assigned to the first Default decryptor by default.
|
||||||
|
/// </summary>
|
||||||
|
public IRSADecryptor Vault { get; }
|
||||||
|
|
||||||
|
public IRSADecryptor this[string key]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var key_params = key.Split(_params.KeyNameSeparator);
|
||||||
|
|
||||||
|
if (key_params.Length != 2)
|
||||||
|
throw new ArgumentException($"Invalid key format. Expected two segments separated by '{_params.KeyNameSeparator}', but received: '{key}'.", nameof(key));
|
||||||
|
|
||||||
|
return _params.Decryptors.FirstOrDefault(d => d.Issuer == key_params[0] && d.Audience == key_params[1])
|
||||||
|
?? throw new KeyNotFoundException($"No decryptor found matching the issuer '{key_params[0]}' and audience '{key_params[1]}'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IRSADecryptor this[int index] => index < 0 || index >= Decryptors.Count()
|
||||||
|
? Decryptors.ElementAt(index)
|
||||||
|
: throw new ArgumentOutOfRangeException(
|
||||||
|
nameof(index),
|
||||||
|
index,
|
||||||
|
$"The index {index} is out of range. The valid indices for {GetType()} are between 0 and {Decryptors.Count() - 1} (inclusive). Please ensure the index is within this range.");
|
||||||
|
|
||||||
public AsymCryptService(IOptions<TAsymCryptParams> options, ILogger<AsymCryptService<TAsymCryptParams>>? logger = null) : base(options)
|
public AsymCryptService(IOptions<TAsymCryptParams> options, ILogger<AsymCryptService<TAsymCryptParams>>? logger = null) : base(options)
|
||||||
{
|
{
|
||||||
logger?.LogInformation("Core.Secrets version: {Version}, Created on: {CreationDate}.", Secrets.Version, Secrets.CreationDate.ToString("dd.MM.yyyy"));
|
logger?.LogInformation("Core.Secrets version: {Version}, Created on: {CreationDate}.", Secrets.Version, Secrets.CreationDate.ToString("dd.MM.yyyy"));
|
||||||
|
|
||||||
|
if (!_params.Decryptors.Any())
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Any decryptor is not found. Ensure that at least one decryptor is configured in the provided parameters. " +
|
||||||
|
"This issue typically arises if the configuration for decryptors is incomplete or missing. " +
|
||||||
|
"Check the 'Decryptors' collection in the configuration and verify that it contains valid entries."
|
||||||
|
);
|
||||||
|
|
||||||
|
Decryptors = _params.Decryptors;
|
||||||
|
|
||||||
|
Vault = _params.Vault ?? Decryptors.First();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<IRSADecryptor> GetEnumerator() => Decryptors.GetEnumerator();
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => Decryptors.GetEnumerator();
|
||||||
|
|
||||||
|
public IEnumerable<IRSAEncryptor> Encryptors
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
foreach (var decryptor in Decryptors)
|
||||||
|
yield return decryptor.Encryptor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,59 +1,96 @@
|
|||||||
using DigitalData.Core.Abstractions.Security;
|
using DigitalData.Core.Security.Cryptographer;
|
||||||
|
|
||||||
namespace DigitalData.Core.Security.Config
|
namespace DigitalData.Core.Security.Config
|
||||||
{
|
{
|
||||||
public class AsymCryptParams : RSAFactoryParams
|
public class AsymCryptParams : RSAFactoryParams
|
||||||
{
|
{
|
||||||
public string Directory { get; init; } = string.Empty;
|
public string PemDirectory { get; init; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 0: Issuer - 1: Audience - 2: Type tag - 3: Version
|
/// Represents the separator used to concatenate the components of a file-related token string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string FileNameFormat { get; init; } = "{0}_-_{1}_-_{2}_-_{3}.pem";
|
/// <remarks>
|
||||||
|
/// The resulting file-related token string is constructed as follows:
|
||||||
|
/// <c>string.Join(FileNameSeparator, Issuer, Audience, Secret_version)</c>.
|
||||||
|
/// If <c>Secret_version</c> is not null, it will be included in the concatenation.
|
||||||
|
/// </remarks>
|
||||||
|
/// <example>
|
||||||
|
/// For example, if <c>FileNameSeparator = "_-_"</c>, the output might look like:
|
||||||
|
/// <c>"Issuer_-_Audience_-_Secret_version"</c>.
|
||||||
|
/// </example>
|
||||||
|
public string FileNameSeparator { get; init; } = "_-_";
|
||||||
|
|
||||||
public string EncryptorTag { get; init; } = "public";
|
public string FileExtension { get; init; } = "pem";
|
||||||
|
|
||||||
public string DecryptorTag { get; init; } = "private";
|
/// <summary>
|
||||||
|
/// Represents the separator used to concatenate the components of a key-related token string.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The resulting key-related token string is constructed as follows:
|
||||||
|
/// <c>string.Join(KeyNameSeparator, Issuer, Audience, Secret_version)</c>.
|
||||||
|
/// If <c>Secret_version</c> is not null, it will be included in the concatenation.
|
||||||
|
/// </remarks>
|
||||||
|
/// <example>
|
||||||
|
/// For example, if <c>KeyNameSeparator = ":"</c>, the output might look like:
|
||||||
|
/// <c>"Issuer:Audience:Secret_version"</c>.
|
||||||
|
/// </example>
|
||||||
|
public string KeyNameSeparator { get; init; } = ":";
|
||||||
|
|
||||||
public string EncryptedDecryptorTag { get; init; } = "enc-private";
|
/// <summary>
|
||||||
|
///This is the subtext of the pem file name. For the file to be automatically renewed, the name must be assigned to change periodically. For example, by default MM/2 will be refreshed every 2 months.
|
||||||
|
/// <br />
|
||||||
|
/// - <see cref="StringExtensions.ToTag(DateTime, string)" /> is used when converting to tag.
|
||||||
|
/// <br />
|
||||||
|
/// - If the format contains the symbol “//”, the method divides the numeric value obtained from the left side of the format
|
||||||
|
/// by one minus the numeric value obtained from the right side of the format string and adds one. For instance:
|
||||||
|
/// <br />
|
||||||
|
/// - If the date is 02.03.2024 and the format is "MM//2", it extracts the month (02), subtracts one (3), divides it by 2,
|
||||||
|
/// rounds down the outgoing number (1), adds one to the number (resulting in 2).
|
||||||
|
/// <br />
|
||||||
|
/// - If the format does not contain "//", the method uses the default <see cref="DateTime.ToString"/> format.
|
||||||
|
/// <br />
|
||||||
|
/// This method provides a way to format the date based on typical or customized rules, including mathematical operations like division.
|
||||||
|
/// </summary>
|
||||||
|
public string DateTagFormat { get; init; } = "MM//2";
|
||||||
|
|
||||||
public IEnumerable<IRSADecryptor> Decryptors { get; init; } = new List<IRSADecryptor>();
|
public IEnumerable<RSADecryptor> Decryptors { get; init; } = new List<RSADecryptor>();
|
||||||
|
|
||||||
public IEnumerable<IRSAEncryptor> Encryptors { get; init; } = new List<IRSAEncryptor>();
|
public RSADecryptor? Vault { get; init; }
|
||||||
|
|
||||||
private string TypeTagOf(IRSACryptographer crypt)
|
public AsymCryptParams() => AfterCreate += () =>
|
||||||
{
|
|
||||||
if (crypt is IRSAEncryptor)
|
|
||||||
return EncryptorTag;
|
|
||||||
else if (crypt is IRSADecryptor decryptor)
|
|
||||||
return decryptor.Encrypt ? EncryptedDecryptorTag : DecryptorTag;
|
|
||||||
else
|
|
||||||
throw new InvalidOperationException(
|
|
||||||
"Unknown cryptographer type. The crypt parameter must be either IRSAEncryptor or IRSADecryptor.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnDeserialized()
|
|
||||||
{
|
|
||||||
base.OnDeserialized();
|
|
||||||
|
|
||||||
var cryptographers = Encryptors.Cast<IRSACryptographer>().Concat(Decryptors.Cast<IRSACryptographer>());
|
|
||||||
|
|
||||||
foreach (var crypt in cryptographers)
|
|
||||||
{
|
{
|
||||||
// set default path
|
// Create root folder if it does not exist
|
||||||
if (crypt.Pem is null)
|
if (!Directory.Exists(PemDirectory))
|
||||||
{
|
Directory.CreateDirectory(PemDirectory);
|
||||||
crypt.Directory ??= Directory;
|
|
||||||
crypt.FileName ??= string.Format(
|
|
||||||
FileNameFormat,
|
|
||||||
crypt.Issuer,
|
|
||||||
crypt.Audience,
|
|
||||||
TypeTagOf(crypt),
|
|
||||||
Secrets.Version);
|
|
||||||
}
|
|
||||||
|
|
||||||
crypt.Init();
|
foreach (var decryptor in Decryptors)
|
||||||
}
|
{
|
||||||
}
|
// set default path
|
||||||
|
if (decryptor.IsPemNull)
|
||||||
|
{
|
||||||
|
var file_name_params = new List<object> { decryptor.Issuer, decryptor.Audience, KeySizeInBits, DateTime.Now.ToTag(DateTagFormat) };
|
||||||
|
if (decryptor.IsEncrypted)
|
||||||
|
file_name_params.Add(Secrets.Version);
|
||||||
|
|
||||||
|
var file_name = $"{string.Join(FileNameSeparator, file_name_params)}.{FileExtension}";
|
||||||
|
|
||||||
|
var path = Path.Combine(PemDirectory, file_name);
|
||||||
|
|
||||||
|
if (File.Exists(path))
|
||||||
|
decryptor.SetPem(File.ReadAllText(path));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var pem = decryptor.IsEncrypted
|
||||||
|
? Instance.RSAFactory.CreateEncryptedPrivateKeyPem(pbeParameters: PbeParameters, keySizeInBits: KeySizeInBits, password: Secrets.PBE_PASSWORD)
|
||||||
|
: Instance.RSAFactory.CreatePrivateKeyPem(keySizeInBits: KeySizeInBits);
|
||||||
|
|
||||||
|
decryptor.SetPem(pem);
|
||||||
|
|
||||||
|
// Save file in background
|
||||||
|
Task.Run(async () => await File.WriteAllTextAsync(path: path, pem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace DigitalData.Core.Security.Config
|
||||||
|
{
|
||||||
|
public class ParamsConfigureOptions<TParams> : IConfigureOptions<TParams> where TParams : RSAFactoryParams
|
||||||
|
{
|
||||||
|
public void Configure(TParams options) => options.Init();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Security.Cryptography;
|
using System.Reflection;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace DigitalData.Core.Security.Config
|
namespace DigitalData.Core.Security.Config
|
||||||
@@ -11,7 +12,16 @@ namespace DigitalData.Core.Security.Config
|
|||||||
|
|
||||||
public PbeEncryptionAlgorithm PbeEncryptionAlgorithm { get; init; } = PbeEncryptionAlgorithm.Aes256Cbc;
|
public PbeEncryptionAlgorithm PbeEncryptionAlgorithm { get; init; } = PbeEncryptionAlgorithm.Aes256Cbc;
|
||||||
|
|
||||||
public HashAlgorithmName PbeHashAlgorithmName { get; init; } = HashAlgorithmName.SHA256;
|
public HashAlgorithmName PbeHashAlgorithm { get; init; } = HashAlgorithmName.SHA256;
|
||||||
|
|
||||||
|
// TODO: add as json converter to IConfigurIConfiguration.Config
|
||||||
|
public string PbeHashAlgorithmName
|
||||||
|
{
|
||||||
|
get => PbeHashAlgorithm.ToString();
|
||||||
|
init => PbeHashAlgorithm = (typeof(HashAlgorithmName).GetProperty(value, BindingFlags.Public | BindingFlags.Static)?.GetValue(null) is HashAlgorithmName hashAlgorithmName)
|
||||||
|
? hashAlgorithmName
|
||||||
|
: new(value);
|
||||||
|
}
|
||||||
|
|
||||||
public int PbeIterationCount { get; init; } = 100_000;
|
public int PbeIterationCount { get; init; } = 100_000;
|
||||||
|
|
||||||
@@ -22,6 +32,28 @@ namespace DigitalData.Core.Security.Config
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public PbeParameters PbeParameters => _pbeParameters!;
|
public PbeParameters PbeParameters => _pbeParameters!;
|
||||||
|
|
||||||
public virtual void OnDeserialized() => _pbeParameters = new PbeParameters(PbeEncryptionAlgorithm, PbeHashAlgorithmName, PbeIterationCount);
|
/// <summary>
|
||||||
|
/// Provides a thread-safe initialization mechanism using Lazy initialization.
|
||||||
|
/// </summary>
|
||||||
|
private readonly Lazy<bool> _lazyInitializer;
|
||||||
|
|
||||||
|
public bool IsInitialized => _lazyInitializer.IsValueCreated;
|
||||||
|
|
||||||
|
public RSAFactoryParams()
|
||||||
|
{
|
||||||
|
_lazyInitializer = new(() =>
|
||||||
|
{
|
||||||
|
AfterCreate?.Invoke();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
AfterCreate += () => _pbeParameters = new PbeParameters(PbeEncryptionAlgorithm, PbeHashAlgorithm, PbeIterationCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected event Action AfterCreate;
|
||||||
|
|
||||||
|
public void Init() => _ = _lazyInitializer.Value;
|
||||||
|
|
||||||
|
public void OnDeserialized() => Init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
83
DigitalData.Core.Security/Config/StringExtensions.cs
Normal file
83
DigitalData.Core.Security/Config/StringExtensions.cs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
namespace DigitalData.Core.Security.Config
|
||||||
|
{
|
||||||
|
internal static class StringExtensions
|
||||||
|
{
|
||||||
|
public static string ToBase64String(this byte[] bytes) => Convert.ToBase64String(bytes);
|
||||||
|
|
||||||
|
public static byte[] Base64ToByte(this string base64String) => Convert.FromBase64String(base64String);
|
||||||
|
|
||||||
|
public static byte[] ToBytes(this string str) => System.Text.Encoding.UTF8.GetBytes(str);
|
||||||
|
|
||||||
|
public static string BytesToString(this byte[] bytes) => System.Text.Encoding.UTF8.GetString(bytes);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a <see cref="DateTime"/> to a formatted string based on the specified format string.
|
||||||
|
/// <br />
|
||||||
|
/// - If the format contains the symbol “//”, the method divides the numeric value obtained from the left side of the format
|
||||||
|
/// by one minus the numeric value obtained from the right side of the format string and adds one. For instance:
|
||||||
|
/// <br />
|
||||||
|
/// - If the date is 02.03.2024 and the format is "MM//2", it extracts the month (02), subtracts one (3), divides it by 2,
|
||||||
|
/// rounds down the outgoing number (1), adds one to the number (resulting in 2).
|
||||||
|
/// <br />
|
||||||
|
/// - If the format does not contain "//", the method uses the default <see cref="DateTime.ToString"/> format.
|
||||||
|
/// <br />
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="date">The <see cref="DateTime"/> value to be formatted.</param>
|
||||||
|
/// <param name="format">The format string that dictates the formatting of the date. If the format includes the "//" symbol,
|
||||||
|
/// it splits the string at "//" and divides the left-side value by the right-side value. The format string can include standard
|
||||||
|
/// <see cref="DateTime.ToString"/> format patterns.</param>
|
||||||
|
/// <returns>A string representation of the formatted date, or the result of the division operation if "//" is present in the format.</returns>
|
||||||
|
/// <exception cref="ArgumentException">Thrown if the format string is invalid, such as having an incorrect number of parts after "//".</exception>
|
||||||
|
/// <exception cref="DivideByZeroException">Thrown if the right side of the "//" contains a zero, resulting in division by zero.</exception>
|
||||||
|
/// <exception cref="FormatException">Thrown if either the left-side or right-side value of "//" cannot be parsed as an integer.</exception>
|
||||||
|
public static string ToTag(this DateTime date, string format)
|
||||||
|
{
|
||||||
|
if (format is not null && format.Contains("//"))
|
||||||
|
{
|
||||||
|
var subStrings = format.Split("//");
|
||||||
|
|
||||||
|
if (subStrings.Length != 2)
|
||||||
|
throw new ArgumentException($"Date tag format {format} is invalid. It must contain exactly one '//' separator.", nameof(format));
|
||||||
|
|
||||||
|
var formattedLeft = date.ToString(subStrings[0]);
|
||||||
|
|
||||||
|
if (!int.TryParse(formattedLeft, out var dateValue))
|
||||||
|
throw new FormatException($"The left-side value ({formattedLeft}) of the format could not be parsed to an integer.");
|
||||||
|
|
||||||
|
if (!int.TryParse(subStrings[1], out var divisor))
|
||||||
|
throw new FormatException($"The right-side value ({divisor}) of the format could not be parsed to an integer.");
|
||||||
|
|
||||||
|
if (divisor == 0)
|
||||||
|
throw new DivideByZeroException($"Date tag format {format} includes division by zero, which is not allowed.");
|
||||||
|
|
||||||
|
var result = (dateValue - 1) / divisor + 1;
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return date.ToString(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a <see cref="DateTime"/> to a formatted string based on the specified format string.
|
||||||
|
/// <br />
|
||||||
|
/// - If the format contains the symbol “//”, the method divides the numeric value obtained from the left side of the format
|
||||||
|
/// by one minus the numeric value obtained from the right side of the format string and adds one. For instance:
|
||||||
|
/// <br />
|
||||||
|
/// - If the date is 02.03.2024 and the format is "MM//2", it extracts the month (02), subtracts one (3), divides it by 2,
|
||||||
|
/// rounds down the outgoing number (1), adds one to the number (resulting in 2).
|
||||||
|
/// <br />
|
||||||
|
/// - If the format does not contain "//", the method uses the default <see cref="DateTime.ToString"/> format.
|
||||||
|
/// <br />
|
||||||
|
/// This method provides a way to format the date based on typical or customized rules, including mathematical operations like division.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="date">The <see cref="DateOnly"/> value to be formatted. It will convert to DateTime to use the method shared with DateTime.</param>
|
||||||
|
/// <param name="format">The format string that dictates the formatting of the date. If the format includes the "//" symbol,
|
||||||
|
/// it splits the string at "//" and divides the left-side value by the right-side value. The format string can include standard
|
||||||
|
/// <see cref="DateTime.ToString"/> format patterns.</param>
|
||||||
|
/// <returns>A string representation of the formatted date, or the result of the division operation if "//" is present in the format.</returns>
|
||||||
|
/// <exception cref="ArgumentException">Thrown if the format string is invalid, such as having an incorrect number of parts after "//".</exception>
|
||||||
|
/// <exception cref="DivideByZeroException">Thrown if the right side of the "//" contains a zero, resulting in division by zero.</exception>
|
||||||
|
/// <exception cref="FormatException">Thrown if either the left-side or right-side value of "//" cannot be parsed as an integer.</exception>
|
||||||
|
public static string ToTag(this DateOnly date, string format) => date.ToDateTime(new()).ToTag(format);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,53 +1,31 @@
|
|||||||
using DigitalData.Core.Abstractions.Security;
|
using DigitalData.Core.Abstractions.Security;
|
||||||
|
using System.Reflection;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace DigitalData.Core.Security.Cryptographer
|
namespace DigitalData.Core.Security.Cryptographer
|
||||||
{
|
{
|
||||||
|
//TODO: Abstract RSA for future updates (using ECC, El Gamal or Lattice-based Cryptography)
|
||||||
public class RSACryptographer : IRSACryptographer
|
public class RSACryptographer : IRSACryptographer
|
||||||
{
|
{
|
||||||
protected string? _pem;
|
public virtual string Pem { get; init; }
|
||||||
|
|
||||||
public string Pem
|
|
||||||
{
|
|
||||||
get => _pem
|
|
||||||
?? throw new InvalidOperationException($"Pem is not initialized. Please ensure that the PEM is set or properly loaded from the file. Issuer: {Issuer}, Audience: {Audience}.");
|
|
||||||
init => _pem = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string? PemPath => FileName is null ? null : Path.Combine(Directory ?? string.Empty, FileName);
|
|
||||||
|
|
||||||
public string? Directory { get; set; }
|
|
||||||
|
|
||||||
public string? FileName { get; set; }
|
|
||||||
|
|
||||||
public RSAEncryptionPadding Padding { get; init; } = RSAEncryptionPadding.OaepSHA256;
|
public RSAEncryptionPadding Padding { get; init; } = RSAEncryptionPadding.OaepSHA256;
|
||||||
|
|
||||||
|
// TODO: add as json converter to IConfigurIConfiguration.Config
|
||||||
|
public string PaddingName
|
||||||
|
{
|
||||||
|
get => Padding.ToString();
|
||||||
|
init => Padding = typeof(RSAEncryptionPadding).GetProperty(value, BindingFlags.Public | BindingFlags.Static)?.GetValue(null) as RSAEncryptionPadding ?? throw new ArgumentException($"Padding '{value}' not found.");
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual RSA RSA { get; } = RSA.Create();
|
protected virtual RSA RSA { get; } = RSA.Create();
|
||||||
|
|
||||||
public string Issuer { get; init; } = string.Empty;
|
public string Issuer { get; init; } = string.Empty;
|
||||||
|
|
||||||
public string Audience { get; init; } = string.Empty;
|
public string Audience { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||||
internal RSACryptographer() { }
|
internal RSACryptographer() { }
|
||||||
|
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||||
public virtual void UnableToInitPemEvent() => throw new InvalidOperationException(
|
|
||||||
$"Pem is not initialized and pem file is null. Issuer is {Issuer} and audience {Audience}.");
|
|
||||||
|
|
||||||
public virtual void FileNotFoundEvent() => throw new FileNotFoundException(
|
|
||||||
$"Pem is not initialized and pem file is not found in {PemPath}. Issuer is {Issuer} and audience {Audience}.");
|
|
||||||
|
|
||||||
// TODO: make file read asynchronous, consider multiple routing
|
|
||||||
public virtual void Init()
|
|
||||||
{
|
|
||||||
if(_pem is null)
|
|
||||||
{
|
|
||||||
if(PemPath is null)
|
|
||||||
UnableToInitPemEvent();
|
|
||||||
if (File.Exists(PemPath))
|
|
||||||
_pem = File.ReadAllText(PemPath);
|
|
||||||
else
|
|
||||||
FileNotFoundEvent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,28 @@
|
|||||||
using DigitalData.Core.Abstractions.Security;
|
using DigitalData.Core.Abstractions.Security;
|
||||||
using DigitalData.Core.Security.Config;
|
using DigitalData.Core.Security.Config;
|
||||||
using DigitalData.Core.Security.Extensions;
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace DigitalData.Core.Security.Cryptographer
|
namespace DigitalData.Core.Security.Cryptographer
|
||||||
{
|
{
|
||||||
public class RSADecryptor : RSACryptographer, IRSADecryptor, IRSACryptographer
|
public class RSADecryptor : RSACryptographer, IRSADecryptor, IRSACryptographer
|
||||||
{
|
{
|
||||||
public bool Encrypt { get; init; }
|
private string? _pem;
|
||||||
|
|
||||||
|
public override string Pem
|
||||||
|
{
|
||||||
|
#pragma warning disable CS8603 // Possible null reference return.
|
||||||
|
get => _pem;
|
||||||
|
#pragma warning restore CS8603 // Possible null reference return.
|
||||||
|
init
|
||||||
|
{
|
||||||
|
_pem = value;
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsPemNull => _pem is null;
|
||||||
|
|
||||||
|
public bool IsEncrypted { get; init; }
|
||||||
|
|
||||||
private readonly Lazy<IRSAEncryptor> _lazyEncryptor;
|
private readonly Lazy<IRSAEncryptor> _lazyEncryptor;
|
||||||
|
|
||||||
@@ -26,30 +41,23 @@ namespace DigitalData.Core.Security.Cryptographer
|
|||||||
|
|
||||||
public string Decrypt(string data) => RSA.Decrypt(data.Base64ToByte(), Padding).BytesToString();
|
public string Decrypt(string data) => RSA.Decrypt(data.Base64ToByte(), Padding).BytesToString();
|
||||||
|
|
||||||
public override void Init()
|
internal void SetPem(string pem)
|
||||||
{
|
{
|
||||||
base.Init();
|
_pem = pem;
|
||||||
if (Encrypt)
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Init()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_pem))
|
||||||
|
throw PemIsNullException;
|
||||||
|
|
||||||
|
if (IsEncrypted)
|
||||||
RSA.ImportFromEncryptedPem(Pem, Secrets.PBE_PASSWORD.AsSpan());
|
RSA.ImportFromEncryptedPem(Pem, Secrets.PBE_PASSWORD.AsSpan());
|
||||||
else
|
else
|
||||||
RSA.ImportFromPem(Pem);
|
RSA.ImportFromPem(Pem);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void FileNotFoundEvent()
|
private InvalidOperationException PemIsNullException => new($"Pem is null or empty. Issuer: {Issuer}, Audience: {Audience}.");
|
||||||
{
|
|
||||||
var new_decryptor = new RSADecryptor()
|
|
||||||
{
|
|
||||||
Pem = RSAFactory<RSAFactoryParams>.Static.CreateRSAPrivateKeyPem(),
|
|
||||||
Encrypt = Encrypt
|
|
||||||
};
|
|
||||||
|
|
||||||
_pem = new_decryptor.Pem;
|
|
||||||
|
|
||||||
if (PemPath is not null)
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await File.WriteAllTextAsync(_pem, PemPath);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,37 +1,24 @@
|
|||||||
using DigitalData.Core.Abstractions.Security;
|
using DigitalData.Core.Abstractions.Security;
|
||||||
using DigitalData.Core.Security.Config;
|
using DigitalData.Core.Security.Config;
|
||||||
using DigitalData.Core.Security.Extensions;
|
|
||||||
|
|
||||||
namespace DigitalData.Core.Security.Cryptographer
|
namespace DigitalData.Core.Security.Cryptographer
|
||||||
{
|
{
|
||||||
public class RSAEncryptor : RSACryptographer, IRSAEncryptor, IRSACryptographer
|
public class RSAEncryptor : RSACryptographer, IRSAEncryptor, IRSACryptographer
|
||||||
{
|
{
|
||||||
|
public override string Pem
|
||||||
|
{
|
||||||
|
get => base.Pem;
|
||||||
|
init
|
||||||
|
{
|
||||||
|
base.Pem = value;
|
||||||
|
RSA.ImportFromPem(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] Encrypt(byte[] data) => RSA.Encrypt(data, Padding);
|
public byte[] Encrypt(byte[] data) => RSA.Encrypt(data, Padding);
|
||||||
|
|
||||||
public string Encrypt(string data) => RSA.Encrypt(data.Base64ToByte(), Padding).BytesToString();
|
public string Encrypt(string data) => RSA.Encrypt(data.ToBytes(), Padding).ToBase64String();
|
||||||
|
|
||||||
public bool Verify(string data, string signature) => Encrypt(data) == signature;
|
public bool Verify(string data, string signature) => Encrypt(data) == signature;
|
||||||
|
|
||||||
public override void Init()
|
|
||||||
{
|
|
||||||
base.Init();
|
|
||||||
RSA.ImportFromPem(base.Pem);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void FileNotFoundEvent()
|
|
||||||
{
|
|
||||||
var new_decryptor = new RSADecryptor()
|
|
||||||
{
|
|
||||||
Pem = RSAFactory<RSAFactoryParams>.Static.CreateRSAPrivateKeyPem()
|
|
||||||
};
|
|
||||||
|
|
||||||
_pem = new_decryptor.Encryptor.Pem;
|
|
||||||
|
|
||||||
if (PemPath is not null)
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await File.WriteAllTextAsync(_pem, PemPath);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,30 +7,31 @@ namespace DigitalData.Core.Security.Cryptographer
|
|||||||
{
|
{
|
||||||
public class RSAFactory<TRSAFactoryParams> : IRSAFactory<TRSAFactoryParams> where TRSAFactoryParams : RSAFactoryParams
|
public class RSAFactory<TRSAFactoryParams> : IRSAFactory<TRSAFactoryParams> where TRSAFactoryParams : RSAFactoryParams
|
||||||
{
|
{
|
||||||
private static readonly Lazy<RSAFactory<RSAFactoryParams>> LazyInstance = new(() => new(Options.Create<RSAFactoryParams>(new())));
|
|
||||||
|
|
||||||
public static RSAFactory<RSAFactoryParams> Static => LazyInstance.Value;
|
|
||||||
|
|
||||||
protected readonly TRSAFactoryParams _params;
|
protected readonly TRSAFactoryParams _params;
|
||||||
|
|
||||||
public RSAFactory(IOptions<TRSAFactoryParams> options) => _params = options.Value;
|
public RSAFactory(IOptions<TRSAFactoryParams> options)
|
||||||
|
{
|
||||||
|
options.Value.Init();
|
||||||
|
_params = options.Value;
|
||||||
|
}
|
||||||
|
|
||||||
public string CreateRSAPrivateKeyPem(int? keySizeInBits = null)
|
public string CreatePrivateKeyPem(int? keySizeInBits = null, bool encrypt = false) => encrypt
|
||||||
=> RSA.Create(keySizeInBits ?? _params.KeySizeInBits).ExportRSAPrivateKeyPem();
|
? CreateEncryptedPrivateKeyPem(keySizeInBits: keySizeInBits)
|
||||||
|
: RSA.Create(keySizeInBits ?? _params.KeySizeInBits).ExportRSAPrivateKeyPem();
|
||||||
|
|
||||||
public string CreateEncryptedPrivateKeyPem(
|
public string CreateEncryptedPrivateKeyPem(
|
||||||
int? keySizeInBits = null,
|
|
||||||
string? password = null,
|
|
||||||
PbeEncryptionAlgorithm? pbeEncryptionAlgorithm = null,
|
PbeEncryptionAlgorithm? pbeEncryptionAlgorithm = null,
|
||||||
HashAlgorithmName? hashAlgorithmName = null,
|
HashAlgorithmName? hashAlgorithmName = null,
|
||||||
int? iterationCount = null)
|
int? iterationCount = null,
|
||||||
|
int? keySizeInBits = null,
|
||||||
|
string? password = null)
|
||||||
{
|
{
|
||||||
password ??= _params.PbePassword;
|
password ??= _params.PbePassword;
|
||||||
|
|
||||||
var pbeParameters = pbeEncryptionAlgorithm is null && hashAlgorithmName is null && iterationCount is null
|
var pbeParameters = pbeEncryptionAlgorithm is null && hashAlgorithmName is null && iterationCount is null
|
||||||
? new PbeParameters(
|
? new PbeParameters(
|
||||||
pbeEncryptionAlgorithm ?? _params.PbeEncryptionAlgorithm,
|
pbeEncryptionAlgorithm ?? _params.PbeEncryptionAlgorithm,
|
||||||
hashAlgorithmName ?? _params.PbeHashAlgorithmName,
|
hashAlgorithmName ?? _params.PbeHashAlgorithm,
|
||||||
iterationCount ?? _params.PbeIterationCount)
|
iterationCount ?? _params.PbeIterationCount)
|
||||||
: _params.PbeParameters;
|
: _params.PbeParameters;
|
||||||
|
|
||||||
@@ -40,5 +41,28 @@ namespace DigitalData.Core.Security.Cryptographer
|
|||||||
|
|
||||||
return new string(pemChars);
|
return new string(pemChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string CreateEncryptedPrivateKeyPem(
|
||||||
|
PbeParameters pbeParameters,
|
||||||
|
int? keySizeInBits = null,
|
||||||
|
string? password = null)
|
||||||
|
{
|
||||||
|
password ??= _params.PbePassword;
|
||||||
|
|
||||||
|
var encryptedPrivateKey = RSA.Create(keySizeInBits ?? _params.KeySizeInBits).ExportEncryptedPkcs8PrivateKey(password.AsSpan(), pbeParameters);
|
||||||
|
|
||||||
|
var pemChars = PemEncoding.Write(_params.EncryptedPrivateKeyPemLabel, encryptedPrivateKey);
|
||||||
|
|
||||||
|
return new string(pemChars);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IRSADecryptor CreateDecryptor(string pem, string? issuer = null, string? audience = null, bool encrypt = false, RSAEncryptionPadding? padding = null) => new RSADecryptor()
|
||||||
|
{
|
||||||
|
Pem = pem,
|
||||||
|
Issuer = issuer ?? string.Empty,
|
||||||
|
Audience = audience ?? string.Empty,
|
||||||
|
IsEncrypted = encrypt,
|
||||||
|
Padding = padding ?? RSAEncryptionPadding.OaepSHA256
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,53 +3,105 @@ using DigitalData.Core.Security.Config;
|
|||||||
using DigitalData.Core.Security.Cryptographer;
|
using DigitalData.Core.Security.Cryptographer;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace DigitalData.Core.Security
|
namespace DigitalData.Core.Security
|
||||||
{
|
{
|
||||||
public static class DIExtensions
|
public static class DIExtensions
|
||||||
{
|
{
|
||||||
public static JsonSerializerOptions AddCryptographerConverter(this JsonSerializerOptions options)
|
private static IServiceCollection AddParamsConfigureOptions<TParams>(this IServiceCollection services) where TParams : RSAFactoryParams
|
||||||
{
|
=> services.AddSingleton<IConfigureOptions<TParams>, ParamsConfigureOptions<TParams>>();
|
||||||
if (!options.Converters.OfType<HashAlgorithmNameConverter>().Any())
|
|
||||||
options.Converters.Add(new HashAlgorithmNameConverter());
|
|
||||||
|
|
||||||
if (!options.Converters.OfType<JsonStringEnumConverter>().Any())
|
private static IServiceCollection AddAsymCryptService<TAsymCryptParams>(this IServiceCollection services, bool setAsDefault = false) where TAsymCryptParams : AsymCryptParams
|
||||||
options.Converters.Add(new JsonStringEnumConverter());
|
=> setAsDefault
|
||||||
return options;
|
? services.AddParamsConfigureOptions<TAsymCryptParams>().AddSingleton<IAsymCryptService, AsymCryptService<TAsymCryptParams>>()
|
||||||
}
|
: services.AddParamsConfigureOptions<TAsymCryptParams>().AddSingleton<IAsymCryptService<TAsymCryptParams>, AsymCryptService<TAsymCryptParams>>();
|
||||||
|
|
||||||
private static IServiceCollection AddAsymCryptService<TAsymCryptParams>(this IServiceCollection services)
|
/// <summary>
|
||||||
where TAsymCryptParams : AsymCryptParams
|
/// Registers a custom asym crypt service with specified parameters from the given configuration section.
|
||||||
{
|
/// </summary>
|
||||||
services.TryAddScoped<IAsymCryptService<TAsymCryptParams>, AsymCryptService<TAsymCryptParams>>();
|
/// <typeparam name="TAsymCryptParams"></typeparam>
|
||||||
return services;
|
/// <param name="services"></param>
|
||||||
}
|
/// <param name="section"></param>
|
||||||
|
/// <param name="setAsDefault">If true, the factory is registered as the default <see cref="IRSAFactory"/>. Otherwise, it is registered as <see cref="IRSAFactory{TRSAFactoryParams}"/>.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IServiceCollection AddAsymCryptService<TAsymCryptParams>(this IServiceCollection services, IConfigurationSection section, bool setAsDefault = false) where TAsymCryptParams : AsymCryptParams => services
|
||||||
|
.Configure<TAsymCryptParams>(section)
|
||||||
|
.AddAsymCryptService<TAsymCryptParams>(setAsDefault: setAsDefault);
|
||||||
|
|
||||||
public static IServiceCollection AddAsymCryptService<TAsymCryptParams>(this IServiceCollection services, IConfigurationSection section)
|
/// <summary>
|
||||||
where TAsymCryptParams : AsymCryptParams
|
/// Registers a custom asym crypt service with default parameters from the given configuration section.
|
||||||
=> services.Configure<TAsymCryptParams>(section).AddAsymCryptService<TAsymCryptParams>();
|
/// </summary>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <param name="section"></param>
|
||||||
|
/// <param name="setAsDefault"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IServiceCollection AddAsymCryptService(this IServiceCollection services, IConfigurationSection section, bool setAsDefault = false)
|
||||||
|
=> services.Configure<AsymCryptParams>(section).AddAsymCryptService<AsymCryptParams>(setAsDefault: setAsDefault);
|
||||||
|
|
||||||
public static IServiceCollection AddAsymCryptService<TAsymCryptParams>(this IServiceCollection services, TAsymCryptParams param)
|
/// <summary>
|
||||||
where TAsymCryptParams : AsymCryptParams
|
/// Registers an asym crypt service with the specified parameters from the given instance. Optionally, sets it as the default factory.
|
||||||
=> services.AddSingleton(Options.Create(param)).AddAsymCryptService<TAsymCryptParams>();
|
/// </summary>
|
||||||
|
/// <typeparam name="TAsymCryptParams"></typeparam>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <param name="param"></param>
|
||||||
|
/// <param name="setAsDefault">If true, the factory is registered as the default <see cref="IRSAFactory"/>. Otherwise, it is registered as <see cref="IRSAFactory{TRSAFactoryParams}"/>.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IServiceCollection AddAsymCryptService<TAsymCryptParams>(this IServiceCollection services, TAsymCryptParams param, bool setAsDefault = false) where TAsymCryptParams : AsymCryptParams => services
|
||||||
|
.AddSingleton(Options.Create(param))
|
||||||
|
.AddAsymCryptService<TAsymCryptParams>(setAsDefault: setAsDefault);
|
||||||
|
|
||||||
private static IServiceCollection AddRSAFactory<TRSAFactoryParams>(this IServiceCollection services)
|
/// <summary>
|
||||||
|
/// Registers default asym crypt service with the specified parameters from the given instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <param name="param"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IServiceCollection AddAsymCryptService(this IServiceCollection services, AsymCryptParams param) => services
|
||||||
|
.AddAsymCryptService(param: param, setAsDefault: true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers default RSA Factory instance with default params
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <param name="factoryParams"></param>
|
||||||
|
/// <returns>The updated <see cref="IServiceCollection"/> with the RSA Factory registered.</returns>
|
||||||
|
public static IServiceCollection AddRSAFactory(this IServiceCollection services, RSAFactoryParams? factoryParams = null) => services
|
||||||
|
.AddParamsConfigureOptions<RSAFactoryParams>()
|
||||||
|
.AddScoped<IRSAFactory>(_ => new RSAFactory<RSAFactoryParams>(Options.Create(factoryParams ?? new())));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a custom RSA Factory with specified parameters from the given configuration section.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TRSAFactoryParams"></typeparam>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <param name="section"></param>
|
||||||
|
/// <param name="setAsDefault">If true, the factory is registered as the default <see cref="IRSAFactory"/>. Otherwise, it is registered as <see cref="IRSAFactory{TRSAFactoryParams}"/>.</param>
|
||||||
|
/// <returns>The updated <see cref="IServiceCollection"/> with the RSA Factory registered.</returns>
|
||||||
|
public static IServiceCollection AddRSAFactory<TRSAFactoryParams>(this IServiceCollection services, IConfigurationSection section, bool setAsDefault = false)
|
||||||
where TRSAFactoryParams : RSAFactoryParams
|
where TRSAFactoryParams : RSAFactoryParams
|
||||||
{
|
{
|
||||||
services.TryAddScoped<IRSAFactory<TRSAFactoryParams>, RSAFactory<TRSAFactoryParams>>();
|
services.AddParamsConfigureOptions<TRSAFactoryParams>().Configure<TRSAFactoryParams>(section);
|
||||||
return services;
|
return setAsDefault
|
||||||
|
? services.AddSingleton<IRSAFactory, RSAFactory<TRSAFactoryParams>>()
|
||||||
|
: services.AddSingleton<IRSAFactory<TRSAFactoryParams>, RSAFactory<TRSAFactoryParams>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddRSAFactory<TRSAFactoryParams>(this IServiceCollection services, IConfigurationSection section)
|
/// <summary>
|
||||||
|
/// Registers an RSA Factory with the specified parameters from the given instance. Optionally, sets it as the default factory.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TRSAFactoryParams">The type of the RSA factory parameters.</typeparam>
|
||||||
|
/// <param name="services"></param>
|
||||||
|
/// <param name="rsaParams"></param>
|
||||||
|
/// <param name="setAsDefault">If true, the factory is registered as the default <see cref="IRSAFactory"/>. Otherwise, it is registered as <see cref="IRSAFactory{TRSAFactoryParams}"/>.</param>
|
||||||
|
/// <returns>The updated <see cref="IServiceCollection"/> with the RSA Factory registered.</returns>
|
||||||
|
public static IServiceCollection AddRSAFactory<TRSAFactoryParams>(this IServiceCollection services, TRSAFactoryParams rsaParams, bool setAsDefault = false)
|
||||||
where TRSAFactoryParams : RSAFactoryParams
|
where TRSAFactoryParams : RSAFactoryParams
|
||||||
=> services.Configure<TRSAFactoryParams>(section).AddRSAFactory<TRSAFactoryParams>();
|
{
|
||||||
|
services.AddSingleton(Options.Create(rsaParams));
|
||||||
public static IServiceCollection AddRSAFactory<TRSAFactoryParams>(this IServiceCollection services, TRSAFactoryParams param)
|
return setAsDefault
|
||||||
where TRSAFactoryParams : RSAFactoryParams
|
? services.AddParamsConfigureOptions<TRSAFactoryParams>().AddSingleton<IRSAFactory, RSAFactory<TRSAFactoryParams>>()
|
||||||
=> services.AddSingleton(Options.Create(param)).AddRSAFactory<TRSAFactoryParams>();
|
: services.AddParamsConfigureOptions<TRSAFactoryParams>().AddSingleton<IRSAFactory<TRSAFactoryParams>, RSAFactory<TRSAFactoryParams>>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\DigitalData.Core.Abstractions\DigitalData.Core.Abstractions.csproj" />
|
<ProjectReference Include="..\DigitalData.Core.Abstractions\DigitalData.Core.Abstractions.csproj" />
|
||||||
<ProjectReference Include="..\DigitalData.Core.Security.Extensions\DigitalData.Core.Security.Extensions.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace DigitalData.Core.Security
|
|
||||||
{
|
|
||||||
public class HashAlgorithmNameConverter : JsonConverter<HashAlgorithmName>
|
|
||||||
{
|
|
||||||
public override HashAlgorithmName Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => new(reader.GetString() ?? string.Empty);
|
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, HashAlgorithmName value, JsonSerializerOptions options) => writer.WriteStringValue(value.Name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
14
DigitalData.Core.Security/Instance.cs
Normal file
14
DigitalData.Core.Security/Instance.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using DigitalData.Core.Abstractions.Security;
|
||||||
|
using DigitalData.Core.Security.Config;
|
||||||
|
using DigitalData.Core.Security.Cryptographer;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace DigitalData.Core.Security
|
||||||
|
{
|
||||||
|
public static class Instance
|
||||||
|
{
|
||||||
|
private static readonly Lazy<RSAFactory<RSAFactoryParams>> LazyInstance = new(() => new(Options.Create<RSAFactoryParams>(new())));
|
||||||
|
|
||||||
|
public static IRSAFactory RSAFactory => LazyInstance.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,9 +23,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.Core.Legacy.Cli
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.Security", "DigitalData.Core.Security\DigitalData.Core.Security.csproj", "{47D80C65-74A2-4EB8-96A5-D571A9108FB3}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.Security", "DigitalData.Core.Security\DigitalData.Core.Security.csproj", "{47D80C65-74A2-4EB8-96A5-D571A9108FB3}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.Security.Extensions", "DigitalData.Core.Security.Extensions\DigitalData.Core.Security.Extensions.csproj", "{D740182D-82DA-480A-9F87-BFB4A8620A00}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.Terminal", "DigitalData.Core.Terminal\DigitalData.Core.Terminal.csproj", "{0FA93730-8084-4907-B172-87D610323796}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.Core.Terminal", "DigitalData.Core.Terminal\DigitalData.Core.Terminal.csproj", "{0FA93730-8084-4907-B172-87D610323796}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.Core.Tests.API", "DigitalData.Core.Tests.API\DigitalData.Core.Tests.API.csproj", "{9BC2DEC5-E89D-48CC-9A51-4D94496EE4A6}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@@ -72,14 +72,14 @@ Global
|
|||||||
{47D80C65-74A2-4EB8-96A5-D571A9108FB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{47D80C65-74A2-4EB8-96A5-D571A9108FB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{47D80C65-74A2-4EB8-96A5-D571A9108FB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{47D80C65-74A2-4EB8-96A5-D571A9108FB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{47D80C65-74A2-4EB8-96A5-D571A9108FB3}.Release|Any CPU.Build.0 = Release|Any CPU
|
{47D80C65-74A2-4EB8-96A5-D571A9108FB3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{D740182D-82DA-480A-9F87-BFB4A8620A00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{D740182D-82DA-480A-9F87-BFB4A8620A00}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{D740182D-82DA-480A-9F87-BFB4A8620A00}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{D740182D-82DA-480A-9F87-BFB4A8620A00}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{0FA93730-8084-4907-B172-87D610323796}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{0FA93730-8084-4907-B172-87D610323796}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{0FA93730-8084-4907-B172-87D610323796}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{0FA93730-8084-4907-B172-87D610323796}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{0FA93730-8084-4907-B172-87D610323796}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{0FA93730-8084-4907-B172-87D610323796}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{0FA93730-8084-4907-B172-87D610323796}.Release|Any CPU.Build.0 = Release|Any CPU
|
{0FA93730-8084-4907-B172-87D610323796}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9BC2DEC5-E89D-48CC-9A51-4D94496EE4A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9BC2DEC5-E89D-48CC-9A51-4D94496EE4A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9BC2DEC5-E89D-48CC-9A51-4D94496EE4A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9BC2DEC5-E89D-48CC-9A51-4D94496EE4A6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
Reference in New Issue
Block a user