5-improve-encrypted-storage (#6)
Added the use of DEK's for encryption of secrets. Both the KEK's and DEK's are stored in a way that you can have multiple key of which one is active. But the others are still available for decrypting. This allows for implementing key rotation. Co-authored-by: eelke <eelke@eelkeklein.nl> Co-authored-by: Eelke76 <31384324+Eelke76@users.noreply.github.com> Reviewed-on: #6
This commit is contained in:
parent
138f335af0
commit
07393f57fc
87 changed files with 1903 additions and 533 deletions
73
IdentityShroud.Api/Apis/ClientApi.cs
Normal file
73
IdentityShroud.Api/Apis/ClientApi.cs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
using FluentResults;
|
||||
using IdentityShroud.Api.Mappers;
|
||||
using IdentityShroud.Core.Contracts;
|
||||
using IdentityShroud.Core.Model;
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace IdentityShroud.Api;
|
||||
|
||||
|
||||
|
||||
public record ClientCreateReponse(int Id, string ClientId);
|
||||
|
||||
/// <summary>
|
||||
/// The part of the api below realms/{slug}/clients
|
||||
/// </summary>
|
||||
public static class ClientApi
|
||||
{
|
||||
public const string ClientGetRouteName = "ClientGet";
|
||||
|
||||
public static void MapEndpoints(this IEndpointRouteBuilder erp)
|
||||
{
|
||||
RouteGroupBuilder clientsGroup = erp.MapGroup("clients");
|
||||
|
||||
clientsGroup.MapPost("", ClientCreate)
|
||||
.Validate<ClientCreateRequest>()
|
||||
.WithName("ClientCreate")
|
||||
.Produces(StatusCodes.Status201Created);
|
||||
|
||||
var clientIdGroup = clientsGroup.MapGroup("{clientId}")
|
||||
.AddEndpointFilter<ClientIdValidationFilter>();
|
||||
|
||||
clientIdGroup.MapGet("", ClientGet)
|
||||
.WithName(ClientGetRouteName);
|
||||
}
|
||||
|
||||
private static Ok<ClientRepresentation> ClientGet(
|
||||
Guid realmId,
|
||||
int clientId,
|
||||
HttpContext context)
|
||||
{
|
||||
Client client = (Client)context.Items["ClientEntity"]!;
|
||||
return TypedResults.Ok(new ClientMapper().ToDto(client));
|
||||
}
|
||||
|
||||
private static async Task<Results<CreatedAtRoute<ClientCreateReponse>, InternalServerError>>
|
||||
ClientCreate(
|
||||
Guid realmId,
|
||||
ClientCreateRequest request,
|
||||
[FromServices] IClientService service,
|
||||
HttpContext context,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
Realm realm = context.GetValidatedRealm();
|
||||
Result<Client> result = await service.Create(realm.Id, request, cancellationToken);
|
||||
|
||||
if (result.IsFailed)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Client client = result.Value;
|
||||
|
||||
return TypedResults.CreatedAtRoute(
|
||||
new ClientCreateReponse(client.Id, client.ClientId),
|
||||
ClientGetRouteName,
|
||||
new RouteValueDictionary()
|
||||
{
|
||||
["realmId"] = realm.Id,
|
||||
["clientId"] = client.Id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace IdentityShroud.Core.Messages;
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc7517.html
|
||||
|
||||
|
||||
public class JsonWebKey
|
||||
{
|
||||
[JsonPropertyName("kty")]
|
||||
public string KeyType { get; set; } = "RSA";
|
||||
|
||||
// Common values sig(nature) enc(ryption)
|
||||
[JsonPropertyName("use")]
|
||||
public string? Use { get; set; } = "sig"; // "sig" for signature, "enc" for encryption
|
||||
|
||||
// Per standard this field is optional, commented out for now as it seems not
|
||||
// have any good use in an identity server. Anyone validating tokens should use
|
||||
// the algorithm specified in the header of the token.
|
||||
// [JsonPropertyName("alg")]
|
||||
// public string? Algorithm { get; set; } = "RS256";
|
||||
|
||||
[JsonPropertyName("kid")]
|
||||
public required string KeyId { get; set; }
|
||||
|
||||
// RSA Public Key Components
|
||||
[JsonPropertyName("n")]
|
||||
public required string Modulus { get; set; }
|
||||
|
||||
[JsonPropertyName("e")]
|
||||
public required string Exponent { get; set; }
|
||||
|
||||
// Optional fields
|
||||
[JsonPropertyName("x5c")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public List<string>? X509CertificateChain { get; set; }
|
||||
|
||||
[JsonPropertyName("x5t")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? X509CertificateThumbprint { get; set; }
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace IdentityShroud.Core.Messages;
|
||||
|
||||
public class JsonWebKeySet
|
||||
{
|
||||
[JsonPropertyName("keys")]
|
||||
public List<JsonWebKey> Keys { get; set; } = new List<JsonWebKey>();
|
||||
}
|
||||
16
IdentityShroud.Api/Apis/Dto/ClientRepresentation.cs
Normal file
16
IdentityShroud.Api/Apis/Dto/ClientRepresentation.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
namespace IdentityShroud.Api;
|
||||
|
||||
public record ClientRepresentation
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public Guid RealmId { get; set; }
|
||||
public required string ClientId { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
|
||||
public string? SignatureAlgorithm { get; set; }
|
||||
|
||||
public bool AllowClientCredentialsFlow { get; set; } = false;
|
||||
|
||||
public required DateTime CreatedAt { get; set; }
|
||||
}
|
||||
15
IdentityShroud.Api/Apis/EndpointRouteBuilderExtensions.cs
Normal file
15
IdentityShroud.Api/Apis/EndpointRouteBuilderExtensions.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
namespace IdentityShroud.Api;
|
||||
|
||||
public static class EndpointRouteBuilderExtensions
|
||||
{
|
||||
public static RouteHandlerBuilder Validate<TDto>(this RouteHandlerBuilder builder) where TDto : class
|
||||
=> builder.AddEndpointFilter<ValidateFilter<TDto>>();
|
||||
|
||||
public static void MapApis(this IEndpointRouteBuilder erp)
|
||||
{
|
||||
RealmApi.MapRealmEndpoints(erp);
|
||||
|
||||
OpenIdEndpoints.MapEndpoints(erp);
|
||||
}
|
||||
|
||||
}
|
||||
21
IdentityShroud.Api/Apis/Filters/ClientIdValidationFilter.cs
Normal file
21
IdentityShroud.Api/Apis/Filters/ClientIdValidationFilter.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using IdentityShroud.Core.Contracts;
|
||||
using IdentityShroud.Core.Model;
|
||||
|
||||
namespace IdentityShroud.Api;
|
||||
|
||||
public class ClientIdValidationFilter(IClientService clientService) : IEndpointFilter
|
||||
{
|
||||
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
|
||||
{
|
||||
Guid realmId = context.Arguments.OfType<Guid>().First();
|
||||
int id = context.Arguments.OfType<int>().First();
|
||||
Client? client = await clientService.FindById(realmId, id, context.HttpContext.RequestAborted);
|
||||
if (client is null)
|
||||
{
|
||||
return Results.NotFound();
|
||||
}
|
||||
context.HttpContext.Items["ClientEntity"] = client;
|
||||
|
||||
return await next(context);
|
||||
}
|
||||
}
|
||||
20
IdentityShroud.Api/Apis/Filters/RealmIdValidationFilter.cs
Normal file
20
IdentityShroud.Api/Apis/Filters/RealmIdValidationFilter.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
using IdentityShroud.Core.Contracts;
|
||||
using IdentityShroud.Core.Model;
|
||||
|
||||
namespace IdentityShroud.Api;
|
||||
|
||||
public class RealmIdValidationFilter(IRealmService realmService) : IEndpointFilter
|
||||
{
|
||||
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
|
||||
{
|
||||
Guid id = context.Arguments.OfType<Guid>().First();
|
||||
Realm? realm = await realmService.FindById(id, context.HttpContext.RequestAborted);
|
||||
if (realm is null)
|
||||
{
|
||||
return Results.NotFound();
|
||||
}
|
||||
context.HttpContext.Items["RealmEntity"] = realm;
|
||||
|
||||
return await next(context);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
using IdentityShroud.Core.Contracts;
|
||||
using IdentityShroud.Core.Model;
|
||||
using IdentityShroud.Core.Services;
|
||||
|
||||
namespace IdentityShroud.Api;
|
||||
|
||||
|
|
@ -9,12 +9,13 @@ namespace IdentityShroud.Api;
|
|||
/// consistently.
|
||||
/// </summary>
|
||||
/// <param name="realmService"></param>
|
||||
public class SlugValidationFilter(IRealmService realmService) : IEndpointFilter
|
||||
public class RealmSlugValidationFilter(IRealmService realmService) : IEndpointFilter
|
||||
{
|
||||
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
|
||||
{
|
||||
string slug = context.Arguments.OfType<string>().First();
|
||||
Realm? realm = await realmService.FindBySlug(slug);
|
||||
string realmSlug = context.Arguments.OfType<string>().FirstOrDefault()
|
||||
?? throw new InvalidOperationException("Expected argument missing, ensure you include path parameters in your handlers signature even when you don't use them");
|
||||
Realm? realm = await realmService.FindBySlug(realmSlug, context.HttpContext.RequestAborted);
|
||||
if (realm is null)
|
||||
{
|
||||
return Results.NotFound();
|
||||
11
IdentityShroud.Api/Apis/Mappers/ClientMapper.cs
Normal file
11
IdentityShroud.Api/Apis/Mappers/ClientMapper.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using IdentityShroud.Core.Model;
|
||||
using Riok.Mapperly.Abstractions;
|
||||
|
||||
namespace IdentityShroud.Api.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public partial class ClientMapper
|
||||
{
|
||||
[MapperIgnoreSource(nameof(Client.Secrets))]
|
||||
public partial ClientRepresentation ToDto(Client client);
|
||||
}
|
||||
|
|
@ -1,34 +1,22 @@
|
|||
using System.Security.Cryptography;
|
||||
using IdentityShroud.Core.Contracts;
|
||||
using IdentityShroud.Core.Messages;
|
||||
using IdentityShroud.Core.Model;
|
||||
using IdentityShroud.Core.Security;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
|
||||
namespace IdentityShroud.Api.Mappers;
|
||||
|
||||
public class KeyMapper(IEncryptionService encryptionService)
|
||||
public class KeyMapper(IKeyService keyService)
|
||||
{
|
||||
public JsonWebKey KeyToJsonWebKey(Key key)
|
||||
public JsonWebKeySet KeyListToJsonWebKeySet(IEnumerable<RealmKey> keys)
|
||||
{
|
||||
using var rsa = RsaHelper.LoadFromPkcs8(key.GetPrivateKey(encryptionService));
|
||||
RSAParameters parameters = rsa.ExportParameters(includePrivateParameters: false);
|
||||
|
||||
return new JsonWebKey()
|
||||
JsonWebKeySet wks = new();
|
||||
foreach (var k in keys)
|
||||
{
|
||||
KeyType = rsa.SignatureAlgorithm,
|
||||
KeyId = key.Id.ToString(),
|
||||
Use = "sig",
|
||||
Exponent = WebEncoders.Base64UrlEncode(parameters.Exponent!),
|
||||
Modulus = WebEncoders.Base64UrlEncode(parameters.Modulus!),
|
||||
};
|
||||
}
|
||||
|
||||
public JsonWebKeySet KeyListToJsonWebKeySet(IEnumerable<Key> keys)
|
||||
{
|
||||
return new JsonWebKeySet()
|
||||
{
|
||||
Keys = keys.Select(e => KeyToJsonWebKey(e)).ToList(),
|
||||
};
|
||||
var wk = keyService.CreateJsonWebKey(k);
|
||||
if (wk is {})
|
||||
{
|
||||
wks.Keys.Add(wk);
|
||||
}
|
||||
}
|
||||
return wks;
|
||||
}
|
||||
}
|
||||
72
IdentityShroud.Api/Apis/OpenIdEndpoints.cs
Normal file
72
IdentityShroud.Api/Apis/OpenIdEndpoints.cs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
using IdentityShroud.Api.Mappers;
|
||||
using IdentityShroud.Core.Contracts;
|
||||
using IdentityShroud.Core.Messages;
|
||||
using IdentityShroud.Core.Model;
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace IdentityShroud.Api;
|
||||
|
||||
public static class OpenIdEndpoints
|
||||
{
|
||||
// openid: auth/realms/{realmSlug}/.well-known/openid-configuration
|
||||
// openid: auth/realms/{realmSlug}/openid-connect/(auth|token|jwks)
|
||||
|
||||
|
||||
public static void MapEndpoints(this IEndpointRouteBuilder erp)
|
||||
{
|
||||
var realmsGroup = erp.MapGroup("/auth/realms");
|
||||
|
||||
var realmSlugGroup = realmsGroup.MapGroup("{realmSlug}")
|
||||
.AddEndpointFilter<RealmSlugValidationFilter>();
|
||||
realmSlugGroup.MapGet(".well-known/openid-configuration", GetOpenIdConfiguration);
|
||||
|
||||
var openidConnect = realmSlugGroup.MapGroup("openid-connect");
|
||||
openidConnect.MapPost("auth", OpenIdConnectAuth);
|
||||
openidConnect.MapPost("token", OpenIdConnectToken);
|
||||
openidConnect.MapGet("jwks", OpenIdConnectJwks);
|
||||
}
|
||||
|
||||
private static async Task<JsonHttpResult<OpenIdConfiguration>> GetOpenIdConfiguration(
|
||||
string realmSlug,
|
||||
[FromServices]IRealmService realmService,
|
||||
HttpContext context)
|
||||
{
|
||||
Realm realm = context.GetValidatedRealm();
|
||||
|
||||
var s = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}";
|
||||
var searchString = $"realms/{realmSlug}";
|
||||
int index = s.IndexOf(searchString, StringComparison.OrdinalIgnoreCase);
|
||||
string baseUri = s.Substring(0, index + searchString.Length);
|
||||
|
||||
return TypedResults.Json(new OpenIdConfiguration()
|
||||
{
|
||||
AuthorizationEndpoint = baseUri + "/openid-connect/auth",
|
||||
TokenEndpoint = baseUri + "/openid-connect/token",
|
||||
Issuer = baseUri,
|
||||
JwksUri = baseUri + "/openid-connect/jwks",
|
||||
}, AppJsonSerializerContext.Default.OpenIdConfiguration);
|
||||
}
|
||||
|
||||
private static async Task<Results<Ok<JsonWebKeySet>, BadRequest>> OpenIdConnectJwks(
|
||||
string realmSlug,
|
||||
[FromServices]IRealmService realmService,
|
||||
[FromServices]KeyMapper keyMapper,
|
||||
HttpContext context)
|
||||
{
|
||||
Realm realm = context.GetValidatedRealm();
|
||||
await realmService.LoadActiveKeys(realm);
|
||||
return TypedResults.Ok(keyMapper.KeyListToJsonWebKeySet(realm.Keys));
|
||||
}
|
||||
|
||||
private static Task OpenIdConnectToken(HttpContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static Task OpenIdConnectAuth(HttpContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,7 +1,4 @@
|
|||
using FluentResults;
|
||||
using IdentityShroud.Api.Mappers;
|
||||
using IdentityShroud.Api.Validation;
|
||||
using IdentityShroud.Core.Messages;
|
||||
using IdentityShroud.Core.Contracts;
|
||||
using IdentityShroud.Core.Messages.Realm;
|
||||
using IdentityShroud.Core.Model;
|
||||
using IdentityShroud.Core.Services;
|
||||
|
|
@ -15,26 +12,28 @@ public static class HttpContextExtensions
|
|||
public static Realm GetValidatedRealm(this HttpContext context) => (Realm)context.Items["RealmEntity"]!;
|
||||
}
|
||||
|
||||
// api: api/v1/realms/{realmId}/....
|
||||
// api: api/v1/realms/{realmId}/clients/{clientId}
|
||||
|
||||
|
||||
|
||||
public static class RealmApi
|
||||
{
|
||||
public static void MapRealmEndpoints(this IEndpointRouteBuilder app)
|
||||
public static void MapRealmEndpoints(IEndpointRouteBuilder erp)
|
||||
{
|
||||
var realmsGroup = app.MapGroup("/realms");
|
||||
var realmsGroup = erp.MapGroup("/api/v1/realms");
|
||||
realmsGroup.MapPost("", RealmCreate)
|
||||
.Validate<RealmCreateRequest>()
|
||||
.WithName("Create Realm")
|
||||
.Produces(StatusCodes.Status201Created);
|
||||
|
||||
var realmSlugGroup = realmsGroup.MapGroup("{slug}")
|
||||
.AddEndpointFilter<SlugValidationFilter>();
|
||||
realmSlugGroup.MapGet(".well-known/openid-configuration", GetOpenIdConfiguration);
|
||||
var realmIdGroup = realmsGroup.MapGroup("{realmId}")
|
||||
.AddEndpointFilter<RealmIdValidationFilter>();
|
||||
|
||||
ClientApi.MapEndpoints(realmIdGroup);
|
||||
|
||||
|
||||
|
||||
var openidConnect = realmSlugGroup.MapGroup("openid-connect");
|
||||
openidConnect.MapPost("auth", OpenIdConnectAuth);
|
||||
openidConnect.MapPost("token", OpenIdConnectToken);
|
||||
openidConnect.MapGet("jwks", OpenIdConnectJwks);
|
||||
}
|
||||
|
||||
private static async Task<Results<Created<RealmCreateResponse>, InternalServerError>>
|
||||
|
|
@ -47,46 +46,4 @@ public static class RealmApi
|
|||
// TODO make helper to convert failure response to a proper HTTP result.
|
||||
return TypedResults.InternalServerError();
|
||||
}
|
||||
|
||||
private static async Task<Results<Ok<JsonWebKeySet>, BadRequest>> OpenIdConnectJwks(
|
||||
string slug,
|
||||
[FromServices]IRealmService realmService,
|
||||
[FromServices]KeyMapper keyMapper,
|
||||
HttpContext context)
|
||||
{
|
||||
Realm realm = context.GetValidatedRealm();
|
||||
await realmService.LoadActiveKeys(realm);
|
||||
return TypedResults.Ok(keyMapper.KeyListToJsonWebKeySet(realm.Keys));
|
||||
}
|
||||
|
||||
private static Task OpenIdConnectToken(HttpContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static Task OpenIdConnectAuth(HttpContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static async Task<JsonHttpResult<OpenIdConfiguration>> GetOpenIdConfiguration(
|
||||
string slug,
|
||||
[FromServices]IRealmService realmService,
|
||||
HttpContext context)
|
||||
{
|
||||
Realm realm = context.GetValidatedRealm();
|
||||
|
||||
var s = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}";
|
||||
var searchString = $"realms/{slug}";
|
||||
int index = s.IndexOf(searchString, StringComparison.OrdinalIgnoreCase);
|
||||
string baseUri = s.Substring(0, index + searchString.Length);
|
||||
|
||||
return TypedResults.Json(new OpenIdConfiguration()
|
||||
{
|
||||
AuthorizationEndpoint = baseUri + "/openid-connect/auth",
|
||||
TokenEndpoint = baseUri + "/openid-connect/token",
|
||||
Issuer = baseUri,
|
||||
JwksUri = baseUri + "/openid-connect/jwks",
|
||||
}, AppJsonSerializerContext.Default.OpenIdConfiguration);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
using FluentValidation;
|
||||
using IdentityShroud.Core.Contracts;
|
||||
|
||||
namespace IdentityShroud.Api;
|
||||
|
||||
public class ClientCreateRequestValidator : AbstractValidator<ClientCreateRequest>
|
||||
{
|
||||
// most of standard ascii minus the control characters and space
|
||||
private const string ClientIdPattern = "^[\x21-\x7E]+";
|
||||
|
||||
private string[] AllowedAlgorithms = [ "RS256", "ES256" ];
|
||||
|
||||
public ClientCreateRequestValidator()
|
||||
{
|
||||
RuleFor(e => e.ClientId).NotEmpty().MaximumLength(40).Matches(ClientIdPattern);
|
||||
RuleFor(e => e.Name).MaximumLength(80);
|
||||
RuleFor(e => e.Description).MaximumLength(2048);
|
||||
RuleFor(e => e.SignatureAlgorithm)
|
||||
.Must(v => v is null || AllowedAlgorithms.Contains(v))
|
||||
.WithMessage($"SignatureAlgorithm must be one of {string.Join(", ", AllowedAlgorithms)} or null");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
using FluentValidation;
|
||||
using IdentityShroud.Core.Messages.Realm;
|
||||
|
||||
namespace IdentityShroud.Api.Validation;
|
||||
namespace IdentityShroud.Api;
|
||||
|
||||
public class RealmCreateRequestValidator : AbstractValidator<RealmCreateRequest>
|
||||
{
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
using FluentValidation;
|
||||
|
||||
namespace IdentityShroud.Api.Validation;
|
||||
namespace IdentityShroud.Api;
|
||||
|
||||
public class ValidateFilter<T> : IEndpointFilter where T : class
|
||||
{
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using IdentityShroud.Core.Messages;
|
||||
using IdentityShroud.Core.Messages.Realm;
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
|
||||
[JsonSerializable(typeof(OpenIdConfiguration))]
|
||||
[JsonSerializable(typeof(RealmCreateRequest))]
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="12.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.2" />
|
||||
<PackageReference Include="Riok.Mapperly" Version="4.3.1" />
|
||||
<PackageReference Include="Serilog" Version="4.3.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
|
||||
<PackageReference Include="Serilog.Expressions" Version="5.0.0" />
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=apis_005Cdto/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=apis_005Cfilters/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=apis_005Cfilters/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=apis_005Cvalidation/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=validation/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
using FluentValidation;
|
||||
using IdentityShroud.Api;
|
||||
using IdentityShroud.Api.Mappers;
|
||||
using IdentityShroud.Api.Validation;
|
||||
using IdentityShroud.Core;
|
||||
using IdentityShroud.Core.Contracts;
|
||||
using IdentityShroud.Core.Security;
|
||||
using IdentityShroud.Core.Security.Keys;
|
||||
using IdentityShroud.Core.Services;
|
||||
using Serilog;
|
||||
using Serilog.Formatting.Json;
|
||||
|
|
@ -36,13 +36,21 @@ void ConfigureBuilder(WebApplicationBuilder builder)
|
|||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||
services.AddOpenApi();
|
||||
services.AddScoped<Db>();
|
||||
services.AddScoped<IClientService, ClientService>();
|
||||
services.AddSingleton<IClock, ClockService>();
|
||||
services.AddSingleton<IDekEncryptionService, DekEncryptionService>();
|
||||
services.AddScoped<IDataEncryptionService, DataEncryptionService>();
|
||||
services.AddScoped<IRealmContext, RealmContext>();
|
||||
services.AddScoped<IKeyProviderFactory, KeyProviderFactory>();
|
||||
services.AddScoped<IKeyService, KeyService>();
|
||||
services.AddScoped<IRealmService, RealmService>();
|
||||
services.AddOptions<DbConfiguration>().Bind(configuration.GetSection("db"));
|
||||
services.AddSingleton<ISecretProvider, ConfigurationSecretProvider>();
|
||||
services.AddSingleton<KeyMapper>();
|
||||
services.AddSingleton<IEncryptionService, EncryptionService>();
|
||||
services.AddScoped<KeyMapper>();
|
||||
services.AddScoped<IRealmContext, RealmContext>();
|
||||
|
||||
services.AddValidatorsFromAssemblyContaining<RealmCreateRequestValidator>();
|
||||
services.AddValidatorsFromAssemblyContaining<RealmCreateRequestValidator>();
|
||||
services.AddHttpContextAccessor();
|
||||
|
||||
builder.Host.UseSerilog((context, services, configuration) => configuration
|
||||
.Enrich.FromLogContext()
|
||||
|
|
@ -57,7 +65,8 @@ void ConfigureApplication(WebApplication app)
|
|||
app.MapOpenApi();
|
||||
}
|
||||
app.UseSerilogRequestLogging();
|
||||
app.MapRealmEndpoints();
|
||||
app.MapApis();
|
||||
|
||||
// app.UseRouting();
|
||||
// app.MapControllers();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
namespace IdentityShroud.Api.Validation;
|
||||
|
||||
public static class EndpointRouteBuilderExtensions
|
||||
{
|
||||
public static RouteHandlerBuilder Validate<TDto>(this RouteHandlerBuilder builder) where TDto : class
|
||||
=> builder.AddEndpointFilter<ValidateFilter<TDto>>();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue