diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bb4f649..b47fd488 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,52 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v7.2.0](https://github.com/Genocs/genocs-library/compare/v7.1.0...v7.2.0) + +> 14 December 2024 + +- Update versions, remove Polly, and add configuration files [`bbb923f`](https://github.com/Genocs/genocs-library/commit/bbb923f2c15dc5bd99237d76a0114a2984f9d168) +- Refactor and update codebase for C# 9.0 features [`74926b3`](https://github.com/Genocs/genocs-library/commit/74926b3edc638b9a7179ae6d3e746eec346cd0ab) +- Add new service, project, and OpenTelemetry config [`42292cf`](https://github.com/Genocs/genocs-library/commit/42292cffc5b4b329377bf6adaf243504b3bc6073) +- Refactor for multiple roles and sync methods [`db339c2`](https://github.com/Genocs/genocs-library/commit/db339c2ef27cd15cbbde0bfe9b1c9dac6e0a0c12) +- Refactor authentication and JWT handling components [`5636ddc`](https://github.com/Genocs/genocs-library/commit/5636ddc9479d0a80a9c9c87c9175dc33f38d55bb) +- Update configuration settings and service details [`1040881`](https://github.com/Genocs/genocs-library/commit/104088196666c31668f93a52fb89d6d2a70b7943) +- Refactor namespaces and update OpenTelemetry config [`af280e0`](https://github.com/Genocs/genocs-library/commit/af280e0b6a7b08ab333f0ff2339aa068811f0f27) +- Refactor and update configurations and dependencies [`2ba4d28`](https://github.com/Genocs/genocs-library/commit/2ba4d2829a4e635e2b52b0ea022716edd75c6f1c) +- Add OpenTelemetry support and update various configurations [`0e6140b`](https://github.com/Genocs/genocs-library/commit/0e6140ba2ebb2bac7671e28e925546cca4611009) +- Add CreateAdmin feature and refactor user creation [`dd2dfdf`](https://github.com/Genocs/genocs-library/commit/dd2dfdfe40cd45cc3ba02d7aaa04f7b8c1368f47) +- Upgrade to .NET 9.0 and refine project configurations [`3fbbc7e`](https://github.com/Genocs/genocs-library/commit/3fbbc7e5597c7bb0b59383b39b0b4aedbf99bc8b) +- Enhance JWT authentication and code organization [`5894fd7`](https://github.com/Genocs/genocs-library/commit/5894fd7232171ff202a9c2946b88602a149eec03) +- Add JWT authentication and enhance authorization policies [`48b01f3`](https://github.com/Genocs/genocs-library/commit/48b01f31ac67b5bb9e60dcaf9d119031707bf23c) +- Enhance logging, JWT options, and health checks [`86875f5`](https://github.com/Genocs/genocs-library/commit/86875f5d53e372be8a82f1bd2030aaacda6a7914) +- Enable services and enhance configuration settings [`2654fe6`](https://github.com/Genocs/genocs-library/commit/2654fe6b756e97034960ba73c39b57429678295f) +- Refactor JWT handling and improve code readability [`b525d35`](https://github.com/Genocs/genocs-library/commit/b525d35a18add383b0960d4405069b91a97decd5) +- Refactor and improve nullability handling [`07bee95`](https://github.com/Genocs/genocs-library/commit/07bee95f1226ab4406078380da2b25744f413d8e) +- Add console exporter support for OpenTelemetry [`5f0ca21`](https://github.com/Genocs/genocs-library/commit/5f0ca21d0f36032c62102833ea95ad63c1b809aa) +- Update error handling, tracing, and configuration [`eda7085`](https://github.com/Genocs/genocs-library/commit/eda7085847b057983269dd6fd08c237644dfe553) +- Enhance GenocsBuilder and update Program.cs structure [`23aa7c6`](https://github.com/Genocs/genocs-library/commit/23aa7c6418365f49be9de771be028dae7758c02a) +- Standardize property names and improve documentation [`8e48cfa`](https://github.com/Genocs/genocs-library/commit/8e48cfa891777398b276965dd3f02d3b536e75ad) +- Add OtlpEndpoint to LoggerOptions and update exception handling [`3df7280`](https://github.com/Genocs/genocs-library/commit/3df72803f83a03922523c036cfe2686cd9c0bfec) +- Update package versions and modify exception logic [`f24c465`](https://github.com/Genocs/genocs-library/commit/f24c46508d67da55fab3cc96c3ebb295977e9222) + +#### [v7.1.0](https://github.com/Genocs/genocs-library/compare/v7.0.0...v7.1.0) + +> 29 November 2024 + +- Update packages and refactor MongoDB repository methods [`#126`](https://github.com/Genocs/genocs-library/pull/126) +- Ver 700 [`#125`](https://github.com/Genocs/genocs-library/pull/125) +- Update CHANGELOG for v7.0.0 release with recent changes and improvements [`#121`](https://github.com/Genocs/genocs-library/pull/121) +- Refactor RabbitMQ client to use async methods [`d5cf8ab`](https://github.com/Genocs/genocs-library/commit/d5cf8abe6b4a4a21712579373858431ea3f5589c) +- Update appsettings and refactor code for services [`b9e6e8c`](https://github.com/Genocs/genocs-library/commit/b9e6e8c43a700c1865f1ed23c561b0c69a4ce417) +- Update Docker, health checks, and service configurations [`828c7a7`](https://github.com/Genocs/genocs-library/commit/828c7a775b3eb095204e5df0e62614bfbebe6705) +- Add .env template, update appsettings for Docker compatibility, and modify service configurations [`c3c6488`](https://github.com/Genocs/genocs-library/commit/c3c64884098deafec6053bd3838eaefbb67bc4ea) +- Update package references to Genocs.Core and related packages to version 7.1.0 [`a8b8c4a`](https://github.com/Genocs/genocs-library/commit/a8b8c4a9baff9e54c9e38a0cd20bad4bbea5a0dd) +- Refactor and update nullability and configurations [`992c33d`](https://github.com/Genocs/genocs-library/commit/992c33d33e686d5f573d899c911cd297ad086637) +- Refactor constructors and update Program.cs endpoints [`4f14136`](https://github.com/Genocs/genocs-library/commit/4f14136419cb43d8619721f02ce4082c92d880fb) +- Refactor service registration and update dependencies [`78a7892`](https://github.com/Genocs/genocs-library/commit/78a789238db6c2e6def7b1d8103facf1092191e5) +- Upgrade packages and modify NotificationId property [`77bbd75`](https://github.com/Genocs/genocs-library/commit/77bbd75766d14dcde87d386e84e3ccfb248a5062) +- Disable Azure logging and clear connection strings [`3a44081`](https://github.com/Genocs/genocs-library/commit/3a440812ef71aa64ecf0a1a829943d8c2723edd6) + ### [v7.0.0](https://github.com/Genocs/genocs-library/compare/v6.4.0...v7.0.0) > 24 November 2024 diff --git a/Directory.Build.props b/Directory.Build.props index ae739882..b2374060 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -13,7 +13,7 @@ $(MSBuildThisFileDirectory)dotnet.ruleset True True - 6.3.0 + 7.1.0 13.0 Genocs Genocs 2024 diff --git a/containers/infrastructure-monitoring.yml b/containers/infrastructure-monitoring.yml index 6eeedab9..9956e84d 100644 --- a/containers/infrastructure-monitoring.yml +++ b/containers/infrastructure-monitoring.yml @@ -1,4 +1,19 @@ services: + aspire: + image: mcr.microsoft.com/dotnet/aspire-dashboard:9.0 + hostname: aspire_dashboard + container_name: aspire_dashboard + ports: + - 18888:18888 + - 4318:18889 + + environment: + - DOTNET_DASHBOARD_UNSECURED_ALLOW_ANONYMOUS=true + + networks: + - genocs + # network_mode: host + grafana: image: grafana/grafana hostname: grafana diff --git a/genocs.sln b/genocs.sln index dac56edb..53366e9e 100644 --- a/genocs.sln +++ b/genocs.sln @@ -123,14 +123,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Application", "Application" src\apps\docker-compose.override.yml = src\apps\docker-compose.override.yml src\apps\docker-compose.yml = src\apps\docker-compose.yml src\apps\identity-webapi.dockerfile = src\apps\identity-webapi.dockerfile - src\apps\order-webapi.dockerfile = src\apps\order-webapi.dockerfile src\apps\local.env = src\apps\local.env + src\apps\order-webapi.dockerfile = src\apps\order-webapi.dockerfile src\apps\product-webapi.dockerfile = src\apps\product-webapi.dockerfile src\apps\signalr-webapi.dockerfile = src\apps\signalr-webapi.dockerfile EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Genocs.Secrets.AzureKeyVault", "src\Genocs.Secrets.AzureKeyVault\Genocs.Secrets.AzureKeyVault.csproj", "{ECBF1AEE-AE3A-4F81-9943-5675169DEFA7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Genocs.OpenTelemetry", "src\Genocs.OpenTelemetry\Genocs.OpenTelemetry.csproj", "{62380657-23D0-0ECD-8FFE-0B1DA5461D37}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Genocs.Core.Demo.HelloWorld", "src\Genocs.Core.Demo.HelloWorld\Genocs.Core.Demo.HelloWorld.csproj", "{D7C394CF-487D-470D-B05C-CC2DD7EC290B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -297,6 +301,14 @@ Global {ECBF1AEE-AE3A-4F81-9943-5675169DEFA7}.Debug|Any CPU.Build.0 = Debug|Any CPU {ECBF1AEE-AE3A-4F81-9943-5675169DEFA7}.Release|Any CPU.ActiveCfg = Release|Any CPU {ECBF1AEE-AE3A-4F81-9943-5675169DEFA7}.Release|Any CPU.Build.0 = Release|Any CPU + {62380657-23D0-0ECD-8FFE-0B1DA5461D37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {62380657-23D0-0ECD-8FFE-0B1DA5461D37}.Debug|Any CPU.Build.0 = Debug|Any CPU + {62380657-23D0-0ECD-8FFE-0B1DA5461D37}.Release|Any CPU.ActiveCfg = Release|Any CPU + {62380657-23D0-0ECD-8FFE-0B1DA5461D37}.Release|Any CPU.Build.0 = Release|Any CPU + {D7C394CF-487D-470D-B05C-CC2DD7EC290B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D7C394CF-487D-470D-B05C-CC2DD7EC290B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7C394CF-487D-470D-B05C-CC2DD7EC290B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D7C394CF-487D-470D-B05C-CC2DD7EC290B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -325,6 +337,7 @@ Global {B2028A73-6C94-4166-A0BB-22080805E351} = {140B7191-88E9-4EEE-9D86-9A70839F8507} {6CE8740F-8561-481B-AC9F-D1E73C449235} = {B2028A73-6C94-4166-A0BB-22080805E351} {B184733D-2415-4517-BC65-26ED22EEB2C2} = {51A2E158-4686-4764-91D5-3CDDD06280D4} + {D7C394CF-487D-470D-B05C-CC2DD7EC290B} = {220036E9-322D-4D4A-BA98-21DCF111C50A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FF634A51-3CA4-4FB3-A8ED-71C403516166} diff --git a/global.json b/global.json index f6d787e3..6d77f621 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,7 @@ { "sdk": { - "version": "8.0.10", + "version": "9.0.0", "rollForward": "latestMajor", "allowPrerelease": true } -} \ No newline at end of file +} diff --git a/launchSettings.json b/launchSettings.json index c0eaff1c..dc2b791d 100644 --- a/launchSettings.json +++ b/launchSettings.json @@ -8,4 +8,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs b/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs index b0de9341..3a867872 100644 --- a/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs +++ b/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs @@ -7,43 +7,36 @@ namespace Genocs.Auth; /// /// The access token validator middleware. /// -public class AccessTokenValidatorMiddleware : IMiddleware +/// +/// The AccessTokenValidatorMiddleware constructor. +/// +/// The access token service. +/// The options. +public class AccessTokenValidatorMiddleware(IAccessTokenService accessTokenService, JwtOptions options) : IMiddleware { - private readonly IAccessTokenService _accessTokenService; - private readonly IEnumerable _endpoints; - - /// - /// The AccessTokenValidatorMiddleware constructor. - /// - /// The access token service. - /// The options. - public AccessTokenValidatorMiddleware(IAccessTokenService accessTokenService, JwtOptions options) - { - _accessTokenService = accessTokenService; - _endpoints = options.AllowAnonymousEndpoints ?? Enumerable.Empty(); - } + private readonly IAccessTokenService _accessTokenService = accessTokenService; + private readonly IEnumerable _allowAnonymousEndpoints = options.AllowAnonymousEndpoints ?? []; /// /// The InvokeAsync method. /// /// The http context. /// The request delegate. - /// + /// The task. public async Task InvokeAsync(HttpContext context, RequestDelegate next) { string path = context.Request.Path.HasValue ? context.Request.Path.Value : string.Empty; - if (_endpoints.Contains(path)) + // Skip check on AnonymousEndpoints + if (_allowAnonymousEndpoints.Contains(path)) { await next(context); - return; } - if (await _accessTokenService.IsCurrentActiveToken()) + if (_accessTokenService.IsCurrentActiveToken()) { await next(context); - return; } diff --git a/src/Genocs.Auth/Configurations/JwtOptions.cs b/src/Genocs.Auth/Configurations/JwtOptions.cs index 79566a5c..3f13edf3 100644 --- a/src/Genocs.Auth/Configurations/JwtOptions.cs +++ b/src/Genocs.Auth/Configurations/JwtOptions.cs @@ -1,3 +1,5 @@ +using Microsoft.IdentityModel.Tokens; + namespace Genocs.Auth.Configurations; public class JwtOptions @@ -14,7 +16,12 @@ public class JwtOptions public IEnumerable? AllowAnonymousEndpoints { get; set; } public CertificateOptions? Certificate { get; set; } - public string? Algorithm { get; set; } + + /// + /// The algorithm used to sign the token. + /// Defaults to SecurityAlgorithms.HmacSha256 'HS256'. + /// + public string Algorithm { get; set; } = SecurityAlgorithms.HmacSha256; public string? Issuer { get; set; } public string? IssuerSigningKey { get; set; } public string? Authority { get; set; } @@ -31,7 +38,12 @@ public class JwtOptions public bool RequireHttpsMetadata { get; set; } public bool RequireExpirationTime { get; set; } = true; public bool RequireSignedTokens { get; set; } = true; - public int ExpiryMinutes { get; set; } + + /// + /// The expiration time of the token in minutes. + /// Defaults to 60 minutes. + /// + public int ExpiryMinutes { get; set; } = 60; public TimeSpan? Expiry { get; set; } public string? ValidAudience { get; set; } public IEnumerable? ValidAudiences { get; set; } @@ -65,19 +77,31 @@ public class JwtOptions /// Defaults to true. /// public bool IncludeErrorDetails { get; set; } = true; + public string? AuthenticationType { get; set; } public string? NameClaimType { get; set; } /// /// The claim type that will be used to determine the user's roles. - /// Defaults to "Role". + /// Default is "Role". /// public string RoleClaimType { get; set; } = "Role"; public class CertificateOptions { + /// + /// The location of the certificate. + /// public string? Location { get; set; } + + /// + /// The certificate as a byte array. + /// public string? RawData { get; set; } + + /// + /// The certificate password. + /// public string? Password { get; set; } } } \ No newline at end of file diff --git a/src/Genocs.Auth/DisabledAuthenticationPolicyEvaluator.cs b/src/Genocs.Auth/DisabledAuthenticationPolicyEvaluator.cs index 85cc1ab8..c4cf0edf 100644 --- a/src/Genocs.Auth/DisabledAuthenticationPolicyEvaluator.cs +++ b/src/Genocs.Auth/DisabledAuthenticationPolicyEvaluator.cs @@ -17,10 +17,7 @@ internal sealed class DisabledAuthenticationPolicyEvaluator : IPolicyEvaluator /// public Task AuthenticateAsync(AuthorizationPolicy policy, HttpContext context) { - var authenticationTicket = new AuthenticationTicket( - new ClaimsPrincipal(), - new AuthenticationProperties(), - JwtBearerDefaults.AuthenticationScheme); + var authenticationTicket = new AuthenticationTicket(new ClaimsPrincipal(), new AuthenticationProperties(), JwtBearerDefaults.AuthenticationScheme); return Task.FromResult(AuthenticateResult.Success(authenticationTicket)); } @@ -33,11 +30,7 @@ public Task AuthenticateAsync(AuthorizationPolicy policy, Ht /// /// /// - public Task AuthorizeAsync( - AuthorizationPolicy policy, - AuthenticateResult authenticationResult, - HttpContext context, - object resource) + public Task AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource) { return Task.FromResult(PolicyAuthorizationResult.Success()); } diff --git a/src/Genocs.Auth/Extensions.cs b/src/Genocs.Auth/Extensions.cs index 05e93b9f..6abd6e6b 100644 --- a/src/Genocs.Auth/Extensions.cs +++ b/src/Genocs.Auth/Extensions.cs @@ -1,3 +1,5 @@ +using System.Security.Cryptography.X509Certificates; +using System.Text; using Genocs.Auth.Configurations; using Genocs.Auth.Handlers; using Genocs.Auth.Services; @@ -11,8 +13,6 @@ using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Tokens; -using System.Security.Cryptography.X509Certificates; -using System.Text; namespace Genocs.Auth; @@ -20,10 +20,14 @@ public static class Extensions { private const string RegistryName = "auth"; - public static IGenocsBuilder AddJwt( - this IGenocsBuilder builder, - string sectionName = JwtOptions.Position, - Action? optionsFactory = null) + /// + /// Add JWT authentication. + /// + /// The Genocs builder. + /// The JWT configuration section in case you want to change the default name. + /// The option builder action in case option requires custom action to be done. + /// The Genocs builder you can use for chain. + public static IGenocsBuilder AddJwt(this IGenocsBuilder builder, string sectionName = JwtOptions.Position, Action? optionsFactory = null) { if (string.IsNullOrWhiteSpace(sectionName)) { @@ -34,26 +38,32 @@ public static IGenocsBuilder AddJwt( return builder.AddJwt(options, optionsFactory); } - private static IGenocsBuilder AddJwt( - this IGenocsBuilder builder, - JwtOptions options, - Action? optionsFactory = null) + /// + /// Add JWT authentication. Internal function. + /// + /// The Genocs builder. + /// The JWT options. + /// The option builder action in case option requires custom action to be done. + /// The Genocs builder you can use for chain. + private static IGenocsBuilder AddJwt(this IGenocsBuilder builder, JwtOptions options, Action? optionsFactory = null) { if (!builder.TryRegister(RegistryName)) { return builder; } - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddTransient(); - if (!options.Enabled) { builder.Services.AddSingleton(); } + // To be able to access the HttpContext in the InMemoryAccessTokenService + builder.Services.AddSingleton(); + + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddTransient(); + var tokenValidationParameters = new TokenValidationParameters { RequireAudience = options.RequireAudience, @@ -121,13 +131,8 @@ private static IGenocsBuilder AddJwt( // If no certificate is provided, use symmetric encryption. if (!string.IsNullOrWhiteSpace(options.IssuerSigningKey) && !hasCertificate) { - if (string.IsNullOrWhiteSpace(options.Algorithm) || hasCertificate) - { - options.Algorithm = SecurityAlgorithms.HmacSha256; - } - - byte[] rawKey = Encoding.UTF8.GetBytes(options.IssuerSigningKey); - tokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(rawKey); + byte[] key = Encoding.UTF8.GetBytes(options.IssuerSigningKey); + tokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(key); Console.WriteLine("Using symmetric encryption for issuing tokens."); } @@ -141,6 +146,9 @@ private static IGenocsBuilder AddJwt( tokenValidationParameters.RoleClaimType = options.RoleClaimType; } + // Authorization settings + builder.Services.AddAuthorization(); + builder.Services .AddAuthentication(o => { @@ -179,12 +187,14 @@ private static IGenocsBuilder AddJwt( /// The Genocs builder. /// The configuration section name. /// The Genocs builder you can use for chain. - public static IGenocsBuilder AddOpenIdJwt( - this IGenocsBuilder builder, - string sectionName = JwtOptions.Position) + public static IGenocsBuilder AddOpenIdJwt(this IGenocsBuilder builder, string sectionName = JwtOptions.Position) { + if (string.IsNullOrWhiteSpace(sectionName)) + { + sectionName = JwtOptions.Position; + } - JwtOptions options = builder.Configuration.GetOptions(sectionName); + JwtOptions options = builder.Configuration!.GetOptions(sectionName); string metadataAddress = $"{options.Issuer}{options.MetadataAddress}"; var configurationManager = new ConfigurationManager(metadataAddress, new OpenIdConnectConfigurationRetriever()); @@ -215,16 +225,14 @@ public static IGenocsBuilder AddOpenIdJwt( /// The optional section name. Default name: 'jwt'. /// The Genocs builder you can use for chaining. /// Whenever mandatory data like 'IssuerSigningKey' is missing. - public static IGenocsBuilder AddPrivateKeyJwt( - this IGenocsBuilder builder, - string sectionName = JwtOptions.Position) + public static IGenocsBuilder AddPrivateKeyJwt(this IGenocsBuilder builder, string sectionName = JwtOptions.Position) { if (string.IsNullOrWhiteSpace(sectionName)) { sectionName = JwtOptions.Position; } - JwtOptions options = builder.Configuration.GetOptions(sectionName); + JwtOptions options = builder.Configuration!.GetOptions(sectionName); if (string.IsNullOrWhiteSpace(options.IssuerSigningKey)) { @@ -257,6 +265,11 @@ public static IGenocsBuilder AddPrivateKeyJwt( return builder; } + /// + /// This middleware validates the access token in real-time. + /// + /// The app builder. + /// The app builder you can use for chaining. public static IApplicationBuilder UseAccessTokenValidator(this IApplicationBuilder app) => app.UseMiddleware(); } @@ -271,5 +284,5 @@ internal static class DateExtensions /// /// /// - public static long ToTimestamp(this DateTime dateTime) => new DateTimeOffset(dateTime).ToUnixTimeSeconds(); + public static long ToTimestamp(this in DateTime dateTime) => new DateTimeOffset(dateTime).ToUnixTimeSeconds(); } \ No newline at end of file diff --git a/src/Genocs.Auth/Genocs.Auth.csproj b/src/Genocs.Auth/Genocs.Auth.csproj index f8482eb1..0fa6533c 100644 --- a/src/Genocs.Auth/Genocs.Auth.csproj +++ b/src/Genocs.Auth/Genocs.Auth.csproj @@ -5,14 +5,14 @@ Genocs.Auth Genocs.Auth Genocs.Auth - The authorization library useful to build .NET Core projects. - The authorization library useful to build .NET Core projects. + The Genocs authorization library. + The authorization library. true 5.0.0 Nocco Giovanni Emanuele - microservice microservices solid solid-principles authentication genocs + authentication jwt genocs microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest @@ -20,7 +20,6 @@ - @@ -29,22 +28,18 @@ - - + - - - diff --git a/src/Genocs.Auth/Handlers/JwtHandler.cs b/src/Genocs.Auth/Handlers/JwtHandler.cs index 36e197e0..ce517fc0 100644 --- a/src/Genocs.Auth/Handlers/JwtHandler.cs +++ b/src/Genocs.Auth/Handlers/JwtHandler.cs @@ -1,15 +1,14 @@ -using Genocs.Auth.Configurations; -using Microsoft.IdentityModel.Tokens; +using System.Data; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; +using System.Text; +using Genocs.Auth.Configurations; +using Microsoft.IdentityModel.Tokens; namespace Genocs.Auth.Handlers; internal sealed class JwtHandler : IJwtHandler { - private static readonly IDictionary> EmptyClaims = - new Dictionary>(); - private static readonly ISet DefaultClaims = new HashSet { JwtRegisteredClaimNames.Sub, @@ -23,41 +22,34 @@ internal sealed class JwtHandler : IJwtHandler private readonly JwtOptions _options; private readonly TokenValidationParameters _tokenValidationParameters; private readonly SigningCredentials _signingCredentials; - private readonly string? _issuer; public JwtHandler(JwtOptions options, TokenValidationParameters tokenValidationParameters) { - var issuerSigningKey = tokenValidationParameters.IssuerSigningKey; - if (issuerSigningKey is null) - { - throw new InvalidOperationException("Issuer signing key not set."); - } + var issuerSigningKey = tokenValidationParameters.IssuerSigningKey + ?? throw new InvalidOperationException("Issuer signing key not set."); if (string.IsNullOrWhiteSpace(options.Algorithm)) { - throw new InvalidOperationException("Security algorithm not set."); + options.Algorithm = issuerSigningKey is SymmetricSecurityKey + ? SecurityAlgorithms.HmacSha256 + : SecurityAlgorithms.RsaSha256; } _options = options; _tokenValidationParameters = tokenValidationParameters; _signingCredentials = new SigningCredentials(issuerSigningKey, _options.Algorithm); - _issuer = options.Issuer; } /// /// Creates a new token. /// - /// - /// - /// - /// + /// The UserId. + /// The User Role. + /// The audience. + /// The list of claims. /// /// It is thrown when mandatory data is empty. - public JsonWebToken CreateToken( - string userId, - string? role = null, - string? audience = null, - IDictionary>? claims = null) + public JsonWebToken CreateToken(string userId, IEnumerable? roles = null, string? audience = null, IDictionary>? claims = null) { if (string.IsNullOrWhiteSpace(userId)) { @@ -73,9 +65,12 @@ public JsonWebToken CreateToken( new(JwtRegisteredClaimNames.Iat, now.ToTimestamp().ToString()), }; - if (!string.IsNullOrWhiteSpace(role)) + if (roles is not null) { - jwtClaims.Add(new Claim(ClaimTypes.Role, role)); + foreach (string item in roles.Where(x => !string.IsNullOrEmpty(x))) + { + jwtClaims.Add(new Claim(ClaimTypes.Role, item)); + } } if (!string.IsNullOrWhiteSpace(audience)) @@ -99,36 +94,33 @@ public JsonWebToken CreateToken( : now.AddMinutes(_options.ExpiryMinutes); var jwt = new JwtSecurityToken( - _issuer, + issuer: _options.Issuer, claims: jwtClaims, notBefore: now, expires: expires, signingCredentials: _signingCredentials); - string token = new JwtSecurityTokenHandler().WriteToken(jwt); + string token = _jwtSecurityTokenHandler.WriteToken(jwt); return new JsonWebToken { + Id = userId, AccessToken = token, RefreshToken = string.Empty, Expires = expires.ToTimestamp(), - Id = userId, - Role = role ?? string.Empty, - Claims = claims ?? EmptyClaims + Roles = roles, + Claims = claims }; } /// /// Gets the token payload. /// - /// - /// - public JsonWebTokenPayload? GetTokenPayload(string accessToken) + /// The string describing the access token. + /// The JWT Token payload. + public JsonWebTokenPayload? GetTokenPayload(string token) { - _jwtSecurityTokenHandler.ValidateToken( - accessToken, - _tokenValidationParameters, - out var validatedSecurityToken); + _jwtSecurityTokenHandler.ValidateToken(token, _tokenValidationParameters, out var validatedSecurityToken); if (validatedSecurityToken is not JwtSecurityToken jwt) { @@ -138,11 +130,56 @@ public JsonWebToken CreateToken( return new JsonWebTokenPayload { Subject = jwt.Subject, - Role = jwt.Claims.SingleOrDefault(x => x.Type == ClaimTypes.Role)?.Value, + Roles = jwt.Claims.Where(x => x.Type == ClaimTypes.Role)?.Select(c => c.Value), Expires = jwt.ValidTo.ToTimestamp(), Claims = jwt.Claims.Where(x => !DefaultClaims.Contains(x.Type)) .GroupBy(c => c.Type) .ToDictionary(k => k.Key, v => v.Select(c => c.Value)) }; } + + /// + /// This method creates a token using the JwtSecurityTokenHandler class. + /// + /// The JWT token string. + public string CreateToken() + { + SecurityTokenDescriptor tokenDescriptor = CreateSecurityTokenDescriptor(); + + var tokenHandler = new JwtSecurityTokenHandler(); + var token = tokenHandler.CreateToken(tokenDescriptor); + return tokenHandler.WriteToken(token); + } + + /// + /// This method creates a token using the JsonWebTokenHandler class. + /// + /// The JWT token string. + public string CreateTokenWithJsonWebTokenHandler() + { + SecurityTokenDescriptor tokenDescriptor = CreateSecurityTokenDescriptor(); + + var tokenHandler = new Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler(); + string token = tokenHandler.CreateToken(tokenDescriptor); + return token; + } + + /// + /// Internal method to create a SecurityTokenDescriptor. + /// + /// The created SecurityTokenDescriptor. + private SecurityTokenDescriptor CreateSecurityTokenDescriptor() + { + byte[] key = Encoding.ASCII.GetBytes(_options.IssuerSigningKey!); + return new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(new Claim[] + { + new Claim(ClaimTypes.Name, "username"), + new Claim(ClaimTypes.Role, "role") + }), + Expires = DateTime.UtcNow.AddMinutes(_options.ExpiryMinutes), + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), _options.Algorithm) + }; + } } \ No newline at end of file diff --git a/src/Genocs.Auth/IAccessTokenService.cs b/src/Genocs.Auth/IAccessTokenService.cs index a558e622..e22ba769 100644 --- a/src/Genocs.Auth/IAccessTokenService.cs +++ b/src/Genocs.Auth/IAccessTokenService.cs @@ -2,8 +2,8 @@ namespace Genocs.Auth; public interface IAccessTokenService { - Task IsCurrentActiveToken(); - Task DeactivateCurrentAsync(); - Task IsActiveAsync(string token); - Task DeactivateAsync(string token); + bool IsCurrentActiveToken(); + void DeactivateCurrent(); + bool IsActive(string token); + void Deactivate(string token); } \ No newline at end of file diff --git a/src/Genocs.Auth/IJwtHandler.cs b/src/Genocs.Auth/IJwtHandler.cs index 5720e6fa..8cfba038 100644 --- a/src/Genocs.Auth/IJwtHandler.cs +++ b/src/Genocs.Auth/IJwtHandler.cs @@ -9,20 +9,16 @@ public interface IJwtHandler /// It allows to create a new JsonWebToken. /// /// The userId. - /// The role. + /// The list of roles. /// The audience. /// The claims. /// The JsonWebToken just created. - JsonWebToken CreateToken( - string userId, - string? role = null, - string? audience = null, - IDictionary>? claims = null); + JsonWebToken CreateToken(string userId, IEnumerable? roles = null, string? audience = null, IDictionary>? claims = null); /// /// Get the JsonWebTokenPayload from the accessToken. /// - /// The access token string value. + /// The access token string value. /// The JsonWebTokenPayload. - JsonWebTokenPayload? GetTokenPayload(string accessToken); + JsonWebTokenPayload? GetTokenPayload(string token); } \ No newline at end of file diff --git a/src/Genocs.Auth/JsonWebToken.cs b/src/Genocs.Auth/JsonWebToken.cs index bdf2eefc..d982dafd 100644 --- a/src/Genocs.Auth/JsonWebToken.cs +++ b/src/Genocs.Auth/JsonWebToken.cs @@ -5,6 +5,11 @@ namespace Genocs.Auth; /// public class JsonWebToken { + /// + /// Gets or sets the access token unique identifier. + /// + public string? Id { get; set; } + /// /// Gets or sets the access token. /// @@ -20,15 +25,10 @@ public class JsonWebToken /// public long Expires { get; set; } - /// - /// Gets or sets the access token unique identifier. - /// - public string? Id { get; set; } - /// /// Gets or sets the access token role. /// - public string? Role { get; set; } + public IEnumerable? Roles { get; set; } /// /// The claims. diff --git a/src/Genocs.Auth/JsonWebTokenPayload.cs b/src/Genocs.Auth/JsonWebTokenPayload.cs index b3dcfdc1..1f3f86c1 100644 --- a/src/Genocs.Auth/JsonWebTokenPayload.cs +++ b/src/Genocs.Auth/JsonWebTokenPayload.cs @@ -13,15 +13,15 @@ public class JsonWebTokenPayload /// /// The Identity Role. /// - public string? Role { get; set; } + public IEnumerable? Roles { get; set; } /// - /// The expiration ticks. + /// List of claims. /// - public long Expires { get; set; } + public IDictionary>? Claims { get; set; } /// - /// List of claims. + /// The expiration ticks. /// - public IDictionary>? Claims { get; set; } + public long Expires { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Auth/JwtAuthAttribute.cs b/src/Genocs.Auth/JwtAuthAttribute.cs index 96439a83..763e0a99 100644 --- a/src/Genocs.Auth/JwtAuthAttribute.cs +++ b/src/Genocs.Auth/JwtAuthAttribute.cs @@ -1,11 +1,7 @@ namespace Genocs.Auth; -public class JwtAuthAttribute : AuthAttribute +public class JwtAuthAttribute(string policy = "") + : AuthAttribute(AuthenticationScheme, policy) { public const string AuthenticationScheme = "Bearer"; - - public JwtAuthAttribute(string policy = "") - : base(AuthenticationScheme, policy) - { - } } \ No newline at end of file diff --git a/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs b/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs index 8ce4fb58..f1d51329 100644 --- a/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs +++ b/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs @@ -5,45 +5,36 @@ namespace Genocs.Auth.Services; -internal sealed class InMemoryAccessTokenService : IAccessTokenService +/// +/// This service allows to validate JWT Token in real-time. +/// In this way tokens can be invalidated and the effect shall be immediate. +/// +internal sealed class InMemoryAccessTokenService(IMemoryCache cache, IHttpContextAccessor httpContextAccessor, JwtOptions jwtOptions) : IAccessTokenService { - private readonly IMemoryCache _cache; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly TimeSpan _expires; - - public InMemoryAccessTokenService( - IMemoryCache cache, - IHttpContextAccessor httpContextAccessor, - JwtOptions jwtOptions) - { - _cache = cache; - _httpContextAccessor = httpContextAccessor; - _expires = jwtOptions.Expiry ?? TimeSpan.FromMinutes(jwtOptions.ExpiryMinutes); - } + private readonly IMemoryCache _cache = cache; + private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor; + private readonly TimeSpan _expires = jwtOptions.Expiry ?? TimeSpan.FromMinutes(jwtOptions.ExpiryMinutes); - public Task IsCurrentActiveToken() - => IsActiveAsync(GetCurrentAsync()); + public bool IsCurrentActiveToken() + => IsActive(GetCurrent()); - public Task DeactivateCurrentAsync() - => DeactivateAsync(GetCurrentAsync()); + public void DeactivateCurrent() + => Deactivate(GetCurrent()); - public Task IsActiveAsync(string token) - => Task.FromResult(string.IsNullOrWhiteSpace(_cache.Get(GetKey(token)))); + public bool IsActive(string token) + => string.IsNullOrWhiteSpace(_cache.Get(GetKey(token))); - public Task DeactivateAsync(string token) + public void Deactivate(string token) { _cache.Set(GetKey(token), "revoked", new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = _expires }); - - return Task.CompletedTask; } - private string GetCurrentAsync() + private string GetCurrent() { - var authorizationHeader = _httpContextAccessor - .HttpContext?.Request.Headers["authorization"]; + var authorizationHeader = _httpContextAccessor.HttpContext?.Request.Headers.Authorization; if (authorizationHeader is null) { @@ -52,7 +43,7 @@ private string GetCurrentAsync() return authorizationHeader.Value == StringValues.Empty ? string.Empty - : authorizationHeader.Value.Single().Split(' ').Last(); + : authorizationHeader.Value.Single()?.Split(' ').Last(); } private static string GetKey(string token) => $"blacklisted-tokens:{token}"; diff --git a/src/Genocs.Common/Genocs.Common.csproj b/src/Genocs.Common/Genocs.Common.csproj index 5190a096..2a5a829d 100644 --- a/src/Genocs.Common/Genocs.Common.csproj +++ b/src/Genocs.Common/Genocs.Common.csproj @@ -5,14 +5,14 @@ Genocs.Common Genocs.Common Genocs.Common - The Genocs Library - Common components. + The Genocs common components. The common components to build .NET Core projects along with Genocs Library. true 5.0.0 Nocco Giovanni Emanuele microservice microservices solid solid-principles genocs README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.Core.Demo.HelloWorld/Controllers/WeatherForecastController.cs b/src/Genocs.Core.Demo.HelloWorld/Controllers/WeatherForecastController.cs new file mode 100644 index 00000000..26e7957c --- /dev/null +++ b/src/Genocs.Core.Demo.HelloWorld/Controllers/WeatherForecastController.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Genocs.Core.Demo.HelloWorld.Controllers; +[ApiController] +[Route("[controller]")] +public class WeatherForecastController : ControllerBase +{ + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } + + [HttpGet(Name = "GetWeatherForecast")] + public IEnumerable Get() + { + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = Summaries[Random.Shared.Next(Summaries.Length)] + }) + .ToArray(); + } +} diff --git a/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.csproj b/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.csproj new file mode 100644 index 00000000..85556587 --- /dev/null +++ b/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.csproj @@ -0,0 +1,26 @@ + + + + net9.0 + enable + enable + genocs + + + + + + + + + + + + + + + + + + + diff --git a/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.http b/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.http new file mode 100644 index 00000000..ed67dc51 --- /dev/null +++ b/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.http @@ -0,0 +1,6 @@ +@Genocs.Core.Demo.HelloWorld_HostAddress = http://localhost:5267 + +GET {{Genocs.Core.Demo.HelloWorld_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/src/Genocs.Core.Demo.HelloWorld/Program.cs b/src/Genocs.Core.Demo.HelloWorld/Program.cs new file mode 100644 index 00000000..db0cb88d --- /dev/null +++ b/src/Genocs.Core.Demo.HelloWorld/Program.cs @@ -0,0 +1,87 @@ +using System.Security.Claims; +using Genocs.Auth; +using Genocs.Core.Builders; +using Genocs.GnxOpenTelemetry; +using Genocs.Logging; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization.Infrastructure; + +StaticLogger.EnsureInitialized(); + +var builder = WebApplication.CreateBuilder(args); + +builder.Host + .UseLogging(); + +builder.AddGenocs() + .AddOpenTelemetry() + .AddJwt("simmetric_jwt") + .Build(); + +// Add services to the container. +builder.Services.AddControllers(); + +// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi +builder.Services.AddOpenApi(); + +builder.Services.AddCors(options => +{ + options.AddDefaultPolicy(builder => + { + builder.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader(); + }); +}); + +// Override the default authorization policy +builder.Services.AddAuthorizationBuilder() + .AddPolicy("Reader", builder => builder.RequireAssertion(context => context.User.HasClaim(ClaimTypes.Role, "user"))) + .AddPolicy("Reader2", builder => builder.RequireClaim(ClaimTypes.Role, "user")) + .AddPolicy("Reader3", builder => builder.RequireRole(["user"])) + .AddPolicy("Reader4", builder => builder.AddRequirements(new AssertionRequirement(context => context.User.IsInRole("user")))) + ; + +//builder.Services.AddAuthorizationBuilder() +// .SetFallbackPolicy(new AuthorizationPolicyBuilder() +// .RequireAuthenticatedUser() +// .Build()) +// .AddPolicy(Policies.UserOnly, policy => policy.RequireAssertion(context +// => context.User.HasClaim(ClaimTypes.Role, Roles.User))) +// .AddPolicy(Policies.AdminOnly, policy => policy.RequireAssertion(context +// => context.User.HasClaim(ClaimTypes.Role, Roles.Admin))) +// .AddPolicy(Policies.UserOrAdmin, policy => policy.RequireAssertion(context +// => context.User.HasClaim(ClaimTypes.Role, Roles.User) +// || context.User.HasClaim(ClaimTypes.Role, Roles.Admin))); + + + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.MapOpenApi(); +} + +app.UseHttpsRedirection(); + +app.UseCors(); +app.UseRouting(); +app.UseAuthentication(); +app.UseAuthorization(); + +// Used to validate the access token +// In RealTime +app.UseAccessTokenValidator(); + +app.MapControllers(); + +// Minimal API with authorization +app.MapGet("/", () => "ok").RequireAuthorization(); + +// Minimal API with authorization policy +app.MapGet("/onlyreader", () => "ok").RequireAuthorization("Reader"); +app.MapGet("/onlyreader2", () => "ok").RequireAuthorization("Reader2"); + +app.Run(); diff --git a/src/Genocs.Core.Demo.HelloWorld/Properties/launchSettings.json b/src/Genocs.Core.Demo.HelloWorld/Properties/launchSettings.json new file mode 100644 index 00000000..889b3f53 --- /dev/null +++ b/src/Genocs.Core.Demo.HelloWorld/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5267", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7062;http://localhost:5267", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/Genocs.Core.Demo.HelloWorld/WeatherForecast.cs b/src/Genocs.Core.Demo.HelloWorld/WeatherForecast.cs new file mode 100644 index 00000000..48f14e81 --- /dev/null +++ b/src/Genocs.Core.Demo.HelloWorld/WeatherForecast.cs @@ -0,0 +1,12 @@ +namespace Genocs.Core.Demo.HelloWorld; + +public class WeatherForecast +{ + public DateOnly Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } +} diff --git a/src/Genocs.Core.Demo.HelloWorld/appsettings.Development.json b/src/Genocs.Core.Demo.HelloWorld/appsettings.Development.json new file mode 100644 index 00000000..43b43db8 --- /dev/null +++ b/src/Genocs.Core.Demo.HelloWorld/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "Microsoft.AspNetCore": "Debug" + } + } +} diff --git a/src/Genocs.Core.Demo.HelloWorld/appsettings.json b/src/Genocs.Core.Demo.HelloWorld/appsettings.json new file mode 100644 index 00000000..2c245ee9 --- /dev/null +++ b/src/Genocs.Core.Demo.HelloWorld/appsettings.json @@ -0,0 +1,90 @@ +{ + "app": { + "name": "Demo HelloWorld", + "service": "api-helloworld", + "instance": "01", + "version": "v1.0", + "displayBanner": true, + "displayVersion": true + }, + "logger": { + "level": "debug", + "excludePaths": [ + "/", + "/healthz", + "/alive", + "/metrics" + ], + "excludeProperties": [ + "api_key", + "access_key", + "ApiKey", + "ApiSecret", + "ClientId", + "ClientSecret", + "ConnectionString", + "Password", + "Email", + "Login", + "Secret", + "Token" + ], + "console": { + "enabled": true + }, + "file": { + "enabled": true, + "path": "logs/logs.txt", + "interval": "day" + }, + "tags": {} + }, + "openTelemetry": { + "enabled": true, + "exporter": { + "enabled": true, + "otlpEndpoint": "http://localhost:4318", + "protocol": "Grpc", + "processorType": "Batch", + "maxQueueSize": 2048, + "scheduledDelayMilliseconds": 5000, + "exporterTimeoutMilliseconds": 30000, + "maxExportBatchSize": 512 + }, + "console": { + "enabled": true, + "enableTracing": true, + "enableMetrics": true, + "enableLogging": true + }, + "azure": { + "enabled": true, + "enableTracing": true, + "enableMetrics": true, + "enableLogging": true, + "connectionString": "InstrumentationKey=1496274b-bda7-4ac6-88ab-9f73b4d3c7b8;IngestionEndpoint=https://italynorth-0.in.applicationinsights.azure.com/;LiveEndpoint=https://italynorth.livediagnostics.monitor.azure.com/;ApplicationId=c417f66d-3611-48a2-80fe-5a6d302bed4f" + } + }, + "Logging": { + "LogLevel": { + "Default": "Debug", + "Microsoft.AspNetCore": "Debug" + } + }, + "AllowedHosts": "*", + "simmetric_jwt": { + "enabled": true, + "requireHttpsMetadata": false, + "issuer": "genocs-identity-service", + "issuerSigningKey": "THIS IS USED TO SIGN AND VERIFY JWT TOKENS, REPLACE IT WITH YOUR OWN SECRET, IT CAN BE ANY STRING", + "validateIssuer": true, + "validIssuer": "genocs-identity-service", + "validateAudience": false, + "validateLifetime": false, + "requireExpirationTime": false, + "requireSignedTokens": false, + "validateIssuerSigningKey": false, + "expiry": "01:00:00", + "saveToken": true + } +} diff --git a/src/Genocs.Core.Demo.Infrastructure/Genocs.Core.Demo.Infrastructure.csproj b/src/Genocs.Core.Demo.Infrastructure/Genocs.Core.Demo.Infrastructure.csproj index 657c487a..833ce3ca 100644 --- a/src/Genocs.Core.Demo.Infrastructure/Genocs.Core.Demo.Infrastructure.csproj +++ b/src/Genocs.Core.Demo.Infrastructure/Genocs.Core.Demo.Infrastructure.csproj @@ -6,4 +6,9 @@ false + + + + + diff --git a/src/Genocs.Core.Demo.Infrastructure/SecurityAuthentication/TokenProvider.cs b/src/Genocs.Core.Demo.Infrastructure/SecurityAuthentication/TokenProvider.cs new file mode 100644 index 00000000..11bee712 --- /dev/null +++ b/src/Genocs.Core.Demo.Infrastructure/SecurityAuthentication/TokenProvider.cs @@ -0,0 +1,63 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; +using Microsoft.Extensions.Configuration; +using Microsoft.IdentityModel.JsonWebTokens; +using Microsoft.IdentityModel.Tokens; + +namespace Genocs.Core.Demo.Infrastructure.SecurityAuthentication; + +public sealed class TokenProvider(IConfiguration configuration) +{ + public string Issuer => configuration["JwtSettings:Issuer"]; + public string Audience => configuration["JwtSettings:Audience"]; + public string Key => configuration["JwtSettings:Key"]; + public int Expiration => int.Parse(configuration["JwtSettings:Expiration"]); + + /// + /// This method creates a token using the JwtSecurityTokenHandler class. + /// + /// + public string CreateToken() + { + var tokenHandler = new JwtSecurityTokenHandler(); + byte[] key = Encoding.ASCII.GetBytes(Key); + + var tokenDescriptor = new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(new Claim[] + { + new Claim(ClaimTypes.Name, "username"), + new Claim(ClaimTypes.Role, "role") + }), + Expires = DateTime.UtcNow.AddMinutes(Expiration), + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) + }; + + var token = tokenHandler.CreateToken(tokenDescriptor); + return tokenHandler.WriteToken(token); + } + + /// + /// This method creates a token using the JsonWebTokenHandler class. + /// + /// + public string CreateTokenWithJsonWebTokenHandler() + { + var tokenHandler = new JsonWebTokenHandler(); + byte[] key = Encoding.ASCII.GetBytes(Key); + var tokenDescriptor = new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(new Claim[] + { + new Claim(ClaimTypes.Name, "username"), + new Claim(ClaimTypes.Role, "role") + }), + Expires = DateTime.UtcNow.AddMinutes(Expiration), + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) + }; + + string token = tokenHandler.CreateToken(tokenDescriptor); + return token; + } +} diff --git a/src/Genocs.Core.Demo.WebApi/Genocs.Core.Demo.WebApi.csproj b/src/Genocs.Core.Demo.WebApi/Genocs.Core.Demo.WebApi.csproj index 85e7e8b6..4352b420 100644 --- a/src/Genocs.Core.Demo.WebApi/Genocs.Core.Demo.WebApi.csproj +++ b/src/Genocs.Core.Demo.WebApi/Genocs.Core.Demo.WebApi.csproj @@ -32,9 +32,8 @@ - + - @@ -43,4 +42,8 @@ + + + + diff --git a/src/Genocs.Core.Demo.WebApi/Program.cs b/src/Genocs.Core.Demo.WebApi/Program.cs index 33d6eedd..8468cfd6 100644 --- a/src/Genocs.Core.Demo.WebApi/Program.cs +++ b/src/Genocs.Core.Demo.WebApi/Program.cs @@ -20,9 +20,9 @@ .UseAzureKeyVault() .UseLogging(); -builder.AddGenocs() +builder + .AddGenocs() .AddJwt() -// .AddOpenIdJwt() .AddOpenTelemetry() .AddMongoFast() .RegisterMongoRepositories(Assembly.GetExecutingAssembly()) @@ -60,6 +60,8 @@ var app = builder.Build(); +app.UseGenocs(); + // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { diff --git a/src/Genocs.Core.Demo.Worker/Genocs.Core.Demo.Worker.csproj b/src/Genocs.Core.Demo.Worker/Genocs.Core.Demo.Worker.csproj index f9b2dcbc..dcc3acc6 100644 --- a/src/Genocs.Core.Demo.Worker/Genocs.Core.Demo.Worker.csproj +++ b/src/Genocs.Core.Demo.Worker/Genocs.Core.Demo.Worker.csproj @@ -4,7 +4,7 @@ net9.0 false false - _Genocs + Genocs @@ -21,9 +21,8 @@ - + - diff --git a/src/Genocs.Core/Builders/Extensions.cs b/src/Genocs.Core/Builders/Extensions.cs index 4c82f9eb..ca6262c1 100644 --- a/src/Genocs.Core/Builders/Extensions.cs +++ b/src/Genocs.Core/Builders/Extensions.cs @@ -87,7 +87,7 @@ public static TModel GetOptions(this IGenocsBuilder builder, string sect } /// - /// Map default endpoints to setup health checks. + /// Map default endpoints to setup root endpoint and health checks. /// /// The web Application. /// The WebApplication to be used for chain. @@ -110,7 +110,7 @@ public static IApplicationBuilder MapDefaultEndpoints(this IApplicationBuilder a }); // All health checks must pass for app to be considered ready to accept traffic after starting - endpoints.MapHealthChecks("/health"); + endpoints.MapHealthChecks("/healthz"); // Only health checks tagged with the "live" tag must pass for app to be considered alive endpoints.MapHealthChecks("/alive", new HealthCheckOptions @@ -136,15 +136,6 @@ public static WebApplication MapDefaultEndpoints(this WebApplication app) return app; } - // All health checks must pass for app to be considered ready to accept traffic after starting - app.MapHealthChecks("/healthz"); - - // Only health checks tagged with the "live" tag must pass for app to be considered alive - app.MapHealthChecks("/alive", new HealthCheckOptions - { - Predicate = r => r.Tags.Contains("live") - }); - app.MapGet("/", async context => { // Get the Entry Assembly Name and Version @@ -154,7 +145,17 @@ public static WebApplication MapDefaultEndpoints(this WebApplication app) string message = $"Service {serviceVersion ?? assemblyVersion} is running"; await context.Response.WriteAsync(context.RequestServices.GetService()?.Name ?? message); - }); + }).AllowAnonymous(); + + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks("/healthz").AllowAnonymous(); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks("/alive", new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }).AllowAnonymous(); + return app; } @@ -177,11 +178,27 @@ private static void Setup(IGenocsBuilder builder) Console.ForegroundColor = current; // Add the health checks + // Add health checks to the application + // Since the health checks is item potent, we can add it multiple times + // Add a default liveness check to ensure app is responsive builder.Services - .AddHealthChecks(); - - // .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); // Add a default liveness check to ensure app is responsive - + .AddHealthChecks() + .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); + + /* + * This is an example of how to add a MongoDB health check. + * Please note that you need to install the NuGet package AspNetCore.HealthChecks.MongoDb + * + .AddMongoDb( + builder.Configuration.GetSection("DBSettings:HealthConnectionString").Value!, + builder.Configuration.GetSection("DBSettings:Database").Value!, + name: "mongodb", + timeout: TimeSpan.FromSeconds(10), + tags: ["live"]); + + */ + + // Set the memory cache as default. builder.Services.AddMemoryCache(); builder.Services.AddSingleton(); diff --git a/src/Genocs.Core/Builders/GenocsBuilder.cs b/src/Genocs.Core/Builders/GenocsBuilder.cs index 8be14b99..cd9e2bac 100644 --- a/src/Genocs.Core/Builders/GenocsBuilder.cs +++ b/src/Genocs.Core/Builders/GenocsBuilder.cs @@ -21,14 +21,22 @@ public sealed class GenocsBuilder : IGenocsBuilder /// public IConfiguration? Configuration { get; private set; } + /// + /// The web application builder. + /// public WebApplicationBuilder? WebApplicationBuilder { get; private set; } + /// + /// The Genocs builder constructor. + /// + /// The service collection. + /// The IConfiguration. private GenocsBuilder(IServiceCollection services, IConfiguration? configuration) { _services = services; Configuration = configuration; - _buildActions = new List>(); + _buildActions = []; _services.AddSingleton(new StartupInitializer()); } @@ -38,7 +46,7 @@ private GenocsBuilder(WebApplicationBuilder builder) Configuration = builder.Configuration; _services = builder.Services; - _buildActions = new List>(); + _buildActions = []; _services.AddSingleton(new StartupInitializer()); } @@ -70,6 +78,13 @@ public void AddInitializer() startupInitializer.AddInitializer(initializer); }); + /// + /// Build the Genocs application. + /// + /// + /// Remember to call the Build on the application builder. + /// + /// The Service Provider to be used for chaining. public IServiceProvider Build() { var serviceProvider = _services.BuildServiceProvider(); diff --git a/src/Genocs.Core/Genocs.Core.csproj b/src/Genocs.Core/Genocs.Core.csproj index 3d789e37..7beff245 100644 --- a/src/Genocs.Core/Genocs.Core.csproj +++ b/src/Genocs.Core/Genocs.Core.csproj @@ -12,7 +12,7 @@ Nocco Giovanni Emanuele microservice microservices solid solid-principles genocs README_NUGET.md - Updated to NET8 + Upgraded to NET9.0 True latest diff --git a/src/Genocs.Discovery.Consul/Genocs.Discovery.Consul.csproj b/src/Genocs.Discovery.Consul/Genocs.Discovery.Consul.csproj index 1b289f3d..92830b94 100644 --- a/src/Genocs.Discovery.Consul/Genocs.Discovery.Consul.csproj +++ b/src/Genocs.Discovery.Consul/Genocs.Discovery.Consul.csproj @@ -5,14 +5,14 @@ Genocs.Discovery.Consul Genocs.Discovery.Consul Genocs.Discovery.Consul - The service discovery by Consul library useful to build .NET Core projects. - The service discovery by Consul library useful to build .NET Core projects. + The service discovery library powered by Consul. + The service discovery library powered by Consul. true 5.0.0 Nocco Giovanni Emanuele - microservice microservices solid solid-principles genocs service-discovery + service discovery service-discovery genocs microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.HTTP.RestEase/Genocs.HTTP.RestEase.csproj b/src/Genocs.HTTP.RestEase/Genocs.HTTP.RestEase.csproj index fbd22554..b6e9c27e 100644 --- a/src/Genocs.HTTP.RestEase/Genocs.HTTP.RestEase.csproj +++ b/src/Genocs.HTTP.RestEase/Genocs.HTTP.RestEase.csproj @@ -5,14 +5,14 @@ Genocs.HTTP.RestEase Genocs.HTTP.RestEase Genocs.HTTP.RestEase - The http support library useful to build .NET Core projects. - The http support library useful to build .NET Core projects. + The http support library. + The http support library. true 5.0.0 Nocco Giovanni Emanuele - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + aggregate dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest @@ -32,7 +32,6 @@ - diff --git a/src/Genocs.HTTP/Genocs.HTTP.csproj b/src/Genocs.HTTP/Genocs.HTTP.csproj index bba35e2c..c43c914e 100644 --- a/src/Genocs.HTTP/Genocs.HTTP.csproj +++ b/src/Genocs.HTTP/Genocs.HTTP.csproj @@ -5,14 +5,14 @@ Genocs.HTTP Genocs.HTTP Genocs.HTTP - The http support library useful to build .NET Core projects. - The http support library useful to build .NET Core projects. + The http client library. + The http client library. true 5.0.0 Nocco Giovanni Emanuele - microservice microservices solid solid-principles genocs http-client + http http-client genocs microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.HTTP/GenocsHttpClient.cs b/src/Genocs.HTTP/GenocsHttpClient.cs index 7ccefdad..46fbc08e 100644 --- a/src/Genocs.HTTP/GenocsHttpClient.cs +++ b/src/Genocs.HTTP/GenocsHttpClient.cs @@ -45,7 +45,7 @@ public GenocsHttpClient( public virtual Task GetAsync(string uri) => SendAsync(uri, Method.Get); - public virtual Task GetAsync(string uri, IHttpClientSerializer? serializer = null) + public virtual Task GetAsync(string uri, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Get, serializer: serializer); public Task> GetResultAsync(string uri, IHttpClientSerializer? serializer = null) @@ -57,10 +57,10 @@ public virtual Task PostAsync(string uri, object? data = nu public Task PostAsync(string uri, HttpContent content) => SendAsync(uri, Method.Post, content); - public virtual Task PostAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) + public virtual Task PostAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Post, GetJsonPayload(data, serializer)); - public Task PostAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null) + public Task PostAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Post, content, serializer); public Task> PostResultAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) @@ -75,10 +75,10 @@ public virtual Task PutAsync(string uri, object? data = nul public Task PutAsync(string uri, HttpContent content) => SendAsync(uri, Method.Put, content); - public virtual Task PutAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) + public virtual Task PutAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Put, GetJsonPayload(data, serializer), serializer); - public Task PutAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null) + public Task PutAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Put, content, serializer); public Task> PutResultAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) @@ -93,10 +93,10 @@ public Task PatchAsync(string uri, object? data = null, IHt public Task PatchAsync(string uri, HttpContent content) => SendAsync(uri, Method.Patch, content); - public Task PatchAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) + public Task PatchAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Patch, GetJsonPayload(data, serializer)); - public Task PatchAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null) + public Task PatchAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Patch, content, serializer); public Task> PatchResultAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null) @@ -108,7 +108,7 @@ public Task> PatchResultAsync(string uri, HttpContent content, public virtual Task DeleteAsync(string uri) => SendAsync(uri, Method.Delete); - public Task DeleteAsync(string uri, IHttpClientSerializer? serializer = null) + public Task DeleteAsync(string uri, IHttpClientSerializer? serializer = null) => SendAsync(uri, Method.Delete, serializer: serializer); public Task> DeleteResultAsync(string uri, IHttpClientSerializer? serializer = null) @@ -119,19 +119,22 @@ public Task SendAsync(HttpRequestMessage request) .WaitAndRetryAsync(_settings.Retries, r => TimeSpan.FromSeconds(Math.Pow(2, r))) .ExecuteAsync(() => _client.SendAsync(request)); - public Task SendAsync(HttpRequestMessage request, IHttpClientSerializer? serializer = null) + public Task SendAsync(HttpRequestMessage request, IHttpClientSerializer? serializer = null) => Policy.Handle() .WaitAndRetryAsync(_settings.Retries, r => TimeSpan.FromSeconds(Math.Pow(2, r))) .ExecuteAsync(async () => { + // Send the HTTP request var response = await _client.SendAsync(request); + + // Check if the response indicates a successful status code if (!response.IsSuccessStatusCode) { - return default!; + // If not successful, throw an exception so the retry will come. + throw new HttpRequestException($"The HTTP request failed with status code {response.StatusCode}."); } var stream = await response.Content.ReadAsStreamAsync(); - return await DeserializeJsonFromStream(stream, serializer); }); @@ -173,7 +176,7 @@ public void SetHeaders(IDictionary headers) public void SetHeaders(Action headers) => headers?.Invoke(_client.DefaultRequestHeaders); - protected virtual async Task SendAsync(string uri, Method method, HttpContent? content = null, IHttpClientSerializer? serializer = null) + protected virtual async Task SendAsync(string uri, Method method, HttpContent? content = null, IHttpClientSerializer? serializer = null) { var response = await SendAsync(uri, method, content); if (!response.IsSuccessStatusCode) @@ -186,11 +189,7 @@ protected virtual async Task SendAsync(string uri, Method method, HttpCont return await DeserializeJsonFromStream(stream, serializer); } - protected virtual async Task> SendResultAsync( - string uri, - Method method, - HttpContent? content = null, - IHttpClientSerializer? serializer = null) + protected virtual async Task> SendResultAsync(string uri, Method method, HttpContent? content = null, IHttpClientSerializer? serializer = null) { var response = await SendAsync(uri, method, content); if (!response.IsSuccessStatusCode) @@ -207,11 +206,18 @@ protected virtual async Task> SendResultAsync( protected virtual Task SendAsync(string uri, Method method, HttpContent? content = null) => Policy.Handle() .WaitAndRetryAsync(_settings.Retries, r => TimeSpan.FromSeconds(Math.Pow(2, r))) - .ExecuteAsync(() => + .ExecuteAsync(async () => { string requestUri = uri.StartsWith("http") ? uri : $"http://{uri}"; - return GetResponseAsync(requestUri, method, content); + var result = await GetResponseAsync(requestUri, method, content) ?? throw new HttpRequestException("The HTTP request failed."); + + if (!result.IsSuccessStatusCode) + { + throw new Exception($"The HTTP request failed with status code {result.StatusCode}."); + } + + return result; }); protected virtual Task GetResponseAsync(string uri, Method method, HttpContent? content = null) diff --git a/src/Genocs.HTTP/GenocsHttpLoggingFilter.cs b/src/Genocs.HTTP/GenocsHttpLoggingFilter.cs index 8ba7d583..dc20fb95 100644 --- a/src/Genocs.HTTP/GenocsHttpLoggingFilter.cs +++ b/src/Genocs.HTTP/GenocsHttpLoggingFilter.cs @@ -5,29 +5,21 @@ namespace Genocs.HTTP; // Credits goes to https://www.stevejgordon.co.uk/httpclientfactory-asp-net-core-logging -internal sealed class GenocsHttpLoggingFilter : IHttpMessageHandlerBuilderFilter +internal sealed class GenocsHttpLoggingFilter(ILoggerFactory loggerFactory, HttpClientOptions options) : IHttpMessageHandlerBuilderFilter { - private readonly ILoggerFactory _loggerFactory; - private readonly HttpClientOptions _options; - - public GenocsHttpLoggingFilter(ILoggerFactory loggerFactory, HttpClientOptions options) - { - _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); - _options = options; - } + private readonly ILoggerFactory _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); + private readonly HttpClientOptions _options = options; public Action Configure(Action next) { - if (next is null) - { - throw new ArgumentNullException(nameof(next)); - } + return next is null + ? throw new ArgumentNullException(nameof(next)) + : (builder => + { + next(builder); - return builder => - { - next(builder); - var logger = _loggerFactory.CreateLogger($"System.Net.Http.HttpClient.{builder.Name}.LogicalHandler"); - builder.AdditionalHandlers.Insert(0, new GenocsLoggingScopeHttpMessageHandler(logger, _options)); - }; + var logger = _loggerFactory.CreateLogger($"System.Net.Http.HttpClient.{builder.Name}.LogicalHandler"); + builder.AdditionalHandlers.Insert(0, new GenocsLoggingScopeHttpMessageHandler(logger, _options)); + }); } } \ No newline at end of file diff --git a/src/Genocs.HTTP/GenocsLoggingScopeHttpMessageHandler.cs b/src/Genocs.HTTP/GenocsLoggingScopeHttpMessageHandler.cs index 468ab49d..5b044f60 100644 --- a/src/Genocs.HTTP/GenocsLoggingScopeHttpMessageHandler.cs +++ b/src/Genocs.HTTP/GenocsLoggingScopeHttpMessageHandler.cs @@ -14,8 +14,9 @@ public GenocsLoggingScopeHttpMessageHandler(ILogger logger, HttpClientOptions se { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _ = settings ?? throw new ArgumentNullException(nameof(settings)); - _maskedRequestUrlParts = - new HashSet(settings.RequestMasking?.UrlParts ?? Enumerable.Empty()); + + _maskedRequestUrlParts = [.. settings.RequestMasking?.UrlParts ?? []]; + _maskTemplate = string.IsNullOrWhiteSpace(settings.RequestMasking?.MaskTemplate) ? "*****" : settings.RequestMasking.MaskTemplate; @@ -23,10 +24,7 @@ public GenocsLoggingScopeHttpMessageHandler(ILogger logger, HttpClientOptions se protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - if (request is null) - { - throw new ArgumentNullException(nameof(request)); - } + ArgumentNullException.ThrowIfNull(request); using (Log.BeginRequestPipelineScope(_logger, request, _maskedRequestUrlParts, _maskTemplate)) { diff --git a/src/Genocs.HTTP/ICorrelationContextFactory.cs b/src/Genocs.HTTP/ICorrelationContextFactory.cs index 1e042c08..378f2f11 100644 --- a/src/Genocs.HTTP/ICorrelationContextFactory.cs +++ b/src/Genocs.HTTP/ICorrelationContextFactory.cs @@ -1,6 +1,13 @@ namespace Genocs.HTTP; +/// +/// The CorrelationContext Factory interface. +/// public interface ICorrelationContextFactory { + /// + /// Create a correlationId. + /// + /// The correlationId just created. string Create(); } \ No newline at end of file diff --git a/src/Genocs.HTTP/ICorrelationIdFactory.cs b/src/Genocs.HTTP/ICorrelationIdFactory.cs index 099e8075..d67042e3 100644 --- a/src/Genocs.HTTP/ICorrelationIdFactory.cs +++ b/src/Genocs.HTTP/ICorrelationIdFactory.cs @@ -1,7 +1,7 @@ namespace Genocs.HTTP; /// -/// Generic CorrelationId Factory interface. +/// The CorrelationId Factory interface. /// public interface ICorrelationIdFactory { diff --git a/src/Genocs.HTTP/IHttpClient.cs b/src/Genocs.HTTP/IHttpClient.cs index 568bae1e..ebc2be53 100644 --- a/src/Genocs.HTTP/IHttpClient.cs +++ b/src/Genocs.HTTP/IHttpClient.cs @@ -8,39 +8,47 @@ namespace Genocs.HTTP; public interface IHttpClient { Task GetAsync(string uri); - Task GetAsync(string uri, IHttpClientSerializer? serializer = null); + Task GetAsync(string uri, IHttpClientSerializer? serializer = null); Task> GetResultAsync(string uri, IHttpClientSerializer? serializer = null); Task PostAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null); Task PostAsync(string uri, HttpContent content); - Task PostAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null); - Task PostAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null); + Task PostAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null); + Task PostAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null); Task> PostResultAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null); Task> PostResultAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null); Task PutAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null); Task PutAsync(string uri, HttpContent content); - Task PutAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null); - Task PutAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null); + Task PutAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null); + Task PutAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null); Task> PutResultAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null); Task> PutResultAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null); Task PatchAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null); Task PatchAsync(string uri, HttpContent content); - Task PatchAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null); - Task PatchAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null); + Task PatchAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null); + Task PatchAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null); Task> PatchResultAsync(string uri, object? data = null, IHttpClientSerializer? serializer = null); Task> PatchResultAsync(string uri, HttpContent content, IHttpClientSerializer? serializer = null); Task DeleteAsync(string uri); - Task DeleteAsync(string uri, IHttpClientSerializer? serializer = null); + Task DeleteAsync(string uri, IHttpClientSerializer? serializer = null); Task> DeleteResultAsync(string uri, IHttpClientSerializer? serializer = null); Task SendAsync(HttpRequestMessage request); - Task SendAsync(HttpRequestMessage request, IHttpClientSerializer? serializer = null); /// /// Send the request and return the result. /// /// The type to be send. - /// - /// - /// + /// The request. + /// The serializer. + /// The return object. + Task SendAsync(HttpRequestMessage request, IHttpClientSerializer? serializer = null); + + /// + /// Send the request and return the result. + /// + /// The type to be send. + /// The request. + /// The serializer. + /// The HttpResult object. Task> SendResultAsync(HttpRequestMessage request, IHttpClientSerializer? serializer = null); /// diff --git a/src/Genocs.LoadBalancing.Fabio/Genocs.LoadBalancing.Fabio.csproj b/src/Genocs.LoadBalancing.Fabio/Genocs.LoadBalancing.Fabio.csproj index 0f2f27a5..1848deb8 100644 --- a/src/Genocs.LoadBalancing.Fabio/Genocs.LoadBalancing.Fabio.csproj +++ b/src/Genocs.LoadBalancing.Fabio/Genocs.LoadBalancing.Fabio.csproj @@ -5,14 +5,14 @@ Genocs.LoadBalancing.Fabio Genocs.LoadBalancing.Fabio Genocs.LoadBalancing.Fabio - The load balacer based on Fabio library useful to build .NET Core projects. - The load balacer based on Fabio library useful to build .NET Core projects. + The load balacer based on Fabio. + The load balacer based on Fabio. true 5.0.0 Nocco Giovanni Emanuele - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + load balancer load-balancer design-patterns dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.Logging/Configurations/AzureOptions.cs b/src/Genocs.Logging/Configurations/AzureOptions.cs index 81af694c..47d0d4e5 100644 --- a/src/Genocs.Logging/Configurations/AzureOptions.cs +++ b/src/Genocs.Logging/Configurations/AzureOptions.cs @@ -6,7 +6,7 @@ namespace Genocs.Logging.Configurations; public class AzureOptions { /// - /// It define whether the Azure application insights logger and tracing are enabled or not. + /// It defines whether the Azure application insights logger and tracing are enabled or not. /// public bool Enabled { get; set; } @@ -15,14 +15,13 @@ public class AzureOptions /// public string? ConnectionString { get; set; } - /// - /// It define whether the Azure application insights logger and tracing are enabled or not. + /// It defines whether the Azure application insights logger and tracing are enabled or not. /// public bool EnableTracing { get; set; } /// - /// It define whether the Azure application insights logger and metrics are enabled or not. + /// It defines whether the Azure application insights logger and metrics are enabled or not. /// public bool EnableMetrics { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Logging/Configurations/ConsoleOptions.cs b/src/Genocs.Logging/Configurations/ConsoleOptions.cs index 4112f484..383d3634 100644 --- a/src/Genocs.Logging/Configurations/ConsoleOptions.cs +++ b/src/Genocs.Logging/Configurations/ConsoleOptions.cs @@ -6,17 +6,22 @@ namespace Genocs.Logging.Configurations; public class ConsoleOptions { /// - /// It define whether the console logger and tracing are enabled or not. + /// It defines whether the console logger and tracing are enabled or not. /// public bool Enabled { get; set; } /// - /// It define whether the console logger and tracing are enabled or not. + /// It defines whether the console logger will use structured logging or not. + /// + public bool EnableStructured { get; set; } + + /// + /// It defines whether the console logger and tracing are enabled or not. /// public bool EnableTracing { get; set; } /// - /// It define whether the console logger and metrics are enabled or not. + /// It defines whether the console logger and metrics are enabled or not. /// public bool EnableMetrics { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Logging/Configurations/ElkOptions.cs b/src/Genocs.Logging/Configurations/ElkOptions.cs index b090e0ed..9bb043ae 100644 --- a/src/Genocs.Logging/Configurations/ElkOptions.cs +++ b/src/Genocs.Logging/Configurations/ElkOptions.cs @@ -6,12 +6,12 @@ namespace Genocs.Logging.Configurations; public class ElkOptions { /// - /// It define whether the Elasticsearch logger and tracing are enabled or not. + /// It defines whether the Elasticsearch logger and tracing are enabled or not. /// public bool Enabled { get; set; } /// - /// It define whether the Elasticsearch authentication is enabled or not. + /// It defines whether the Elasticsearch authentication is enabled or not. /// public bool BasicAuthEnabled { get; set; } diff --git a/src/Genocs.Logging/Configurations/LoggerOptions.cs b/src/Genocs.Logging/Configurations/LoggerOptions.cs index 59afbad2..fbe3503a 100644 --- a/src/Genocs.Logging/Configurations/LoggerOptions.cs +++ b/src/Genocs.Logging/Configurations/LoggerOptions.cs @@ -17,6 +17,11 @@ public class LoggerOptions public string? Level { get; set; } + /// + /// It defines the OpenTelemetry exporter endpoint. In case you are using Serilog with OpenTelemetry. + /// + public string? OtlpEndpoint { get; set; } + /// /// The Console Logging and tracing Settings. /// diff --git a/src/Genocs.Logging/Configurations/MongoOptions.cs b/src/Genocs.Logging/Configurations/MongoOptions.cs index c2750e18..e8c5b384 100644 --- a/src/Genocs.Logging/Configurations/MongoOptions.cs +++ b/src/Genocs.Logging/Configurations/MongoOptions.cs @@ -6,7 +6,7 @@ namespace Genocs.Logging.Configurations; public class MongoOptions { /// - /// It define whether the MongoDb logger and tracing are enabled or not. + /// It defines whether the MongoDb logger and tracing are enabled or not. /// public bool Enabled { get; set; } } \ No newline at end of file diff --git a/src/Genocs.Logging/Configurations/SeqOptions.cs b/src/Genocs.Logging/Configurations/SeqOptions.cs index 7e20ccb1..2508d13e 100644 --- a/src/Genocs.Logging/Configurations/SeqOptions.cs +++ b/src/Genocs.Logging/Configurations/SeqOptions.cs @@ -6,7 +6,7 @@ namespace Genocs.Logging.Configurations; public class SeqOptions { /// - /// It define whether the Seq logger and tracing are enabled or not. + /// It defines whether the Seq logger and tracing are enabled or not. /// public bool Enabled { get; set; } diff --git a/src/Genocs.Logging/Extensions.cs b/src/Genocs.Logging/Extensions.cs index 41997920..34a1b8ee 100644 --- a/src/Genocs.Logging/Extensions.cs +++ b/src/Genocs.Logging/Extensions.cs @@ -9,8 +9,11 @@ using Microsoft.Extensions.Hosting; using Serilog; using Serilog.Core; +using Serilog.Enrichers.Span; using Serilog.Events; +using Serilog.Exceptions; using Serilog.Filters; +using Serilog.Formatting.Compact; using Serilog.Sinks.Elasticsearch; using Serilog.Sinks.Grafana.Loki; @@ -28,23 +31,23 @@ public static IHostBuilder UseLogging( => hostBuilder .ConfigureServices(services => services.AddSingleton()) .UseSerilog((context, loggerConfiguration) => - { - if (string.IsNullOrWhiteSpace(loggerSectionName)) { - loggerSectionName = LoggerOptions.Position; - } + if (string.IsNullOrWhiteSpace(loggerSectionName)) + { + loggerSectionName = LoggerOptions.Position; + } - if (string.IsNullOrWhiteSpace(appSectionName)) - { - appSectionName = AppOptions.Position; - } + if (string.IsNullOrWhiteSpace(appSectionName)) + { + appSectionName = AppOptions.Position; + } - var loggerOptions = context.Configuration.GetOptions(loggerSectionName); - var appOptions = context.Configuration.GetOptions(appSectionName); + var loggerOptions = context.Configuration.GetOptions(loggerSectionName); + var appOptions = context.Configuration.GetOptions(appSectionName); - MapOptions(loggerOptions, appOptions, loggerConfiguration, context.HostingEnvironment.EnvironmentName); - configure?.Invoke(context, loggerConfiguration); - }); + MapOptions(loggerOptions, appOptions, loggerConfiguration, context.HostingEnvironment.EnvironmentName); + configure?.Invoke(context, loggerConfiguration); + }); public static IEndpointConventionBuilder MapLogLevelHandler( this IEndpointRouteBuilder builder, @@ -64,7 +67,12 @@ private static void MapOptions( .Enrich.WithProperty("Environment", environmentName) .Enrich.WithProperty("Application", appOptions.Service) .Enrich.WithProperty("Instance", appOptions.Instance) - .Enrich.WithProperty("Version", appOptions.Version); + .Enrich.WithProperty("Version", appOptions.Version) + .Enrich.WithExceptionDetails() + .Enrich.WithMachineName() + .Enrich.WithProcessId() + .Enrich.WithThreadId() + .Enrich.WithSpan(); foreach (var (key, value) in loggerOptions.Tags ?? new Dictionary()) { @@ -95,10 +103,26 @@ private static void Configure(LoggerConfiguration loggerConfiguration, LoggerOpt var lokiOptions = options.Loki ?? new LokiOptions(); var azureOptions = options.Azure ?? new AzureOptions(); + // OpenTelemetry setup + if (!string.IsNullOrWhiteSpace(options.OtlpEndpoint)) + { + loggerConfiguration.WriteTo.OpenTelemetry(wt => + { + wt.Endpoint = options.OtlpEndpoint; + }); + } + // console if (consoleOptions.Enabled) { - loggerConfiguration.WriteTo.Console(); + if (consoleOptions.EnableStructured) + { + loggerConfiguration.WriteTo.Console(new RenderedCompactJsonFormatter()); + } + else + { + loggerConfiguration.WriteTo.Async(wt => wt.Console()); + } } // local file system diff --git a/src/Genocs.Logging/Genocs.Logging.csproj b/src/Genocs.Logging/Genocs.Logging.csproj index e2d450af..2da279e8 100644 --- a/src/Genocs.Logging/Genocs.Logging.csproj +++ b/src/Genocs.Logging/Genocs.Logging.csproj @@ -5,18 +5,18 @@ Genocs.Logging Genocs.Logging Genocs.Logging - The logging library useful to build .NET Core projects. - The logging library useful to build .NET Core projects. + The logging library. + The logging library. true 5.0.0 Nocco Giovanni Emanuele aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest - + @@ -28,10 +28,18 @@ + + + + + + + + diff --git a/src/Genocs.MessageBrokers.Outbox.MongoDB/Genocs.MessageBrokers.Outbox.MongoDB.csproj b/src/Genocs.MessageBrokers.Outbox.MongoDB/Genocs.MessageBrokers.Outbox.MongoDB.csproj index ce80682a..bfa6cd0f 100644 --- a/src/Genocs.MessageBrokers.Outbox.MongoDB/Genocs.MessageBrokers.Outbox.MongoDB.csproj +++ b/src/Genocs.MessageBrokers.Outbox.MongoDB/Genocs.MessageBrokers.Outbox.MongoDB.csproj @@ -10,9 +10,9 @@ true 5.0.0 Nocco Giovanni Emanuele - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.MessageBrokers.Outbox/Genocs.MessageBrokers.Outbox.csproj b/src/Genocs.MessageBrokers.Outbox/Genocs.MessageBrokers.Outbox.csproj index fad347ca..0c9260d5 100644 --- a/src/Genocs.MessageBrokers.Outbox/Genocs.MessageBrokers.Outbox.csproj +++ b/src/Genocs.MessageBrokers.Outbox/Genocs.MessageBrokers.Outbox.csproj @@ -10,9 +10,9 @@ true 5.0.0 Nocco Giovanni Emanuele - aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.MessageBrokers.RabbitMQ/Genocs.MessageBrokers.RabbitMQ.csproj b/src/Genocs.MessageBrokers.RabbitMQ/Genocs.MessageBrokers.RabbitMQ.csproj index ef26b78e..ce6e5953 100644 --- a/src/Genocs.MessageBrokers.RabbitMQ/Genocs.MessageBrokers.RabbitMQ.csproj +++ b/src/Genocs.MessageBrokers.RabbitMQ/Genocs.MessageBrokers.RabbitMQ.csproj @@ -12,7 +12,7 @@ Nocco Giovanni Emanuele aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.MessageBrokers.RabbitMQ/IExceptionToMessageMapper.cs b/src/Genocs.MessageBrokers.RabbitMQ/IExceptionToMessageMapper.cs index 7762afb5..d60d6642 100644 --- a/src/Genocs.MessageBrokers.RabbitMQ/IExceptionToMessageMapper.cs +++ b/src/Genocs.MessageBrokers.RabbitMQ/IExceptionToMessageMapper.cs @@ -2,5 +2,5 @@ namespace Genocs.MessageBrokers.RabbitMQ; public interface IExceptionToMessageMapper { - object Map(Exception exception, object message); + object? Map(Exception exception, object message); } \ No newline at end of file diff --git a/src/Genocs.MessageBrokers/Genocs.MessageBrokers.csproj b/src/Genocs.MessageBrokers/Genocs.MessageBrokers.csproj index d3bf1f90..a0e17770 100644 --- a/src/Genocs.MessageBrokers/Genocs.MessageBrokers.csproj +++ b/src/Genocs.MessageBrokers/Genocs.MessageBrokers.csproj @@ -12,7 +12,7 @@ Nocco Giovanni Emanuele aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.Metrics/AppMetrics/Extensions.cs b/src/Genocs.Metrics/AppMetrics/Extensions.cs index 94e769c3..c36209e3 100644 --- a/src/Genocs.Metrics/AppMetrics/Extensions.cs +++ b/src/Genocs.Metrics/AppMetrics/Extensions.cs @@ -19,20 +19,18 @@ namespace Genocs.Metrics.AppMetrics; public static class Extensions { - private const string MetricsSectionName = "metrics"; - private const string AppSectionName = "app"; private const string RegistryName = "metrics.metrics"; private static bool _initialized; [Description("For the time being it sets Kestrel's AllowSynchronousIO = true, see https://github.com/AppMetrics/AppMetrics/issues/396")] public static IGenocsBuilder AddMetrics( this IGenocsBuilder builder, - string metricsSectionName = MetricsSectionName, - string appSectionName = AppSectionName) + string metricsSectionName = Configurations.MetricsOptions.Position, + string appSectionName = AppOptions.Position) { if (string.IsNullOrWhiteSpace(metricsSectionName)) { - metricsSectionName = MetricsSectionName; + metricsSectionName = Configurations.MetricsOptions.Position; } if (string.IsNullOrWhiteSpace(appSectionName)) @@ -50,11 +48,11 @@ public static IGenocsBuilder AddMetrics( public static IGenocsBuilder AddMetrics( this IGenocsBuilder builder, Func buildOptions, - string appSectionName = AppSectionName) + string appSectionName = AppOptions.Position) { if (string.IsNullOrWhiteSpace(appSectionName)) { - appSectionName = AppSectionName; + appSectionName = AppOptions.Position; } var metricsOptions = buildOptions(new MetricsOptionsBuilder()).Build(); diff --git a/src/Genocs.Metrics/Genocs.Metrics.csproj b/src/Genocs.Metrics/Genocs.Metrics.csproj index b6a8cde2..4335329b 100644 --- a/src/Genocs.Metrics/Genocs.Metrics.csproj +++ b/src/Genocs.Metrics/Genocs.Metrics.csproj @@ -5,14 +5,14 @@ Genocs.Metrics Genocs.Metrics Genocs.Metrics - The metrics interface library useful to build .NET Core projects. - The metrics interface library useful to build .NET Core projects. + The metrics interface library. + The metrics interface library. true 5.0.0 Nocco Giovanni Emanuele aggregate architecture boilerplate ddd ddd-architecture design-patterns domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.OpenTelemetry/Configurations/AzureOptions.cs b/src/Genocs.OpenTelemetry/Configurations/AzureOptions.cs new file mode 100644 index 00000000..f54652d1 --- /dev/null +++ b/src/Genocs.OpenTelemetry/Configurations/AzureOptions.cs @@ -0,0 +1,32 @@ +namespace Genocs.GnxOpenTelemetry.Configurations; + +/// +/// ConsoleOptions Settings. +/// +public class AzureOptions +{ + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + /// + /// It defines the azure Connection string. + /// + public string? ConnectionString { get; set; } + + /// + /// It defines whether the console tracing are enabled or not. + /// + public bool EnableTracing { get; set; } + + /// + /// It defines whether the console metrics are enabled or not. + /// + public bool EnableMetrics { get; set; } + + /// + /// It defines whether the console logging are enabled or not. + /// + public bool EnableLogging { get; set; } +} \ No newline at end of file diff --git a/src/Genocs.OpenTelemetry/Configurations/ConsoleOptions.cs b/src/Genocs.OpenTelemetry/Configurations/ConsoleOptions.cs new file mode 100644 index 00000000..b20a9aae --- /dev/null +++ b/src/Genocs.OpenTelemetry/Configurations/ConsoleOptions.cs @@ -0,0 +1,27 @@ +namespace Genocs.GnxOpenTelemetry.Configurations; + +/// +/// ConsoleOptions Settings. +/// +public class ConsoleOptions +{ + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + /// + /// It defines whether the console tracing are enabled or not. + /// + public bool EnableTracing { get; set; } + + /// + /// It defines whether the console metrics are enabled or not. + /// + public bool EnableMetrics { get; set; } + + /// + /// It defines whether the console logging are enabled or not. + /// + public bool EnableLogging { get; set; } +} \ No newline at end of file diff --git a/src/Genocs.OpenTelemetry/Configurations/OpenTelemetryOptions.cs b/src/Genocs.OpenTelemetry/Configurations/OpenTelemetryOptions.cs new file mode 100644 index 00000000..3e96a949 --- /dev/null +++ b/src/Genocs.OpenTelemetry/Configurations/OpenTelemetryOptions.cs @@ -0,0 +1,30 @@ +namespace Genocs.GnxOpenTelemetry.Configurations; + +/// +/// OpenTelemetry Settings. +/// +public class OpenTelemetryOptions +{ + /// + /// Default section name. + /// + public const string Position = "openTelemetry"; + + /// + /// It defines whether the section is enabled or not. + /// + /// + public bool Enabled { get; set; } + + public OtlpExportOptions? Exporter { get; set; } + + /// + /// Console OpenTelemetry settings. + /// + public ConsoleOptions? Console { get; set; } + + /// + /// Azure OpenTelemetry settings. + /// + public AzureOptions? Azure { get; set; } +} diff --git a/src/Genocs.OpenTelemetry/Configurations/OtlpExportOptions.cs b/src/Genocs.OpenTelemetry/Configurations/OtlpExportOptions.cs new file mode 100644 index 00000000..12ae4f41 --- /dev/null +++ b/src/Genocs.OpenTelemetry/Configurations/OtlpExportOptions.cs @@ -0,0 +1,34 @@ +namespace Genocs.GnxOpenTelemetry.Configurations; + +/// +/// OtlpExportOptions Settings. +/// +public class OtlpExportOptions +{ + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { get; set; } + + /// + /// The Otlp Exporter endpoint. + /// + public string? OtlpEndpoint { get; set; } + + /// + /// The used OtlpExportProtocol. + /// IT could be [Grpc|HttpProtobuf]. + /// + public string Protocol { get; set; } = "Grpc"; + + /// + /// The used ExportProcessorType. + /// It could be [Simple|Batch]. + /// + public string ProcessorType { get; set; } = "Batch"; + + public int MaxQueueSize { get; set; } = 2048; + public int ScheduledDelayMilliseconds { get; set; } = 5000; + public int ExporterTimeoutMilliseconds { get; set; } = 30000; + public int MaxExportBatchSize { get; set; } = 512; +} \ No newline at end of file diff --git a/src/Genocs.OpenTelemetry/Extensions.cs b/src/Genocs.OpenTelemetry/Extensions.cs new file mode 100644 index 00000000..81f4ca5a --- /dev/null +++ b/src/Genocs.OpenTelemetry/Extensions.cs @@ -0,0 +1,213 @@ +using Azure.Monitor.OpenTelemetry.Exporter; +using Genocs.Common.Configurations; +using Genocs.Core.Builders; +using Genocs.Core.Exceptions; +using Genocs.GnxOpenTelemetry.Configurations; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using OpenTelemetry; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +namespace Genocs.GnxOpenTelemetry; + +public static class OpenTelemetryExtensions +{ + public static IGenocsBuilder AddOpenTelemetry( + this IGenocsBuilder builder) + { + AppOptions appOptions = builder.GetOptions(AppOptions.Position) + ?? throw new GenocsException.InvalidConfigurationException("app config section is missing. AddOpenTelemetry requires those configuration."); + + // No OpenTelemetryTracing in case of missing ServiceName + if (string.IsNullOrWhiteSpace(appOptions.Service)) + { + return builder; + } + + OpenTelemetryOptions? openTelemetryOptions = builder.GetOptions(OpenTelemetryOptions.Position); + + builder.Services.AddOpenTelemetry() + .ConfigureResource(resource => resource.AddService(serviceName: appOptions.Service)) + .WithMetrics(metrics => + { + // Setup standard instrumentations + metrics + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + + // Setup the exporter + if (openTelemetryOptions.Exporter?.Enabled == true) + { + metrics + .AddOtlpExporter(otlpOptions => + { + otlpOptions.Endpoint = new Uri(openTelemetryOptions.Exporter.OtlpEndpoint!); + + // Check if Jaeger is enabled + if (openTelemetryOptions.Exporter?.Enabled == true) + { + // Parse enum + otlpOptions.Protocol = Enum.Parse(openTelemetryOptions.Exporter.Protocol); + otlpOptions.ExportProcessorType = Enum.Parse(openTelemetryOptions.Exporter.ProcessorType); + + // Check if Batch Exporter before setting options + otlpOptions.BatchExportProcessorOptions = new BatchExportProcessorOptions + { + MaxQueueSize = openTelemetryOptions.Exporter.MaxQueueSize, + ScheduledDelayMilliseconds = openTelemetryOptions.Exporter.ScheduledDelayMilliseconds, + ExporterTimeoutMilliseconds = openTelemetryOptions.Exporter.ExporterTimeoutMilliseconds, + MaxExportBatchSize = openTelemetryOptions.Exporter.MaxExportBatchSize + }; + } + }); + } + + // Setup Console exporter + if (openTelemetryOptions.Console?.Enabled == true && openTelemetryOptions.Console.EnableMetrics) + { + // you should add OpenTelemetry.Exporter.Console NuGet package + metrics.AddConsoleExporter(); + } + + // Setup Azure exporter + if (openTelemetryOptions.Azure?.Enabled == true && openTelemetryOptions.Azure.EnableMetrics) + { + // you should add OpenTelemetry.Exporter.Console NuGet package + metrics.AddAzureMonitorMetricExporter(o => + { + o.ConnectionString = openTelemetryOptions.Azure.ConnectionString; + }); + } + }) + .WithTracing(tracing => + { + tracing + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation(); + + // Setup the exporter + if (openTelemetryOptions.Exporter?.Enabled == true) + { + tracing + .AddOtlpExporter(otlpOptions => + { + otlpOptions.Endpoint = new Uri(openTelemetryOptions.Exporter.OtlpEndpoint!); + + // Check if Jaeger is enabled + if (openTelemetryOptions.Exporter?.Enabled == true) + { + // Parse enum + otlpOptions.Protocol = Enum.Parse(openTelemetryOptions.Exporter.Protocol); + otlpOptions.ExportProcessorType = Enum.Parse(openTelemetryOptions.Exporter.ProcessorType); + + // Check if Batch Exporter before setting options + otlpOptions.BatchExportProcessorOptions = new BatchExportProcessorOptions + { + MaxQueueSize = openTelemetryOptions.Exporter.MaxQueueSize, + ScheduledDelayMilliseconds = openTelemetryOptions.Exporter.ScheduledDelayMilliseconds, + ExporterTimeoutMilliseconds = openTelemetryOptions.Exporter.ExporterTimeoutMilliseconds, + MaxExportBatchSize = openTelemetryOptions.Exporter.MaxExportBatchSize + }; + } + }); + } + + // Setup Console exporter + if (openTelemetryOptions.Console?.Enabled == true && openTelemetryOptions.Console.EnableTracing) + { + // you should add OpenTelemetry.Exporter.Console NuGet package + tracing.AddConsoleExporter(); + } + + // Setup Azure exporter + if (openTelemetryOptions.Azure?.Enabled == true && openTelemetryOptions.Azure.EnableTracing) + { + // you should add Azure.Monitor.OpenTelemetry.Exporter NuGet package + tracing.AddAzureMonitorTraceExporter(o => + { + o.ConnectionString = openTelemetryOptions.Azure.ConnectionString; + }); + } + }); + + // Add the OpenTelemetry logging provider + builder.WebApplicationBuilder?.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + + // Setup the exporter + if (openTelemetryOptions.Exporter?.Enabled == true) + { + logging.AddOtlpExporter(otlpOptions => + { + otlpOptions.Endpoint = new Uri(openTelemetryOptions.Exporter.OtlpEndpoint!); + + // Check if Jaeger is enabled + if (openTelemetryOptions.Exporter?.Enabled == true) + { + // Parse enum + otlpOptions.Protocol = Enum.Parse(openTelemetryOptions.Exporter.Protocol); + otlpOptions.ExportProcessorType = Enum.Parse(openTelemetryOptions.Exporter.ProcessorType); + + // Check if Batch Exporter before setting options + otlpOptions.BatchExportProcessorOptions = new BatchExportProcessorOptions + { + MaxQueueSize = openTelemetryOptions.Exporter.MaxQueueSize, + ScheduledDelayMilliseconds = openTelemetryOptions.Exporter.ScheduledDelayMilliseconds, + ExporterTimeoutMilliseconds = openTelemetryOptions.Exporter.ExporterTimeoutMilliseconds, + MaxExportBatchSize = openTelemetryOptions.Exporter.MaxExportBatchSize + }; + } + }); + } + + // Setup Console exporter + if (openTelemetryOptions.Console?.Enabled == true && openTelemetryOptions.Console.EnableLogging) + { + // you should add OpenTelemetry.Exporter.Console NuGet package + logging.AddConsoleExporter(); + } + + // Setup Azure exporter + if (openTelemetryOptions.Azure?.Enabled == true && openTelemetryOptions.Azure.EnableLogging) + { + // you should add Azure.Monitor.OpenTelemetry.Exporter NuGet package + logging.AddAzureMonitorLogExporter(o => + { + o.ConnectionString = openTelemetryOptions.Azure.ConnectionString; + }); + } + }); + + return builder; + } + + /* + private static void SetupJaegerExporter(OpenTelemetry.Exporter.OtlpExporterOptions provider) + { + provider.AddOtlpExporter(o => + { + o.Endpoint = new Uri(jaegerOptions.Endpoint); + + // Parse enum + o.Protocol = Enum.Parse(jaegerOptions.Protocol); + o.ExportProcessorType = Enum.Parse(jaegerOptions.ProcessorType); + + // Check if Batch Exporter before setting options + o.BatchExportProcessorOptions = new BatchExportProcessorOptions + { + MaxQueueSize = jaegerOptions.MaxQueueSize, + ScheduledDelayMilliseconds = jaegerOptions.ScheduledDelayMilliseconds, + ExporterTimeoutMilliseconds = jaegerOptions.ExporterTimeoutMilliseconds, + MaxExportBatchSize = jaegerOptions.MaxExportBatchSize + }; + }); + } + */ + +} diff --git a/src/Genocs.OpenTelemetry/Genocs.OpenTelemetry.csproj b/src/Genocs.OpenTelemetry/Genocs.OpenTelemetry.csproj new file mode 100644 index 00000000..a0b88a06 --- /dev/null +++ b/src/Genocs.OpenTelemetry/Genocs.OpenTelemetry.csproj @@ -0,0 +1,45 @@ + + + + net9.0;net8.0;net7.0;net6.0 + Genocs.OpenTelemetry + Genocs.OpenTelemetry + Genocs.OpenTelemetry + The Open Telemetry support library. + The Open Telemetry support library. + true + 5.0.0 + Nocco Giovanni Emanuele + open telemetry dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + README_NUGET.md + Upgraded to NET9.0 + True + latest + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Genocs.OpenTelemetry/README_NUGET.md b/src/Genocs.OpenTelemetry/README_NUGET.md new file mode 100644 index 00000000..4d6f6184 --- /dev/null +++ b/src/Genocs.OpenTelemetry/README_NUGET.md @@ -0,0 +1,23 @@ +# Open Telemetry library + +This package contains the component you can use to setup OpenTelementry. + + +## Description + +OpenTelemetry is Open-Source widly adopted solution to implement three components. + +- Tracing +- Metrics +- Logging + +## Getting Started + +To get started, you need to install the package and configure the settings. +``` json + "openTelemetry": { + "enableTracing": true, + "enableMetrics": true, + "enableLogging": true + } +``` diff --git a/src/Genocs.Persistence.MongoDb/Domain/Entities/IMongoDbEntity.cs b/src/Genocs.Persistence.MongoDb/Domain/Entities/IMongoDbEntity.cs index 0f38ca67..e6d19ac0 100644 --- a/src/Genocs.Persistence.MongoDb/Domain/Entities/IMongoDbEntity.cs +++ b/src/Genocs.Persistence.MongoDb/Domain/Entities/IMongoDbEntity.cs @@ -6,7 +6,4 @@ namespace Genocs.Persistence.MongoDb.Domain.Entities; /// /// Default MongoDB entity. /// -public interface IMongoDbEntity : IEntity -{ - -} +public interface IMongoDbEntity : IEntity; diff --git a/src/Genocs.Persistence.MongoDb/Domain/Repositories/MongoDbBaseRepositoryOfType.cs b/src/Genocs.Persistence.MongoDb/Domain/Repositories/MongoDbBaseRepositoryOfType.cs index de19fe26..9d78c0c0 100644 --- a/src/Genocs.Persistence.MongoDb/Domain/Repositories/MongoDbBaseRepositoryOfType.cs +++ b/src/Genocs.Persistence.MongoDb/Domain/Repositories/MongoDbBaseRepositoryOfType.cs @@ -93,7 +93,7 @@ public override TEntity Get(TKey id) /// /// First Or Default entity. /// - /// The domain objjet id. + /// The domain object id. /// The entity if found otherwise null. public override TEntity FirstOrDefault(TKey id) { diff --git a/src/Genocs.Persistence.MongoDb/Extensions/MongoDbExtensions.cs b/src/Genocs.Persistence.MongoDb/Extensions/MongoDbExtensions.cs index aa7eea7a..41569aa1 100644 --- a/src/Genocs.Persistence.MongoDb/Extensions/MongoDbExtensions.cs +++ b/src/Genocs.Persistence.MongoDb/Extensions/MongoDbExtensions.cs @@ -1,6 +1,6 @@ +using System.Reflection; using Genocs.Core.Builders; using Genocs.Core.Domain.Entities; -using Genocs.Persistence.MongoDb.Builders; using Genocs.Persistence.MongoDb.Configurations; using Genocs.Persistence.MongoDb.Domain.Repositories; using Genocs.Persistence.MongoDb.Factories; @@ -11,7 +11,6 @@ using Microsoft.Extensions.DependencyInjection; using MongoDB.Driver; using MongoDB.Driver.Core.Extensions.DiagnosticSources; -using System.Reflection; namespace Genocs.Persistence.MongoDb.Extensions; @@ -29,7 +28,7 @@ public static class MongoDbExtensions /// The Genocs builder. /// The section name. /// The seeder name. - /// Defines if setup the MongoDB Conventions. + /// Defines if setup the MongoDB standard Conventions. /// The Genocs builder. public static IGenocsBuilder AddMongo( this IGenocsBuilder builder, @@ -46,25 +45,6 @@ public static IGenocsBuilder AddMongo( return builder.AddMongo(mongoOptions, seederType, registerConventions); } - /// - /// It allows to add support for MongoDb. - /// - /// The Genocs builder. - /// The Genocs builder. - /// The seeder name. - /// Defines if setup the MongoDB Conventions. - /// The Genocs builder. - public static IGenocsBuilder AddMongo( - this IGenocsBuilder builder, - Func buildOptions, - Type? seederType = null, - bool registerConventions = true) - { - var mongoOptions = buildOptions(new MongoDbOptionsBuilder()).Build(); - return builder.AddMongo(mongoOptions, seederType, registerConventions); - } - /// /// Setup MongoDb support. /// @@ -130,6 +110,7 @@ public static IGenocsBuilder AddMongo( // Setup conventions if (registerConventions && !_conventionsRegistered) { + _conventionsRegistered = true; ServiceCollectionExtensions.RegisterConventions(); } @@ -185,11 +166,17 @@ public static IGenocsBuilder AddMongoFast( builder.Services.Configure(section); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddSingleton(); builder.Services.AddScoped(typeof(IMongoDbRepository<>), typeof(MongoDbRepository<>)); + builder.AddInitializer(); + if (registerConventions && !_conventionsRegistered) { + _conventionsRegistered = true; ServiceCollectionExtensions.RegisterConventions(); } diff --git a/src/Genocs.Persistence.MongoDb/Factories/MongoSessionFactory.cs b/src/Genocs.Persistence.MongoDb/Factories/MongoSessionFactory.cs index faec674e..19e908c2 100644 --- a/src/Genocs.Persistence.MongoDb/Factories/MongoSessionFactory.cs +++ b/src/Genocs.Persistence.MongoDb/Factories/MongoSessionFactory.cs @@ -3,12 +3,9 @@ namespace Genocs.Persistence.MongoDb.Factories; -internal sealed class MongoSessionFactory : IMongoSessionFactory +internal sealed class MongoSessionFactory(IMongoClient client) : IMongoSessionFactory { - private readonly IMongoClient _client; - - public MongoSessionFactory(IMongoClient client) - => _client = client; + private readonly IMongoClient _client = client; public Task CreateAsync() => _client.StartSessionAsync(); diff --git a/src/Genocs.Persistence.MongoDb/Genocs.Persistence.MongoDb.csproj b/src/Genocs.Persistence.MongoDb/Genocs.Persistence.MongoDb.csproj index e9daafd2..80159a21 100644 --- a/src/Genocs.Persistence.MongoDb/Genocs.Persistence.MongoDb.csproj +++ b/src/Genocs.Persistence.MongoDb/Genocs.Persistence.MongoDb.csproj @@ -6,13 +6,13 @@ Genocs.Persistence.MongoDb Genocs.Persistence.MongoDb The Persistence MongoDB Library. - The Genocs Library .NET Core to be used with MongoDB as persistence datalayer.. + The Genocs Library .NET Core to be used with MongoDB as persistence datalayer. true 5.0.0 Nocco Giovanni Emanuele mongodb aggregate architecture boilerplate repository-patterns domain-driven-design dotnet-core microservice microservices solid solid-principles README_NUGET.md - Moved to NET8.0 + Upgraded to NET9.0 True latest @@ -26,7 +26,7 @@ - + diff --git a/src/Genocs.Persistence.MongoDb/Initializers/MongoDbInitializer.cs b/src/Genocs.Persistence.MongoDb/Initializers/MongoDbInitializer.cs index dd60928e..faa39f24 100644 --- a/src/Genocs.Persistence.MongoDb/Initializers/MongoDbInitializer.cs +++ b/src/Genocs.Persistence.MongoDb/Initializers/MongoDbInitializer.cs @@ -4,19 +4,13 @@ namespace Genocs.Persistence.MongoDb.Initializers; -internal sealed class MongoDbInitializer : IMongoDbInitializer +internal sealed class MongoDbInitializer(IMongoDatabase database, IMongoDbSeeder seeder, MongoDbOptions options) + : IMongoDbInitializer { private static int _initialized; - private readonly bool _seed; - private readonly IMongoDatabase _database; - private readonly IMongoDbSeeder _seeder; - - public MongoDbInitializer(IMongoDatabase database, IMongoDbSeeder seeder, MongoDbOptions options) - { - _database = database; - _seeder = seeder; - _seed = options.Seed; - } + private readonly bool _seed = options.Seed; + private readonly IMongoDatabase _database = database; + private readonly IMongoDbSeeder _seeder = seeder; public Task InitializeAsync() { diff --git a/src/Genocs.Persistence.MongoDb/MongoDatabaseProvider.cs b/src/Genocs.Persistence.MongoDb/MongoDatabaseProvider.cs index 565c4854..4ee40f95 100644 --- a/src/Genocs.Persistence.MongoDb/MongoDatabaseProvider.cs +++ b/src/Genocs.Persistence.MongoDb/MongoDatabaseProvider.cs @@ -21,13 +21,12 @@ public class MongoDatabaseProvider : IMongoDatabaseProvider /// public IMongoDatabase Database { get; private set; } - /// /// Default Constructor. /// /// /// - /// This exception happend in case mandatory data is missing. + /// This exception happens in case mandatory data is missing. public MongoDatabaseProvider(IOptions options, IOptions encrypOptions) { if (options == null) throw new NullReferenceException(nameof(options)); diff --git a/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbInitializer.cs b/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbInitializer.cs index a2858825..1c1fe402 100644 --- a/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbInitializer.cs +++ b/src/Genocs.Persistence.MongoDb/Repositories/IMongoDbInitializer.cs @@ -5,6 +5,4 @@ namespace Genocs.Persistence.MongoDb.Repositories; /// /// The MongoDbInitializer interface placeholder. /// -public interface IMongoDbInitializer : IInitializer -{ -} \ No newline at end of file +public interface IMongoDbInitializer : IInitializer; \ No newline at end of file diff --git a/src/Genocs.Persistence.MongoDb/Seeders/MongoDbSeeder.cs b/src/Genocs.Persistence.MongoDb/Seeders/MongoDbSeeder.cs index bb6924bd..709427b1 100644 --- a/src/Genocs.Persistence.MongoDb/Seeders/MongoDbSeeder.cs +++ b/src/Genocs.Persistence.MongoDb/Seeders/MongoDbSeeder.cs @@ -14,7 +14,7 @@ protected virtual async Task CustomSeedAsync(IMongoDatabase database) { var cursor = await database.ListCollectionsAsync(); var collections = await cursor.ToListAsync(); - if (collections.Any()) + if (collections.Count != 0) { return; } diff --git a/src/Genocs.Persistence.Redis/Genocs.Persistence.Redis.csproj b/src/Genocs.Persistence.Redis/Genocs.Persistence.Redis.csproj index fce5256d..030968f7 100644 --- a/src/Genocs.Persistence.Redis/Genocs.Persistence.Redis.csproj +++ b/src/Genocs.Persistence.Redis/Genocs.Persistence.Redis.csproj @@ -12,7 +12,7 @@ Nocco Giovanni Emanuele redis rediscache design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.QueryBuilder/Genocs.QueryBuilder.csproj b/src/Genocs.QueryBuilder/Genocs.QueryBuilder.csproj index 615f851b..2e2e2a86 100644 --- a/src/Genocs.QueryBuilder/Genocs.QueryBuilder.csproj +++ b/src/Genocs.QueryBuilder/Genocs.QueryBuilder.csproj @@ -12,7 +12,7 @@ Nocco Giovanni Emanuele aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.Secrets.AzureKeyVault/Genocs.Secrets.AzureKeyVault.csproj b/src/Genocs.Secrets.AzureKeyVault/Genocs.Secrets.AzureKeyVault.csproj index 1a38cff7..1ad5dbf7 100644 --- a/src/Genocs.Secrets.AzureKeyVault/Genocs.Secrets.AzureKeyVault.csproj +++ b/src/Genocs.Secrets.AzureKeyVault/Genocs.Secrets.AzureKeyVault.csproj @@ -12,7 +12,7 @@ Nocco Giovanni Emanuele aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.Secrets.Vault/Genocs.Secrets.Vault.csproj b/src/Genocs.Secrets.Vault/Genocs.Secrets.Vault.csproj index f8233639..f94da3c9 100644 --- a/src/Genocs.Secrets.Vault/Genocs.Secrets.Vault.csproj +++ b/src/Genocs.Secrets.Vault/Genocs.Secrets.Vault.csproj @@ -12,7 +12,7 @@ Nocco Giovanni Emanuele aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.Security/Genocs.Security.csproj b/src/Genocs.Security/Genocs.Security.csproj index cea9af1f..14611885 100644 --- a/src/Genocs.Security/Genocs.Security.csproj +++ b/src/Genocs.Security/Genocs.Security.csproj @@ -10,9 +10,9 @@ true 5.0.0 Nocco Giovanni Emanuele - aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + security hashing encrypt decrypt dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Moved tAligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.ServiceBusAzure/Genocs.ServiceBusAzure.csproj b/src/Genocs.ServiceBusAzure/Genocs.ServiceBusAzure.csproj index 9f6bfec2..973027a1 100644 --- a/src/Genocs.ServiceBusAzure/Genocs.ServiceBusAzure.csproj +++ b/src/Genocs.ServiceBusAzure/Genocs.ServiceBusAzure.csproj @@ -12,7 +12,7 @@ Nocco Giovanni Emanuele aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.Tracing/Extensions.cs b/src/Genocs.Tracing/Extensions.cs index 1f91c164..fedddbaf 100644 --- a/src/Genocs.Tracing/Extensions.cs +++ b/src/Genocs.Tracing/Extensions.cs @@ -20,7 +20,7 @@ public static class Extensions { /// - /// Custom settings for OpenTelemetry. + /// It allows to insert OpenTelemetry into the build pipeline. /// /// The Genocs builder. /// The Genocs builder you can use for chain. @@ -53,7 +53,7 @@ public static IGenocsBuilder AddOpenTelemetry(this IGenocsBuilder builder) // Set Custom Open telemetry services.AddOpenTelemetry() - .WithTracing(x => + .WithTracing((TracerProviderBuilder x) => { TracerProviderBuilder provider = x.SetResourceBuilder(ResourceBuilder.CreateDefault() .AddService( diff --git a/src/Genocs.Tracing/Genocs.Tracing.csproj b/src/Genocs.Tracing/Genocs.Tracing.csproj index 646b1479..1575c586 100644 --- a/src/Genocs.Tracing/Genocs.Tracing.csproj +++ b/src/Genocs.Tracing/Genocs.Tracing.csproj @@ -5,12 +5,12 @@ Genocs.Tracing Genocs.Tracing Genocs.Tracing - The tracing library to use Azure Service Bus. - The tracing library to use Azure Service Bus. + The tracing library to setup OpenTelemetry. + The tracing library to setup OpenTelemetry. true 5.0.0 Nocco Giovanni Emanuele - aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles + telemetry opentelemetry genocs dotnet dotnetcore dotnet-core microservice microservices README_NUGET.md Aligned to the ecosystem True @@ -37,7 +37,6 @@ - diff --git a/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs index 96f572d6..abefbea4 100644 --- a/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs +++ b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs @@ -6,36 +6,18 @@ namespace Genocs.WebApi.CQRS.Builders; -public class DispatcherEndpointsBuilder : IDispatcherEndpointsBuilder +public class DispatcherEndpointsBuilder(IEndpointsBuilder builder) : IDispatcherEndpointsBuilder { - private readonly IEndpointsBuilder _builder; + private readonly IEndpointsBuilder _builder = builder; - public DispatcherEndpointsBuilder(IEndpointsBuilder builder) - { - _builder = builder; - } - - public IDispatcherEndpointsBuilder Get( - string path, - Func? context = null, - Action? endpoint = null, - bool auth = false, - string? roles = null, - params string[] policies) + public IDispatcherEndpointsBuilder Get(string path, Func? context = null, Action? endpoint = null, bool auth = false, string? roles = null, params string[] policies) { _builder.Get(path, context, endpoint, auth, roles, policies); return this; } - public IDispatcherEndpointsBuilder Get( - string path, - Func? beforeDispatch = null, - Func? afterDispatch = null, - Action? endpoint = null, - bool auth = false, - string? roles = null, - params string[] policies) + public IDispatcherEndpointsBuilder Get(string path, Func? beforeDispatch = null, Func? afterDispatch = null, Action? endpoint = null, bool auth = false, string? roles = null, params string[] policies) where TQuery : class, IQuery { _builder.Get(path, async (query, ctx) => @@ -65,23 +47,30 @@ public IDispatcherEndpointsBuilder Get( return this; } - public IDispatcherEndpointsBuilder Post(string path, Func? context = null, - Action? endpoint = null, bool auth = false, string? roles = null, - params string[] policies) + public IDispatcherEndpointsBuilder Post( + string path, + Func? context = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) { _builder.Post(path, context, endpoint, auth, roles, policies); - return this; } - public IDispatcherEndpointsBuilder Post(string path, Func? beforeDispatch = null, - Func? afterDispatch = null, Action? endpoint = null, - bool auth = false, string? roles = null, - params string[] policies) + public IDispatcherEndpointsBuilder Post( + string path, + Func? beforeDispatch = null, + Func? afterDispatch = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) where T : class, ICommand { - _builder.Post(path, (cmd, ctx) => BuildCommandContext(cmd, ctx, beforeDispatch, afterDispatch), - endpoint, auth, roles, policies); + _builder.Post(path, (cmd, ctx) + => BuildCommandContext(cmd, ctx, beforeDispatch, afterDispatch), endpoint, auth, roles, policies); return this; } @@ -95,56 +84,78 @@ public IDispatcherEndpointsBuilder Put(string path, Func? con return this; } - public IDispatcherEndpointsBuilder Put(string path, Func? beforeDispatch = null, - Func? afterDispatch = null, Action? endpoint = null, - bool auth = false, string? roles = null, - params string[] policies) + public IDispatcherEndpointsBuilder Put( + string path, + Func? beforeDispatch = null, + Func? afterDispatch = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) where T : class, ICommand { - _builder.Put(path, (cmd, ctx) => BuildCommandContext(cmd, ctx, beforeDispatch, afterDispatch), endpoint, - auth, roles, policies); + _builder.Put(path, (cmd, ctx) + => BuildCommandContext(cmd, ctx, beforeDispatch, afterDispatch), endpoint, auth, roles, policies); return this; } - public IDispatcherEndpointsBuilder Delete(string path, Func? context = null, - Action? endpoint = null, bool auth = false, string? roles = null, - params string[] policies) + public IDispatcherEndpointsBuilder Delete( + string path, + Func? context = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) { _builder.Delete(path, context, endpoint, auth, roles, policies); return this; } - public IDispatcherEndpointsBuilder Delete(string path, Func? beforeDispatch = null, - Func? afterDispatch = null, Action? endpoint = null, - bool auth = false, string? roles = null, - params string[] policies) + public IDispatcherEndpointsBuilder Delete( + string path, + Func? beforeDispatch = null, + Func? afterDispatch = null, + Action? endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) where T : class, ICommand { - _builder.Delete(path, (cmd, ctx) => BuildCommandContext(cmd, ctx, beforeDispatch, afterDispatch), - endpoint, auth, roles, policies); + _builder.Delete(path, (cmd, ctx) + => BuildCommandContext(cmd, ctx, beforeDispatch, afterDispatch), endpoint, auth, roles, policies); return this; } - private static async Task BuildCommandContext(T command, HttpContext context, - Func? beforeDispatch = null, - Func? afterDispatch = null) where T : class, ICommand + private static async Task BuildCommandContext( + T command, + HttpContext? context, + Func? beforeDispatch = null, + Func? afterDispatch = null) + where T : class, ICommand { if (beforeDispatch is not null) { await beforeDispatch(command, context); } - var dispatcher = context.RequestServices.GetRequiredService(); - await dispatcher.SendAsync(command); - if (afterDispatch is null) + ICommandDispatcher? dispatcher = context?.RequestServices.GetRequiredService(); + + if (dispatcher != null) + { + await dispatcher.SendAsync(command); + } + + if (context != null) { context.Response.StatusCode = 200; - return; } - await afterDispatch(command, context); + if (afterDispatch is not null) + { + await afterDispatch(command, context); + } } } \ No newline at end of file diff --git a/src/Genocs.WebApi.CQRS/Genocs.WebApi.CQRS.csproj b/src/Genocs.WebApi.CQRS/Genocs.WebApi.CQRS.csproj index 5ee4c0d0..421b6f7e 100644 --- a/src/Genocs.WebApi.CQRS/Genocs.WebApi.CQRS.csproj +++ b/src/Genocs.WebApi.CQRS/Genocs.WebApi.CQRS.csproj @@ -12,7 +12,7 @@ Nocco Giovanni Emanuele aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.WebApi.CQRS/IDispatcherEndpointsBuilder.cs b/src/Genocs.WebApi.CQRS/IDispatcherEndpointsBuilder.cs index 67de3369..464e09d9 100644 --- a/src/Genocs.WebApi.CQRS/IDispatcherEndpointsBuilder.cs +++ b/src/Genocs.WebApi.CQRS/IDispatcherEndpointsBuilder.cs @@ -9,7 +9,7 @@ public interface IDispatcherEndpointsBuilder { IDispatcherEndpointsBuilder Get( string path, - Func? context = null, + Func? context = null, Action? endpoint = null, bool auth = false, string? roles = null, @@ -17,8 +17,8 @@ IDispatcherEndpointsBuilder Get( IDispatcherEndpointsBuilder Get( string path, - Func? beforeDispatch = null, - Func? afterDispatch = null, + Func? beforeDispatch = null, + Func? afterDispatch = null, Action? endpoint = null, bool auth = false, string? roles = null, @@ -27,7 +27,7 @@ IDispatcherEndpointsBuilder Get( IDispatcherEndpointsBuilder Post( string path, - Func? context = null, + Func? context = null, Action? endpoint = null, bool auth = false, string? roles = null, @@ -35,8 +35,8 @@ IDispatcherEndpointsBuilder Post( IDispatcherEndpointsBuilder Post( string path, - Func? beforeDispatch = null, - Func? afterDispatch = null, + Func? beforeDispatch = null, + Func? afterDispatch = null, Action? endpoint = null, bool auth = false, string? roles = null, @@ -45,7 +45,7 @@ IDispatcherEndpointsBuilder Post( IDispatcherEndpointsBuilder Put( string path, - Func? context = null, + Func? context = null, Action? endpoint = null, bool auth = false, string? roles = null, @@ -53,8 +53,8 @@ IDispatcherEndpointsBuilder Put( IDispatcherEndpointsBuilder Put( string path, - Func? beforeDispatch = null, - Func? afterDispatch = null, + Func? beforeDispatch = null, + Func? afterDispatch = null, Action? endpoint = null, bool auth = false, string? roles = null, @@ -63,7 +63,7 @@ IDispatcherEndpointsBuilder Put( IDispatcherEndpointsBuilder Delete( string path, - Func? context = null, + Func? context = null, Action? endpoint = null, bool auth = false, string? roles = null, @@ -71,8 +71,8 @@ IDispatcherEndpointsBuilder Delete( IDispatcherEndpointsBuilder Delete( string path, - Func? beforeDispatch = null, - Func? afterDispatch = null, + Func? beforeDispatch = null, + Func? afterDispatch = null, Action? endpoint = null, bool auth = false, string? roles = null, diff --git a/src/Genocs.WebApi.Security/Genocs.WebApi.Security.csproj b/src/Genocs.WebApi.Security/Genocs.WebApi.Security.csproj index 51d3f1ef..f4a5620b 100644 --- a/src/Genocs.WebApi.Security/Genocs.WebApi.Security.csproj +++ b/src/Genocs.WebApi.Security/Genocs.WebApi.Security.csproj @@ -12,7 +12,7 @@ Nocco Giovanni Emanuele aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.WebApi.Swagger/Genocs.WebApi.Swagger.csproj b/src/Genocs.WebApi.Swagger/Genocs.WebApi.Swagger.csproj index 38c8cd00..e624574d 100644 --- a/src/Genocs.WebApi.Swagger/Genocs.WebApi.Swagger.csproj +++ b/src/Genocs.WebApi.Swagger/Genocs.WebApi.Swagger.csproj @@ -12,7 +12,7 @@ Nocco Giovanni Emanuele openapi open api design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.WebApi/EndpointsBuilder.cs b/src/Genocs.WebApi/EndpointsBuilder.cs index efad9aa9..a0d45643 100644 --- a/src/Genocs.WebApi/EndpointsBuilder.cs +++ b/src/Genocs.WebApi/EndpointsBuilder.cs @@ -6,16 +6,10 @@ namespace Genocs.WebApi; -public class EndpointsBuilder : IEndpointsBuilder +public class EndpointsBuilder(IEndpointRouteBuilder routeBuilder, WebApiEndpointDefinitions definitions) : IEndpointsBuilder { - private readonly WebApiEndpointDefinitions _definitions; - private readonly IEndpointRouteBuilder _routeBuilder; - - public EndpointsBuilder(IEndpointRouteBuilder routeBuilder, WebApiEndpointDefinitions definitions) - { - _routeBuilder = routeBuilder; - _definitions = definitions; - } + private readonly WebApiEndpointDefinitions _definitions = definitions; + private readonly IEndpointRouteBuilder _routeBuilder = routeBuilder; public IEndpointsBuilder Get( string path, @@ -33,9 +27,13 @@ public IEndpointsBuilder Get( return this; } - public IEndpointsBuilder Get(string path, Func? context = null, - Action endpoint = null, bool auth = false, string roles = null, - params string[] policies) + public IEndpointsBuilder Get( + string path, + Func? context = null, + Action endpoint = null, + bool auth = false, + string? roles = null, + params string[] policies) where T : class { var builder = _routeBuilder.MapGet(path, ctx => BuildQueryContext(ctx, context)); @@ -121,9 +119,7 @@ public IEndpointsBuilder Delete(string path, Func? context = return this; } - public IEndpointsBuilder Delete(string path, Func? context = null, - Action endpoint = null, bool auth = false, string? roles = null, - params string[] policies) + public IEndpointsBuilder Delete(string path, Func? context = null, Action? endpoint = null, bool auth = false, string? roles = null, params string[] policies) where T : class { var builder = _routeBuilder.MapDelete(path, ctx => BuildQueryContext(ctx, context)); @@ -134,32 +130,34 @@ public IEndpointsBuilder Delete(string path, Func? cont return this; } - private static void ApplyAuthRolesAndPolicies(IEndpointConventionBuilder builder, - bool auth, - string? roles, - params string[] policies) + private static void ApplyAuthRolesAndPolicies(IEndpointConventionBuilder builder, bool auth, string? roles, params string[] policies) { - if (policies is not null && policies.Any()) + if (policies?.Any() == true) { builder.RequireAuthorization(policies); return; } - var hasRoles = !string.IsNullOrWhiteSpace(roles); - var authorize = new AuthorizeAttribute(); + bool hasRoles = !string.IsNullOrWhiteSpace(roles); if (hasRoles) { + var authorize = new AuthorizeAttribute(); authorize.Roles = roles; + builder.RequireAuthorization(authorize); + return; } - if (auth || hasRoles) + if (auth) { - builder.RequireAuthorization(authorize); + builder.RequireAuthorization(); + return; } + + // I don't like this, but it is the only way to allow anonymous access + builder.AllowAnonymous(); } - private static async Task BuildRequestContext(HttpContext httpContext, - Func? context = null) + private static async Task BuildRequestContext(HttpContext httpContext, Func? context = null) where T : class { var request = await httpContext.ReadJsonAsync(); @@ -171,8 +169,7 @@ private static async Task BuildRequestContext(HttpContext httpContext, await context.Invoke(request, httpContext); } - private static async Task BuildQueryContext(HttpContext httpContext, - Func? context = null) + private static async Task BuildQueryContext(HttpContext httpContext, Func? context = null) where T : class { var request = httpContext.ReadQuery(); diff --git a/src/Genocs.WebApi/Exceptions/ExceptionResponse.cs b/src/Genocs.WebApi/Exceptions/ExceptionResponse.cs index c6930e55..fbef7d49 100644 --- a/src/Genocs.WebApi/Exceptions/ExceptionResponse.cs +++ b/src/Genocs.WebApi/Exceptions/ExceptionResponse.cs @@ -2,14 +2,8 @@ namespace Genocs.WebApi.Exceptions; -public class ExceptionResponse +public class ExceptionResponse(object response, HttpStatusCode statusCode) { - public object Response { get; } - public HttpStatusCode StatusCode { get; } - - public ExceptionResponse(object response, HttpStatusCode statusCode) - { - Response = response; - StatusCode = statusCode; - } + public object Response { get; } = response; + public HttpStatusCode StatusCode { get; } = statusCode; } \ No newline at end of file diff --git a/src/Genocs.WebApi/Exceptions/IExceptionToResponseMapper.cs b/src/Genocs.WebApi/Exceptions/IExceptionToResponseMapper.cs index 7017e866..f4cf6f93 100644 --- a/src/Genocs.WebApi/Exceptions/IExceptionToResponseMapper.cs +++ b/src/Genocs.WebApi/Exceptions/IExceptionToResponseMapper.cs @@ -2,5 +2,5 @@ namespace Genocs.WebApi.Exceptions; public interface IExceptionToResponseMapper { - ExceptionResponse Map(Exception exception); + ExceptionResponse? Map(Exception exception); } \ No newline at end of file diff --git a/src/Genocs.WebApi/Extensions.cs b/src/Genocs.WebApi/Extensions.cs index c5d53f63..4e1a10ac 100644 --- a/src/Genocs.WebApi/Extensions.cs +++ b/src/Genocs.WebApi/Extensions.cs @@ -150,9 +150,7 @@ public static IApplicationBuilder UseEndpoints( public static IApplicationBuilder UseErrorHandler(this IApplicationBuilder builder) => builder.UseMiddleware(); - public static IApplicationBuilder UseAllForwardedHeaders( - this IApplicationBuilder builder, - bool resetKnownNetworksAndProxies = true) + public static IApplicationBuilder UseAllForwardedHeaders(this IApplicationBuilder builder, bool resetKnownNetworksAndProxies = true) { ForwardedHeadersOptions forwardingOptions = new ForwardedHeadersOptions { @@ -420,9 +418,9 @@ private static bool HasRouteData(this HttpRequest request) public static string Args(this HttpContext context, string key) => context.Args(key); - public static T Args(this HttpContext context, string key) + public static T? Args(this HttpContext context, string key) { - if (!context.GetRouteData().Values.TryGetValue(key, out var value)) + if (!context.GetRouteData().Values.TryGetValue(key, out object? value)) { return default; } @@ -432,7 +430,7 @@ public static T Args(this HttpContext context, string key) return (T)value; } - var data = value?.ToString(); + string? data = value?.ToString(); if (string.IsNullOrWhiteSpace(data)) { return default; @@ -443,6 +441,6 @@ public static T Args(this HttpContext context, string key) private class EmptyExceptionToResponseMapper : IExceptionToResponseMapper { - public ExceptionResponse Map(Exception exception) => null; + public ExceptionResponse? Map(Exception exception) => null; } } \ No newline at end of file diff --git a/src/Genocs.WebApi/Genocs.WebApi.csproj b/src/Genocs.WebApi/Genocs.WebApi.csproj index 6d3e30cb..c032593e 100644 --- a/src/Genocs.WebApi/Genocs.WebApi.csproj +++ b/src/Genocs.WebApi/Genocs.WebApi.csproj @@ -12,7 +12,7 @@ Nocco Giovanni Emanuele aggregate architecture boilerplate ddd ddd-architecture design-patterns docker domain-driven-design dotnet dotnetcore dotnet-core microservice microservices solid solid-principles README_NUGET.md - Aligned to the ecosystem + Upgraded to NET9.0 True latest diff --git a/src/Genocs.WebApi/GenocsFormatterResolver.cs b/src/Genocs.WebApi/GenocsFormatterResolver.cs index 51307536..b64c9241 100644 --- a/src/Genocs.WebApi/GenocsFormatterResolver.cs +++ b/src/Genocs.WebApi/GenocsFormatterResolver.cs @@ -9,17 +9,16 @@ internal sealed class GenocsFormatterResolver : IJsonFormatterResolver public static readonly IJsonFormatterResolver Instance = new GenocsFormatterResolver(); private static readonly IJsonFormatterResolver[] Resolvers = - { - StandardResolver.AllowPrivateCamelCase, - - }; + [ + StandardResolver.AllowPrivateCamelCase + ]; public IJsonFormatter GetFormatter() { return FormatterCache.Formatter; } - public static List Formatters { get; } = new(); + public static List Formatters { get; } = []; private static class FormatterCache { diff --git a/src/Genocs.WebApi/IEndpointsBuilder.cs b/src/Genocs.WebApi/IEndpointsBuilder.cs index ab7e9fda..9341a054 100644 --- a/src/Genocs.WebApi/IEndpointsBuilder.cs +++ b/src/Genocs.WebApi/IEndpointsBuilder.cs @@ -58,7 +58,7 @@ IEndpointsBuilder Put( IEndpointsBuilder Put( string path, - Func? context = null, + Func? context = null, Action? endpoint = null, bool auth = false, string? roles = null, @@ -67,7 +67,7 @@ IEndpointsBuilder Put( IEndpointsBuilder Delete( string path, - Func? context = null, + Func? context = null, Action? endpoint = null, bool auth = false, string? roles = null, @@ -75,7 +75,7 @@ IEndpointsBuilder Delete( IEndpointsBuilder Delete( string path, - Func? context = null, + Func? context = null, Action? endpoint = null, bool auth = false, string? roles = null, diff --git a/src/Genocs.WebApi/Requests/IRequest.cs b/src/Genocs.WebApi/Requests/IRequest.cs index 3ef171cb..6d42bc7b 100644 --- a/src/Genocs.WebApi/Requests/IRequest.cs +++ b/src/Genocs.WebApi/Requests/IRequest.cs @@ -1,5 +1,3 @@ namespace Genocs.WebApi.Requests; -public interface IRequest -{ -} \ No newline at end of file +public interface IRequest; \ No newline at end of file diff --git a/src/Genocs.WebApi/WebApiEndpointDefinition.cs b/src/Genocs.WebApi/WebApiEndpointDefinition.cs index 2e3e77c3..578312f3 100644 --- a/src/Genocs.WebApi/WebApiEndpointDefinition.cs +++ b/src/Genocs.WebApi/WebApiEndpointDefinition.cs @@ -1,8 +1,6 @@ namespace Genocs.WebApi; -public class WebApiEndpointDefinitions : List -{ -} +public class WebApiEndpointDefinitions : List; public class WebApiEndpointDefinition { diff --git a/src/apps/.editorconfig b/src/apps/.editorconfig new file mode 100644 index 00000000..586c5fb7 --- /dev/null +++ b/src/apps/.editorconfig @@ -0,0 +1,270 @@ +root = true + +[*] +roslynator_accessibility_modifiers = explicit +roslynator_use_anonymous_function_or_method_group = anonymous_function|method_group +roslynator_enum_has_flag_style = method +roslynator_object_creation_type_style = explicit|implicit|implicit_when_type_is_obvious + +indent_style = space + +trim_trailing_whitespace = true + +insert_final_newline = false + +[*.md] +trim_trailing_whitespace = false + +[*.json] +indent_size = 2 + +[*.cs] +dotnet_sort_system_directives_first = true:warning + +csharp_style_namespace_declarations = file_scoped:warning + +csharp_style_var_for_built_in_types = false:warning + +csharp_style_var_when_type_is_apparent = true:warning + +csharp_style_var_elsewhere = true:warning + +csharp_new_line_before_members_in_anonymous_types = true:warning + +# SA1623: Property summary documentation should match accessors +dotnet_diagnostic.SA1623.severity = none + +# SA1101: Prefix local calls with this +dotnet_diagnostic.SA1101.severity = none + +# SA1642: Constructor summary documentation should begin with standard text +dotnet_diagnostic.SA1642.severity = none + +# SA1309: Field names should not begin with underscore +dotnet_diagnostic.SA1309.severity = none + +# RCS1194: Implement exception constructors. +dotnet_diagnostic.RCS1194.severity = none + +# SA1000: Keywords should be spaced correctly +dotnet_diagnostic.SA1000.severity = none + +# SA1124: Do not use regions +dotnet_diagnostic.SA1124.severity = none + +# SA1413: Use trailing comma in multi-line initializers +dotnet_diagnostic.SA1413.severity = none + +# SA1201: Elements should appear in the correct order +dotnet_diagnostic.SA1201.severity = suggestion + +# SA1638: File header file name documentation should match file name +dotnet_diagnostic.SA1638.severity = warning + +# SA1633: File should have header +dotnet_diagnostic.SA1633.severity = none + +# SA1404: Code analysis suppression should have justification +dotnet_diagnostic.SA1404.severity = none + +# SA1206: Declaration keywords should follow order +dotnet_diagnostic.SA1206.severity = none + +# CA1040: Avoid empty interfaces +dotnet_diagnostic.CA1040.severity = none + +# RCS1012: Use explicit type instead of 'var' +dotnet_diagnostic.RCS1012.severity = none + +# RCS1008: Use explicit type instead of 'var' +dotnet_diagnostic.RCS1008.severity = none + +# CA1725 +dotnet_diagnostic.CA1725.severity = none + +# RCS1009: Use explicit type instead of 'var' +dotnet_diagnostic.RCS1009.severity = none + +# SA1402: File may only contain a single type +dotnet_diagnostic.SA1402.severity = suggestion + +# CA1711 +dotnet_diagnostic.CA1711.severity = none + +# CA1720: Identifier contains type name +dotnet_diagnostic.CA1720.severity = none + +# IDE0022: Use block body for methods +dotnet_diagnostic.IDE0022.severity = none + +# SA1011: Closing square brackets should be spaced correctly +dotnet_diagnostic.SA1011.severity = none + +# CA1721 +dotnet_diagnostic.CA1721.severity = none + +# SA1313: Parameter names should begin with lower-case letter +dotnet_diagnostic.SA1313.severity = none + +# SecurityIntelliSenseCS: MS Security rules violation +dotnet_diagnostic.SecurityIntelliSenseCS.severity = suggestion + +# SA1123: Do not place regions within elements +dotnet_diagnostic.SA1123.severity = none + +# RCS1046: Add suffix 'Async' to asynchronous method name +dotnet_diagnostic.RCS1046.severity = warning + +# SA1625: Element documentation should not be copied and pasted +dotnet_diagnostic.SA1625.severity = none + +# SCS9999 +dotnet_diagnostic.SCS9999.severity = none + +# RCS1090 Add call to 'ConfigureAwait' +dotnet_diagnostic.RCS1090.severity = none + +# RCS1170 Use read-only auto-implemented property +dotnet_diagnostic.RCS1170.severity = none + +# SA1649 +dotnet_diagnostic.SA1649.severity = none + +# RCS1021 Use expression-bodied lambda. +dotnet_diagnostic.RCS1021.severity = none + +# RCS1047 Remove suffix 'Async' from non-asynchronous method name. +# dotnet_diagnostic.RCS1047.severity = silent + +# SA1600 Elements should be documented +dotnet_diagnostic.SA1600.severity = silent + +# CS1591 Missing XML comment for publicly visible type or member +dotnet_diagnostic.CS1591.severity = none + +# SA1602: Enumeration items should be documented +dotnet_diagnostic.SA1602.severity = none + +# CA1720 Identifier 'Decimal' contains type name +dotnet_diagnostic.CA1720.severity = none + +# SA1601: Partial elements should be documented +dotnet_diagnostic.SA1601.severity = silent + +# CA1711 Rename type name UserEventHandler so that it does not end in 'EventHandler' +dotnet_diagnostic.CA1711.severity = none + +# CA1307: Specify StringComparison for clarity +dotnet_diagnostic.CA1307.severity = none +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_indent_labels = one_less_than_current +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_style_prefer_readonly_struct = true:suggestion +csharp_prefer_static_local_function = true:suggestion +csharp_style_prefer_readonly_struct_member = true:suggestion +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent +csharp_style_conditional_delegate_call = true:suggestion +csharp_space_around_binary_operators = before_and_after +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_prefer_collection_expression = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_readonly_field = true:suggestion +dotnet_style_predefined_type_for_member_access = true:silent +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_allow_statement_immediately_after_block_experimental = true:silent +dotnet_style_allow_multiple_blank_lines_experimental = true:silent +dotnet_code_quality_unused_parameters = all:suggestion diff --git a/src/apps/Directory.Build.props b/src/apps/Directory.Build.props new file mode 100644 index 00000000..b2374060 --- /dev/null +++ b/src/apps/Directory.Build.props @@ -0,0 +1,77 @@ + + + + + enable + enable + true + false + false + $(MSBuildThisFileDirectory)dotnet.ruleset + True + True + 7.1.0 + 13.0 + Genocs + Genocs 2024 + LICENSE + https://github.com/Genocs/genocs-library + https://github.com/Genocs/genocs-library.git + icon.png + git + True + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + True + \ + + + True + \ + + + True + \ + + + + + + + + + + + + true + + + true + + + \ No newline at end of file diff --git a/src/apps/Directory.Build.targets b/src/apps/Directory.Build.targets new file mode 100644 index 00000000..fd9797bc --- /dev/null +++ b/src/apps/Directory.Build.targets @@ -0,0 +1,5 @@ + + + $(OutputPath)$(AssemblyName).xml + + \ No newline at end of file diff --git a/src/apps/NuGet.config b/src/apps/NuGet.config new file mode 100644 index 00000000..a552551f --- /dev/null +++ b/src/apps/NuGet.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/apps/api-gateway/Genocs.APIGateway/Framework/CustomForwarderHttpClientFactory.cs b/src/apps/api-gateway/Genocs.APIGateway/Framework/CustomForwarderHttpClientFactory.cs index 14cc5f93..d967fbb0 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Framework/CustomForwarderHttpClientFactory.cs +++ b/src/apps/api-gateway/Genocs.APIGateway/Framework/CustomForwarderHttpClientFactory.cs @@ -3,15 +3,10 @@ namespace Genocs.APIGateway.Framework; -internal class CustomForwarderHttpClientFactory : IForwarderHttpClientFactory +internal class CustomForwarderHttpClientFactory(CorrelationIdFactory correlationIdFactory) : IForwarderHttpClientFactory { - private readonly CorrelationIdFactory _correlationIdFactory; + private readonly CorrelationIdFactory _correlationIdFactory = correlationIdFactory; - public CustomForwarderHttpClientFactory(CorrelationIdFactory correlationIdFactory) - { - _correlationIdFactory = correlationIdFactory; - } - public HttpMessageInvoker CreateClient(ForwarderHttpClientContext context) { if (context.OldClient != null && context.NewConfig == context.OldConfig) @@ -34,7 +29,7 @@ public HttpMessageInvoker CreateClient(ForwarderHttpClientContext context) handler.SslOptions.EnabledSslProtocols = newClientOptions.SslProtocols.Value; } - // TODO: Enable this + // TODO: Enable this //if (newClientOptions.ClientCertificate != null) //{ // handler.SslOptions.ClientCertificates = new X509CertificateCollection @@ -53,27 +48,18 @@ public HttpMessageInvoker CreateClient(ForwarderHttpClientContext context) handler.SslOptions.RemoteCertificateValidationCallback = (sender, cert, chain, errors) => cert.Subject == "demo.io"; } - - var httpMessageInvoker = new CustomHttpMessageInvoker(_correlationIdFactory, handler, true); - return httpMessageInvoker; + return new CustomHttpMessageInvoker(_correlationIdFactory, handler, true); } - - private class CustomHttpMessageInvoker : HttpMessageInvoker + private class CustomHttpMessageInvoker(CorrelationIdFactory correlationIdFactory, HttpMessageHandler handler, bool disposeHandler) + : HttpMessageInvoker(handler, disposeHandler) { - private readonly CorrelationIdFactory _correlationIdFactory; - - public CustomHttpMessageInvoker(CorrelationIdFactory correlationIdFactory, HttpMessageHandler handler, - bool disposeHandler) : base(handler, disposeHandler) - { - _correlationIdFactory = correlationIdFactory; - } + private readonly CorrelationIdFactory _correlationIdFactory = correlationIdFactory; - public override async Task SendAsync(HttpRequestMessage request, - CancellationToken cancellationToken) + public override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - var correlationId = _correlationIdFactory.Create(); + string correlationId = _correlationIdFactory.Create(); request.Headers.TryAddWithoutValidation("x-correlation-id", correlationId); return await base.SendAsync(request, cancellationToken); } diff --git a/src/apps/api-gateway/Genocs.APIGateway/Framework/ICorrelationContextBuilder.cs b/src/apps/api-gateway/Genocs.APIGateway/Framework/ICorrelationContextBuilder.cs index 9618311f..66b68717 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Framework/ICorrelationContextBuilder.cs +++ b/src/apps/api-gateway/Genocs.APIGateway/Framework/ICorrelationContextBuilder.cs @@ -2,10 +2,5 @@ namespace Genocs.APIGateway.Framework; internal interface ICorrelationContextBuilder { - CorrelationContext Build( - HttpContext context, - string correlationId, - string spanContext, - string? name = null, - string? resourceId = null); + CorrelationContext Build(HttpContext context, string correlationId, string spanContext, string? name = null, string? resourceId = null); } \ No newline at end of file diff --git a/src/apps/api-gateway/Genocs.APIGateway/Framework/LogContextMiddleware.cs b/src/apps/api-gateway/Genocs.APIGateway/Framework/LogContextMiddleware.cs index a1fe66e6..2894e747 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Framework/LogContextMiddleware.cs +++ b/src/apps/api-gateway/Genocs.APIGateway/Framework/LogContextMiddleware.cs @@ -2,18 +2,14 @@ namespace Genocs.APIGateway.Framework; -internal class LogContextMiddleware : IMiddleware +internal class LogContextMiddleware(CorrelationIdFactory correlationIdFactory) : IMiddleware { - private readonly CorrelationIdFactory _correlationIdFactory; - - public LogContextMiddleware(CorrelationIdFactory correlationIdFactory) - { - _correlationIdFactory = correlationIdFactory ?? throw new ArgumentNullException(nameof(correlationIdFactory)); - } + private readonly CorrelationIdFactory _correlationIdFactory = correlationIdFactory ?? throw new ArgumentNullException(nameof(correlationIdFactory)); public async Task InvokeAsync(HttpContext context, RequestDelegate next) { string correlationId = _correlationIdFactory.Create(); + using (LogContext.PushProperty("CorrelationId", correlationId)) { await next(context); diff --git a/src/apps/api-gateway/Genocs.APIGateway/Framework/MessagingMiddleware.cs b/src/apps/api-gateway/Genocs.APIGateway/Framework/MessagingMiddleware.cs index e737416a..5cfff50e 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Framework/MessagingMiddleware.cs +++ b/src/apps/api-gateway/Genocs.APIGateway/Framework/MessagingMiddleware.cs @@ -23,10 +23,7 @@ public MessagingMiddleware( CorrelationIdFactory correlationIdFactory, IOptions messagingOptions) { - if (messagingOptions is null) - { - throw new ArgumentNullException(nameof(messagingOptions)); - } + ArgumentNullException.ThrowIfNull(messagingOptions); _rabbitMQClient = rabbitMQClient ?? throw new ArgumentNullException(nameof(rabbitMQClient)); _routeMatcher = routeMatcher ?? throw new ArgumentNullException(nameof(routeMatcher)); @@ -54,7 +51,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) continue; } - var key = $"{endpoint.Exchange}:{endpoint.RoutingKey}"; + string key = $"{endpoint.Exchange}:{endpoint.RoutingKey}"; if (!Conventions.TryGetValue(key, out var conventions)) { conventions = new MessageConventions(typeof(object), endpoint.RoutingKey, endpoint.Exchange, null); diff --git a/src/apps/api-gateway/Genocs.APIGateway/Genocs.APIGateway.csproj b/src/apps/api-gateway/Genocs.APIGateway/Genocs.APIGateway.csproj index 76e0cb44..2584da73 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Genocs.APIGateway.csproj +++ b/src/apps/api-gateway/Genocs.APIGateway/Genocs.APIGateway.csproj @@ -31,7 +31,6 @@ - diff --git a/src/apps/api-gateway/Genocs.APIGateway/Properties/launchSettings.json b/src/apps/api-gateway/Genocs.APIGateway/Properties/launchSettings.json index 7c5232dd..180989f0 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Properties/launchSettings.json +++ b/src/apps/api-gateway/Genocs.APIGateway/Properties/launchSettings.json @@ -2,7 +2,6 @@ "profiles": { "Local": { "commandName": "Project", - "launchBrowser": true, "applicationUrl": "https://localhost:5500;http://localhost:5501", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "development" diff --git a/src/apps/api-gateway/Genocs.APIGateway/Startup.cs b/src/apps/api-gateway/Genocs.APIGateway/Startup.cs index 379c37d9..8a8d006b 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Startup.cs +++ b/src/apps/api-gateway/Genocs.APIGateway/Startup.cs @@ -11,14 +11,9 @@ namespace Genocs.APIGateway; -internal class Startup +internal class Startup(IConfiguration configuration) { - public IConfiguration Configuration { get; } - - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } + public IConfiguration Configuration { get; } = configuration; public void ConfigureServices(IServiceCollection services) { @@ -52,11 +47,11 @@ private async Task ConfigureServicesAsync(IServiceCollection services) .AddWebApi() .Build(); - services.AddAuthorization(options => - { - options.AddPolicy("authenticatedUser", policy => - policy.RequireAuthenticatedUser()); - }); + //services.AddAuthorization(options => + //{ + // options.AddPolicy("authenticatedUser", policy => + // policy.RequireAuthenticatedUser()); + //}); services.AddCors(cors => { diff --git a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json index 4b117e2c..7a83b97f 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json +++ b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json @@ -13,7 +13,7 @@ "service": "api-gateway", "address": "docker.for.mac.localhost", "port": "5501", - "pingEnabled": true, + "pingEnabled": false, "pingEndpoint": "healthz", "pingInterval": 3, "removeAfterInterval": 3, @@ -31,7 +31,7 @@ "services": { }, "requestMasking": { - "enabled": true, + "enabled": false, "maskTemplate": "*****" }, "correlationIdHeader": "x-correlation-id" @@ -84,28 +84,61 @@ "jaeger": { "enabled": true, "serviceName": "api-gateway", - "endpoint": "http://localhost:4317", - "protocol": "Grpc", - "processorType": "Batch", - "maxQueueSize": 2048, - "scheduledDelayMilliseconds": 5000, - "exporterTimeoutMilliseconds": 30000, - "maxExportBatchSize": 512 + "udpHost": "http://localhost:4317", + "udpPort": 6831, + "maxPacketSize": 65000, + "sampler": "const", + "excludePaths": [ + "/", + "/healthz", + "/alive", + "/metrics" + ] + }, + "openTelemetry": { + "enabled": true, + "exporter": { + "enabled": true, + "otlpEndpoint": "http://localhost:4317", + "protocol": "Grpc", + "processorType": "Batch", + "maxQueueSize": 2048, + "scheduledDelayMilliseconds": 5000, + "exporterTimeoutMilliseconds": 30000, + "maxExportBatchSize": 512 + }, + "console": { + "enabled": true, + "enableTracing": true, + "enableMetrics": true, + "enableLogging": true + }, + "azure": { + "enabled": true, + "enableTracing": true, + "enableMetrics": true, + "enableLogging": true, + "connectionString": "InstrumentationKey=1496274b-bda7-4ac6-88ab-9f73b4d3c7b8;IngestionEndpoint=https://italynorth-0.in.applicationinsights.azure.com/;LiveEndpoint=https://italynorth.livediagnostics.monitor.azure.com/;ApplicationId=c417f66d-3611-48a2-80fe-5a6d302bed4f" + } }, "jwt": { "enabled": true, "allowAnonymousEndpoints": [], - "certificate": { - "location": "certs/localhost.cer", + "_certificate": { + "location": "certs/localhost.pfx", "password": "test", "rawData": "" }, - "expiryMinutes": 30, + "expiryMinutes": 60, "issuer": "genocs-identity-service", + "issuerSigningKey": "THIS IS USED TO SIGN AND VERIFY JWT TOKENS, REPLACE IT WITH YOUR OWN SECRET, IT CAN BE ANY STRING", "validIssuer": "genocs-identity-service", "validateAudience": false, - "validateIssuer": true, - "validateLifetime": true, + "validateIssuer": false, + "validateLifetime": false, + "requireExpirationTime": false, + "requireSignedTokens": false, + "validateIssuerSigningKey": false, "expiry": "01:00:00" }, "metrics": { @@ -192,7 +225,7 @@ "elk": { "enabled": false, "url": "http://localhost:9200", - "indexFormat": "signalr-service-{0:yyyy.MM.dd}", + "indexFormat": "api-gateway-service-{0:yyyy.MM.dd}", "basicAuthEnabled": false, "username": "user", "password": "secret" @@ -210,8 +243,8 @@ "enabled": true, "reDocEnabled": false, "name": "Gateway", - "title": "API Gateway", - "version": "v01", + "title": "API Gateway Service", + "version": "v1", "description": "API Gateway Service", "routePrefix": "swagger", "includeSecurity": true, @@ -343,5 +376,11 @@ } } } + }, + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + } } } \ No newline at end of file diff --git a/src/apps/apigateway.dockerfile b/src/apps/apigateway.dockerfile index 3a2ae1de..bc8ad175 100644 --- a/src/apps/apigateway.dockerfile +++ b/src/apps/apigateway.dockerfile @@ -9,6 +9,11 @@ FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env WORKDIR /src COPY ["api-gateway/Genocs.APIGateway", "Genocs.APIGateway/"] +COPY ["Directory.Build.props", "Directory.Build.props"] +COPY ["Directory.Build.targets", "Directory.Build.targets"] +COPY ["NuGet.config", "NuGet.config"] +COPY ["dotnet.ruleset", "dotnet.ruleset"] +COPY ["stylecop.json", "stylecop.json"] WORKDIR "/src/Genocs.APIGateway" diff --git a/src/apps/dotnet.ruleset b/src/apps/dotnet.ruleset new file mode 100644 index 00000000..3010e47e --- /dev/null +++ b/src/apps/dotnet.ruleset @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/apps/identity-webapi.dockerfile b/src/apps/identity-webapi.dockerfile index b3c74a00..e7b1b4e9 100644 --- a/src/apps/identity-webapi.dockerfile +++ b/src/apps/identity-webapi.dockerfile @@ -9,6 +9,11 @@ FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env WORKDIR /src COPY ["identity/Genocs.Identities.WebApi", "Genocs.Identities.WebApi/"] COPY ["identity/Genocs.Identities.Application", "Genocs.Identities.Application/"] +COPY ["Directory.Build.props", "Directory.Build.props"] +COPY ["Directory.Build.targets", "Directory.Build.targets"] +COPY ["NuGet.config", "NuGet.config"] +COPY ["dotnet.ruleset", "dotnet.ruleset"] +COPY ["stylecop.json", "stylecop.json"] WORKDIR "/src/Genocs.Identities.WebApi" diff --git a/src/apps/identity/Genocs.Identities.Application/Commands/CreateAdmin.cs b/src/apps/identity/Genocs.Identities.Application/Commands/CreateAdmin.cs new file mode 100644 index 00000000..c73575e2 --- /dev/null +++ b/src/apps/identity/Genocs.Identities.Application/Commands/CreateAdmin.cs @@ -0,0 +1,12 @@ +using Genocs.Core.CQRS.Commands; + +namespace Genocs.Identities.Application.Commands; + +public class CreateAdmin(string email, string name, string password, IEnumerable permissions) : ICommand +{ + public Guid UserId { get; } = Guid.NewGuid(); + public string Email { get; } = email; + public string Name { get; } = name; + public string Password { get; } = password; + public IEnumerable Permissions { get; } = permissions; +} \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Commands/CreateUser.cs b/src/apps/identity/Genocs.Identities.Application/Commands/CreateUser.cs index aaa65e6c..17c44875 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/CreateUser.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/CreateUser.cs @@ -2,28 +2,11 @@ namespace Genocs.Identities.Application.Commands; -public class CreateUser : ICommand +public class CreateUser(string email, string name, string password, IEnumerable permissions) : ICommand { - public Guid UserId { get; } - public string Email { get; } - public string Name { get; } - public string Password { get; } - public string Role { get; } - public IEnumerable Permissions { get; } - - public CreateUser( - Guid userId, - string email, - string name, - string password, - string role, - IEnumerable permissions) - { - UserId = userId == Guid.Empty ? Guid.NewGuid() : userId; - Email = email; - Name = name; - Password = password; - Role = role; - Permissions = permissions; - } + public Guid UserId { get; } = Guid.NewGuid(); + public string Email { get; } = email; + public string Name { get; } = name; + public string Password { get; } = password; + public IEnumerable Permissions { get; } = permissions; } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/CreateAdminHandler.cs b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/CreateAdminHandler.cs new file mode 100644 index 00000000..407a9e4d --- /dev/null +++ b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/CreateAdminHandler.cs @@ -0,0 +1,54 @@ +using Genocs.Core.CQRS.Commands; +using Genocs.Identities.Application.Domain.Constants; +using Genocs.Identities.Application.Domain.Entities; +using Genocs.Identities.Application.Domain.Exceptions; +using Genocs.Identities.Application.Domain.Repositories; +using Genocs.Identities.Application.Events; +using Genocs.Identities.Application.Services; +using Microsoft.Extensions.Logging; +using System.Text.RegularExpressions; + +namespace Genocs.Identities.Application.Commands.Handlers; + +internal sealed class CreateAdminHandler(IUserRepository userRepository, IPasswordService passwordService, + IMessageBroker messageBroker, ILogger logger) : ICommandHandler +{ + private readonly IUserRepository _userRepository = userRepository; + private readonly IPasswordService _passwordService = passwordService; + private readonly IMessageBroker _messageBroker = messageBroker; + private readonly ILogger _logger = logger; + + private static readonly Regex EmailRegex = new Regex( + @"^(?("")("".+?(? { Roles.Admin }, DateTime.UtcNow, command.Permissions); + await _userRepository.AddAsync(user); + _logger.LogInformation($"Created an account for the admin with ID: '{user.Id}'."); + await _messageBroker.PublishAsync(new UserCreated(user.Id, user.Name, user.Roles)); + } +} \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/CreateUserHandler.cs b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/CreateUserHandler.cs index 3f11654b..9555e7a2 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/CreateUserHandler.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/CreateUserHandler.cs @@ -1,4 +1,5 @@ using Genocs.Core.CQRS.Commands; +using Genocs.Identities.Application.Domain.Constants; using Genocs.Identities.Application.Domain.Entities; using Genocs.Identities.Application.Domain.Exceptions; using Genocs.Identities.Application.Domain.Repositories; @@ -9,27 +10,19 @@ namespace Genocs.Identities.Application.Commands.Handlers; -internal sealed class CreateUserHandler : ICommandHandler +internal sealed class CreateUserHandler(IUserRepository userRepository, IPasswordService passwordService, + IMessageBroker messageBroker, ILogger logger) : ICommandHandler { - private readonly IUserRepository _userRepository; - private readonly IPasswordService _passwordService; - private readonly IMessageBroker _messageBroker; - private readonly ILogger _logger; + private readonly IUserRepository _userRepository = userRepository; + private readonly IPasswordService _passwordService = passwordService; + private readonly IMessageBroker _messageBroker = messageBroker; + private readonly ILogger _logger = logger; private static readonly Regex EmailRegex = new Regex( @"^(?("")("".+?(? logger) - { - _userRepository = userRepository; - _passwordService = passwordService; - _messageBroker = messageBroker; - _logger = logger; - } - public async Task HandleAsync(CreateUser command, CancellationToken cancellationToken = default) { if (!EmailRegex.IsMatch(command.Email)) @@ -52,12 +45,10 @@ public async Task HandleAsync(CreateUser command, CancellationToken cancellation throw new NameInUseException(command.Name); } - var role = string.IsNullOrWhiteSpace(command.Role) ? "user" : command.Role.ToLowerInvariant(); - var password = _passwordService.Hash(command.Password); - user = new User(command.UserId, command.Email, command.Name, password, role, DateTime.UtcNow, - command.Permissions); + string password = _passwordService.Hash(command.Password); + user = new User(command.UserId, command.Email, command.Name, password, new List { Roles.User }, DateTime.UtcNow, command.Permissions); await _userRepository.AddAsync(user); _logger.LogInformation($"Created an account for the user with ID: '{user.Id}'."); - await _messageBroker.PublishAsync(new UserCreated(user.Id, user.Name, user.Role)); + await _messageBroker.PublishAsync(new UserCreated(user.Id, user.Name, user.Roles)); } } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/LockUserHandler.cs b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/LockUserHandler.cs index 0348156a..0efd8ff7 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/LockUserHandler.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/LockUserHandler.cs @@ -6,25 +6,14 @@ namespace Genocs.Identities.Application.Commands.Handlers; -internal sealed class LockUserHandler : ICommandHandler +internal sealed class LockUserHandler(IUserRepository userRepository, IMessageBroker messageBroker) : ICommandHandler { - private readonly IUserRepository _userRepository; - private readonly IMessageBroker _messageBroker; - - public LockUserHandler(IUserRepository userRepository, IMessageBroker messageBroker) - { - _userRepository = userRepository; - _messageBroker = messageBroker; - } + private readonly IUserRepository _userRepository = userRepository; + private readonly IMessageBroker _messageBroker = messageBroker; public async Task HandleAsync(LockUser command, CancellationToken cancellationToken = default) { - var user = await _userRepository.GetAsync(command.UserId); - if (user is null) - { - throw new UserNotFoundException(command.UserId); - } - + var user = await _userRepository.GetAsync(command.UserId) ?? throw new UserNotFoundException(command.UserId); if (user.Lock()) { await _userRepository.UpdateAsync(user); diff --git a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/RevokeAccessTokenHandler.cs b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/RevokeAccessTokenHandler.cs index f40fa874..bfd25250 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/RevokeAccessTokenHandler.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/RevokeAccessTokenHandler.cs @@ -3,15 +3,12 @@ namespace Genocs.Identities.Application.Commands.Handlers; -internal sealed class RevokeAccessTokenHandler : ICommandHandler +internal sealed class RevokeAccessTokenHandler(IAccessTokenService accessTokenService) + : ICommandHandler { - private readonly IAccessTokenService _accessTokenService; - - public RevokeAccessTokenHandler(IAccessTokenService accessTokenService) - { - _accessTokenService = accessTokenService ?? throw new ArgumentNullException(nameof(accessTokenService)); - } + private readonly IAccessTokenService _accessTokenService = accessTokenService + ?? throw new ArgumentNullException(nameof(accessTokenService)); public async Task HandleAsync(RevokeAccessToken command, CancellationToken cancellationToken = default) - => await _accessTokenService.DeactivateAsync(command.AccessToken); + => await Task.Run(() => { _accessTokenService.Deactivate(command.AccessToken); }, cancellationToken); } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/SignInHandler.cs b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/SignInHandler.cs index 29ac976d..7a4f1a82 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/SignInHandler.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/SignInHandler.cs @@ -19,9 +19,15 @@ internal sealed class SignInHandler : ICommandHandler private readonly IMessageBroker _messageBroker; private readonly ILogger _logger; - public SignInHandler(IUserRepository userRepository, IRefreshTokenRepository refreshTokenRepository, - IPasswordService passwordService, IJwtProvider jwtProvider, IRng rng, ITokenStorage storage, - IMessageBroker messageBroker, ILogger logger) + public SignInHandler( + IUserRepository userRepository, + IRefreshTokenRepository refreshTokenRepository, + IPasswordService passwordService, + IJwtProvider jwtProvider, + IRng rng, + ITokenStorage storage, + IMessageBroker messageBroker, + ILogger logger) { _userRepository = userRepository; _refreshTokenRepository = refreshTokenRepository; @@ -53,7 +59,8 @@ public async Task HandleAsync(SignIn command, CancellationToken cancellationToke ["permissions"] = user.Permissions } : null; - var auth = _jwtProvider.Create(user.Id, user.Name, user.Role, claims: claims); + + var auth = _jwtProvider.Create(user.Id, user.Name, user.Roles, claims: claims); auth.RefreshToken = await CreateRefreshTokenAsync(user.Id); _storage.Set(command.Id, auth); _logger.LogInformation($"User with id: {user.Id} has been authenticated."); @@ -62,7 +69,7 @@ public async Task HandleAsync(SignIn command, CancellationToken cancellationToke private async Task CreateRefreshTokenAsync(Guid userId) { - var token = _rng.Generate(30, true); + string token = _rng.Generate(30, true); var refreshToken = new RefreshToken(new AggregateId(), userId, token, DateTime.UtcNow); await _refreshTokenRepository.AddAsync(refreshToken); diff --git a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/UnlockUserHandler.cs b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/UnlockUserHandler.cs index e731360d..aa971c4e 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/UnlockUserHandler.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/UnlockUserHandler.cs @@ -6,24 +6,16 @@ namespace Genocs.Identities.Application.Commands.Handlers; -internal sealed class UnlockUserHandler : ICommandHandler +internal sealed class UnlockUserHandler(IUserRepository userRepository, IMessageBroker messageBroker) + : ICommandHandler { - private readonly IUserRepository _userRepository; - private readonly IMessageBroker _messageBroker; - - public UnlockUserHandler(IUserRepository userRepository, IMessageBroker messageBroker) - { - _userRepository = userRepository; - _messageBroker = messageBroker; - } + private readonly IUserRepository _userRepository = userRepository; + private readonly IMessageBroker _messageBroker = messageBroker; public async Task HandleAsync(UnlockUser command, CancellationToken cancellationToken = default) { - var user = await _userRepository.GetAsync(command.UserId); - if (user is null) - { - throw new UserNotFoundException(command.UserId); - } + var user = await _userRepository.GetAsync(command.UserId) + ?? throw new UserNotFoundException(command.UserId); if (user.Unlock()) { diff --git a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/UseRefreshTokenHandler.cs b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/UseRefreshTokenHandler.cs index f661f506..aa773ea4 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/UseRefreshTokenHandler.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/UseRefreshTokenHandler.cs @@ -13,8 +13,7 @@ internal sealed class UseRefreshTokenHandler : ICommandHandler private readonly IJwtProvider _jwtProvider; private readonly ITokenStorage _storage; - public UseRefreshTokenHandler(IRefreshTokenRepository refreshTokenRepository, IUserRepository userRepository, - IJwtProvider jwtProvider, ITokenStorage storage) + public UseRefreshTokenHandler(IRefreshTokenRepository refreshTokenRepository, IUserRepository userRepository, IJwtProvider jwtProvider, ITokenStorage storage) { _refreshTokenRepository = refreshTokenRepository; _userRepository = userRepository; @@ -24,22 +23,13 @@ public UseRefreshTokenHandler(IRefreshTokenRepository refreshTokenRepository, IU public async Task HandleAsync(UseRefreshToken command, CancellationToken cancellationToken = default) { - var token = await _refreshTokenRepository.GetAsync(command.RefreshToken); - if (token is null) - { - throw new InvalidRefreshTokenException(); - } - + var token = await _refreshTokenRepository.GetAsync(command.RefreshToken) ?? throw new InvalidRefreshTokenException(); if (token.Revoked) { throw new RevokedRefreshTokenException(); } - var user = await _userRepository.GetAsync(token.UserId); - if (user is null) - { - throw new UserNotFoundException(token.UserId); - } + var user = await _userRepository.GetAsync(token.UserId) ?? throw new UserNotFoundException(token.UserId); var claims = user.Permissions.Any() ? new Dictionary> @@ -47,7 +37,8 @@ public async Task HandleAsync(UseRefreshToken command, CancellationToken cancell ["permissions"] = user.Permissions } : null; - var auth = _jwtProvider.Create(token.UserId, user.Name, user.Role, claims: claims); + + var auth = _jwtProvider.Create(token.UserId, user.Name, user.Roles, claims: claims); auth.RefreshToken = command.RefreshToken; _storage.Set(command.Id, auth); } diff --git a/src/apps/identity/Genocs.Identities.Application/Commands/LockUser.cs b/src/apps/identity/Genocs.Identities.Application/Commands/LockUser.cs index b76b57cc..14a94f9a 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/LockUser.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/LockUser.cs @@ -2,12 +2,7 @@ namespace Genocs.Identities.Application.Commands; -public class LockUser : ICommand +public class LockUser(Guid userId) : ICommand { - public Guid UserId { get; } - - public LockUser(Guid userId) - { - UserId = userId; - } + public Guid UserId { get; } = userId; } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Commands/RevokeAccessToken.cs b/src/apps/identity/Genocs.Identities.Application/Commands/RevokeAccessToken.cs index 0f695d2d..8e4a0b9f 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/RevokeAccessToken.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/RevokeAccessToken.cs @@ -2,12 +2,7 @@ namespace Genocs.Identities.Application.Commands; -public class RevokeAccessToken : ICommand +public class RevokeAccessToken(string accessToken) : ICommand { - public string AccessToken { get; } - - public RevokeAccessToken(string accessToken) - { - AccessToken = accessToken; - } + public string AccessToken { get; } = accessToken; } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Commands/RevokeRefreshToken.cs b/src/apps/identity/Genocs.Identities.Application/Commands/RevokeRefreshToken.cs index c9eff371..9ef6bf9e 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/RevokeRefreshToken.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/RevokeRefreshToken.cs @@ -2,12 +2,7 @@ namespace Genocs.Identities.Application.Commands; -public class RevokeRefreshToken : ICommand +public class RevokeRefreshToken(string refreshToken) : ICommand { - public string RefreshToken { get; } - - public RevokeRefreshToken(string refreshToken) - { - RefreshToken = refreshToken; - } + public string RefreshToken { get; } = refreshToken; } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Commands/SignIn.cs b/src/apps/identity/Genocs.Identities.Application/Commands/SignIn.cs index 145006bb..a4be5871 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/SignIn.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/SignIn.cs @@ -2,15 +2,9 @@ namespace Genocs.Identities.Application.Commands; -public class SignIn : ICommand +public class SignIn(string name, string password) : ICommand { public Guid Id { get; } = Guid.NewGuid(); - public string Name { get; set; } - public string Password { get; set; } - - public SignIn(string name, string password) - { - Name = name; - Password = password; - } + public string Name { get; set; } = name; + public string Password { get; set; } = password; } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Commands/UnlockUser.cs b/src/apps/identity/Genocs.Identities.Application/Commands/UnlockUser.cs index adc3f7b0..0adc8c50 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/UnlockUser.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/UnlockUser.cs @@ -2,12 +2,7 @@ namespace Genocs.Identities.Application.Commands; -public class UnlockUser : ICommand +public class UnlockUser(Guid userId) : ICommand { - public Guid UserId { get; } - - public UnlockUser(Guid userId) - { - UserId = userId; - } + public Guid UserId { get; } = userId; } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Commands/UseRefreshToken.cs b/src/apps/identity/Genocs.Identities.Application/Commands/UseRefreshToken.cs index d2353cdd..e41ca82a 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/UseRefreshToken.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/UseRefreshToken.cs @@ -2,13 +2,8 @@ namespace Genocs.Identities.Application.Commands; -public class UseRefreshToken : ICommand +public class UseRefreshToken(string refreshToken) : ICommand { public Guid Id { get; } = Guid.NewGuid(); - public string RefreshToken { get; } - - public UseRefreshToken(string refreshToken) - { - RefreshToken = refreshToken; - } + public string RefreshToken { get; } = refreshToken; } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/ContractAttribute.cs b/src/apps/identity/Genocs.Identities.Application/ContractAttribute.cs index 7d751a81..fe610505 100644 --- a/src/apps/identity/Genocs.Identities.Application/ContractAttribute.cs +++ b/src/apps/identity/Genocs.Identities.Application/ContractAttribute.cs @@ -1,6 +1,6 @@ namespace Genocs.Identities.Application; -// Marker -public class ContractAttribute : Attribute -{ -} \ No newline at end of file +/// +/// +/// +public class ContractAttribute : Attribute; \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/CorrelationIdFactory.cs b/src/apps/identity/Genocs.Identities.Application/CorrelationIdFactory.cs index c535997a..90cbcaf0 100644 --- a/src/apps/identity/Genocs.Identities.Application/CorrelationIdFactory.cs +++ b/src/apps/identity/Genocs.Identities.Application/CorrelationIdFactory.cs @@ -18,10 +18,7 @@ public CorrelationIdFactory( IHttpContextAccessor httpContextAccessor, HttpClientOptions httpClientOptions) { - if (httpClientOptions is null) - { - throw new ArgumentNullException(nameof(httpClientOptions)); - } + ArgumentNullException.ThrowIfNull(httpClientOptions); _messagePropertiesAccessor = messagePropertiesAccessor ?? throw new ArgumentNullException(nameof(messagePropertiesAccessor)); _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); diff --git a/src/apps/identity/Genocs.Identities.Application/DTO/AuthDto.cs b/src/apps/identity/Genocs.Identities.Application/DTO/AuthDto.cs index a5652115..5c524ca2 100644 --- a/src/apps/identity/Genocs.Identities.Application/DTO/AuthDto.cs +++ b/src/apps/identity/Genocs.Identities.Application/DTO/AuthDto.cs @@ -6,9 +6,9 @@ namespace Genocs.Identities.Application.DTO; public class AuthDto { public Guid UserId { get; set; } - public string Username { get; set; } - public string? Role { get; set; } + public string? Username { get; set; } + public IEnumerable? Roles { get; set; } public string? AccessToken { get; set; } - public string RefreshToken { get; set; } + public string? RefreshToken { get; set; } public long Expires { get; set; } } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/DTO/PagedDto.cs b/src/apps/identity/Genocs.Identities.Application/DTO/PagedDto.cs index 0f597c6c..da40694f 100644 --- a/src/apps/identity/Genocs.Identities.Application/DTO/PagedDto.cs +++ b/src/apps/identity/Genocs.Identities.Application/DTO/PagedDto.cs @@ -1,13 +1,12 @@ namespace Genocs.Identities.Application.DTO; - /// -/// It will be removed with Core implementation +/// It will be removed with Core implementation. /// -/// +/// The type. public class PagedDto { - public IEnumerable Items { get; set; } + public IEnumerable Items { get; set; } = new List(); public bool Empty => Items is null || !Items.Any(); public int CurrentPage { get; set; } public int ResultsPerPage { get; set; } diff --git a/src/apps/identity/Genocs.Identities.Application/DTO/UserDetailsDto.cs b/src/apps/identity/Genocs.Identities.Application/DTO/UserDetailsDto.cs index cf9a88f7..75b37af2 100644 --- a/src/apps/identity/Genocs.Identities.Application/DTO/UserDetailsDto.cs +++ b/src/apps/identity/Genocs.Identities.Application/DTO/UserDetailsDto.cs @@ -1,12 +1,11 @@ namespace Genocs.Identities.Application.DTO; /// -/// The UserDetails Dto +/// The UserDetails. /// public class UserDetailsDto : UserDto { - public string Email { get; set; } - public string Role { get; set; } + public string? Email { get; set; } + public IEnumerable? Roles { get; set; } public IEnumerable? Permissions { get; set; } - public decimal Funds { get; set; } } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/DTO/UserDto.cs b/src/apps/identity/Genocs.Identities.Application/DTO/UserDto.cs index e23786f3..105ac2f0 100644 --- a/src/apps/identity/Genocs.Identities.Application/DTO/UserDto.cs +++ b/src/apps/identity/Genocs.Identities.Application/DTO/UserDto.cs @@ -3,7 +3,7 @@ namespace Genocs.Identities.Application.DTO; public class UserDto { public Guid Id { get; set; } - public string Name { get; set; } + public string? Name { get; set; } public DateTime CreatedAt { get; set; } public bool Locked { get; set; } } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Domain/Constants/Policies.cs b/src/apps/identity/Genocs.Identities.Application/Domain/Constants/Policies.cs new file mode 100644 index 00000000..41fdc58d --- /dev/null +++ b/src/apps/identity/Genocs.Identities.Application/Domain/Constants/Policies.cs @@ -0,0 +1,7 @@ +namespace Genocs.Identities.Application.Domain.Constants; +public class Policies +{ + public const string UserOnly = nameof(UserOnly); + public const string AdminOnly = nameof(AdminOnly); + public const string UserOrAdmin = nameof(UserOrAdmin); +} \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Domain/Constants/Roles.cs b/src/apps/identity/Genocs.Identities.Application/Domain/Constants/Roles.cs new file mode 100644 index 00000000..9272f48b --- /dev/null +++ b/src/apps/identity/Genocs.Identities.Application/Domain/Constants/Roles.cs @@ -0,0 +1,8 @@ +namespace Genocs.Identities.Application.Domain.Constants; +public class Roles +{ + public const string User = "user"; + public const string Operator = "operator"; + public const string Manager = "manager"; + public const string Admin = "admin"; +} \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Domain/Entities/Role.cs b/src/apps/identity/Genocs.Identities.Application/Domain/Entities/Role.cs index 46b27c0b..9efa135d 100644 --- a/src/apps/identity/Genocs.Identities.Application/Domain/Entities/Role.cs +++ b/src/apps/identity/Genocs.Identities.Application/Domain/Entities/Role.cs @@ -1,9 +1,9 @@ +using Genocs.Identities.Application.Domain.Constants; + namespace Genocs.Identities.Application.Domain.Entities; public static class Role { - public const string User = "user"; - public const string Admin = "admin"; public static bool IsValid(string role) { @@ -14,6 +14,9 @@ public static bool IsValid(string role) role = role.ToLowerInvariant(); - return role == User || role == Admin; + return role == Roles.User || role == Roles.Admin; } + + public static bool IsValid(IEnumerable roles) + => roles.All(IsValid); } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Domain/Entities/User.cs b/src/apps/identity/Genocs.Identities.Application/Domain/Entities/User.cs index 9e69be07..82f2415b 100644 --- a/src/apps/identity/Genocs.Identities.Application/Domain/Entities/User.cs +++ b/src/apps/identity/Genocs.Identities.Application/Domain/Entities/User.cs @@ -6,19 +6,19 @@ public class User : AggregateRoot { public string Email { get; private set; } public string Name { get; private set; } - public string Role { get; private set; } + public IEnumerable Roles { get; private set; } public string Password { get; private set; } public DateTime CreatedAt { get; private set; } public IEnumerable? Permissions { get; private set; } public bool Locked { get; private set; } public User( - Guid id, + in Guid id, string email, string name, string password, - string role, - DateTime createdAt, + IEnumerable roles, + in DateTime createdAt, IEnumerable? permissions = null, bool locked = false) : base(id) @@ -38,16 +38,16 @@ public User( throw new InvalidPasswordException(); } - if (!Entities.Role.IsValid(role)) + if (!Role.IsValid(roles)) { - throw new InvalidRoleException(role); + throw new InvalidRoleException(string.Join(",", roles)); } Id = id; Email = email.ToLowerInvariant(); Name = name.Trim(); Password = password; - Role = role.ToLowerInvariant(); + Roles = roles.Select(c => c.ToLowerInvariant()); CreatedAt = createdAt; Permissions = permissions ?? Enumerable.Empty(); Locked = locked; diff --git a/src/apps/identity/Genocs.Identities.Application/Domain/Exceptions/InvalidRoleException.cs b/src/apps/identity/Genocs.Identities.Application/Domain/Exceptions/InvalidRoleException.cs index 0b4f2ac8..cec2b691 100644 --- a/src/apps/identity/Genocs.Identities.Application/Domain/Exceptions/InvalidRoleException.cs +++ b/src/apps/identity/Genocs.Identities.Application/Domain/Exceptions/InvalidRoleException.cs @@ -1,8 +1,6 @@ namespace Genocs.Identities.Application.Domain.Exceptions; -public class InvalidRoleException : DomainException +public class InvalidRoleException(string role) + : DomainException($"Invalid role: {role}.") { - public InvalidRoleException(string role) : base($"Invalid role: {role}.") - { - } } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Events/UserCreated.cs b/src/apps/identity/Genocs.Identities.Application/Events/UserCreated.cs index ff21f3e8..8e086593 100644 --- a/src/apps/identity/Genocs.Identities.Application/Events/UserCreated.cs +++ b/src/apps/identity/Genocs.Identities.Application/Events/UserCreated.cs @@ -2,16 +2,9 @@ namespace Genocs.Identities.Application.Events; -public class UserCreated : IEvent +public class UserCreated(Guid userId, string name, IEnumerable roles) : IEvent { - public Guid UserId { get; } - public string Name { get; } - public string Role { get; } - - public UserCreated(Guid userId, string name, string role) - { - UserId = userId; - Name = name; - Role = role; - } + public Guid UserId { get; } = userId; + public string Name { get; } = name; + public IEnumerable Roles { get; } = roles; } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Exceptions/AppException.cs b/src/apps/identity/Genocs.Identities.Application/Exceptions/AppException.cs index f9336e85..7f58a30f 100644 --- a/src/apps/identity/Genocs.Identities.Application/Exceptions/AppException.cs +++ b/src/apps/identity/Genocs.Identities.Application/Exceptions/AppException.cs @@ -1,9 +1,3 @@ namespace Genocs.Identities.Application.Exceptions; -public abstract class AppException : Exception -{ - protected AppException(string message) - : base(message) - { - } -} \ No newline at end of file +public abstract class AppException(string message) : Exception(message); diff --git a/src/apps/identity/Genocs.Identities.Application/Exceptions/ExceptionToMessageMapper.cs b/src/apps/identity/Genocs.Identities.Application/Exceptions/ExceptionToMessageMapper.cs index dd3a5b58..94122bb8 100644 --- a/src/apps/identity/Genocs.Identities.Application/Exceptions/ExceptionToMessageMapper.cs +++ b/src/apps/identity/Genocs.Identities.Application/Exceptions/ExceptionToMessageMapper.cs @@ -4,6 +4,5 @@ namespace Genocs.Identities.Application.Exceptions; internal sealed class ExceptionToMessageMapper : IExceptionToMessageMapper { - public object Map(Exception exception, object message) => null; -} - + public object? Map(Exception exception, object message) => null; +} \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Extensions.cs b/src/apps/identity/Genocs.Identities.Application/Extensions.cs index 660697cc..9a6b3b9f 100644 --- a/src/apps/identity/Genocs.Identities.Application/Extensions.cs +++ b/src/apps/identity/Genocs.Identities.Application/Extensions.cs @@ -1,3 +1,4 @@ +using System.Security.Claims; using System.Text; using Genocs.Auth; using Genocs.Common.Configurations; @@ -8,6 +9,7 @@ using Genocs.HTTP; using Genocs.Identities.Application.Commands; using Genocs.Identities.Application.Decorators; +using Genocs.Identities.Application.Domain.Constants; using Genocs.Identities.Application.Domain.Repositories; using Genocs.Identities.Application.Exceptions; using Genocs.Identities.Application.Logging; @@ -60,7 +62,6 @@ public static async Task AddCoreAsync(this IGenocsBuilder builde .AddInMemoryCommandDispatcher() .AddInMemoryEventDispatcher() .AddInMemoryQueryDispatcher() - .AddJwt() .AddHttpClient(); await builder.AddRabbitMQAsync(); @@ -82,6 +83,23 @@ public static async Task AddCoreAsync(this IGenocsBuilder builde builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(OutboxCommandHandlerDecorator<>)); builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(OutboxEventHandlerDecorator<>)); + builder.Services.AddAuthorizationBuilder() + .AddPolicy(Policies.AdminOnly, builder => builder.RequireClaim(ClaimTypes.Role, Roles.Admin).Build()) + .AddPolicy(Policies.UserOnly, builder => builder.RequireClaim(ClaimTypes.Role, Roles.User).Build()) + .AddPolicy(Policies.UserOrAdmin, builder => builder.RequireClaim(ClaimTypes.Role, Roles.User, Roles.Admin).Build()); + + //builder.Services.AddAuthorizationBuilder() + // .SetFallbackPolicy(new AuthorizationPolicyBuilder() + // .RequireAuthenticatedUser() + // .Build()) + // .AddPolicy(Policies.UserOnly, policy => policy.RequireAssertion(context + // => context.User.HasClaim(ClaimTypes.Role, Roles.User))) + // .AddPolicy(Policies.AdminOnly, policy => policy.RequireAssertion(context + // => context.User.HasClaim(ClaimTypes.Role, Roles.Admin))) + // .AddPolicy(Policies.UserOrAdmin, policy => policy.RequireAssertion(context + // => context.User.HasClaim(ClaimTypes.Role, Roles.User) + // || context.User.HasClaim(ClaimTypes.Role, Roles.Admin))); + return builder; } @@ -92,16 +110,16 @@ public static IApplicationBuilder UseCore(this IApplicationBuilder app) { app.UseMiddleware() .UseErrorHandler() - //.UseJaeger() .UseSwaggerDocs() .UseGenocs() - .UseAccessTokenValidator() + .UseAccessTokenValidator() // Implement the Authorization. .UseMongo() .UsePublicContracts() .UseAuthentication() .UseMetrics() .UseRabbitMQ() - .SubscribeCommand(); + .SubscribeCommand() + .SubscribeCommand(); return app; } @@ -121,7 +139,7 @@ internal static string GetSpanContext(this IMessageProperties messageProperties, return string.Empty; } - if (messageProperties.Headers.TryGetValue(header, out var span) && span is byte[] spanBytes) + if (messageProperties.Headers.TryGetValue(header, out object? span) && span is byte[] spanBytes) { return Encoding.UTF8.GetString(spanBytes); } diff --git a/src/apps/identity/Genocs.Identities.Application/Genocs.Identities.Application.csproj b/src/apps/identity/Genocs.Identities.Application/Genocs.Identities.Application.csproj index 95c70587..8b964e15 100644 --- a/src/apps/identity/Genocs.Identities.Application/Genocs.Identities.Application.csproj +++ b/src/apps/identity/Genocs.Identities.Application/Genocs.Identities.Application.csproj @@ -34,8 +34,4 @@ - - - - diff --git a/src/apps/identity/Genocs.Identities.Application/Logging/LogContextMiddleware.cs b/src/apps/identity/Genocs.Identities.Application/Logging/LogContextMiddleware.cs index d64f034e..6f796f20 100644 --- a/src/apps/identity/Genocs.Identities.Application/Logging/LogContextMiddleware.cs +++ b/src/apps/identity/Genocs.Identities.Application/Logging/LogContextMiddleware.cs @@ -15,7 +15,7 @@ public LogContextMiddleware(ICorrelationIdFactory correlationIdFactory) public async Task InvokeAsync(HttpContext context, RequestDelegate next) { - var correlationId = _correlationIdFactory.Create(); + string? correlationId = _correlationIdFactory.Create(); using (LogContext.PushProperty("CorrelationId", correlationId)) { await next(context); diff --git a/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/RefreshTokenDocument.cs b/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/RefreshTokenDocument.cs index 48820859..e6e70fbe 100644 --- a/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/RefreshTokenDocument.cs +++ b/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/RefreshTokenDocument.cs @@ -24,7 +24,7 @@ public RefreshTokenDocument(RefreshToken refreshToken) RevokedAt = refreshToken.RevokedAt; } - public RefreshToken ToEntity() => new RefreshToken(Id, UserId, Token, CreatedAt, RevokedAt); + public RefreshToken ToEntity() => new(Id, UserId, Token, CreatedAt, RevokedAt); public bool IsTransient() { diff --git a/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/UserDocument.cs b/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/UserDocument.cs index 06ddeb7d..54b92e5b 100644 --- a/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/UserDocument.cs +++ b/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/UserDocument.cs @@ -1,4 +1,3 @@ -using Genocs.Common.Types; using Genocs.Core.Domain.Entities; using Genocs.Identities.Application.Domain.Entities; @@ -9,7 +8,7 @@ public class UserDocument : IEntity public Guid Id { get; set; } public string Email { get; set; } public string Name { get; set; } - public string Role { get; set; } + public IEnumerable Roles { get; set; } public string Password { get; set; } public DateTime CreatedAt { get; set; } public IEnumerable? Permissions { get; set; } @@ -24,14 +23,14 @@ public UserDocument(User user) Id = user.Id; Email = user.Email; Name = user.Name; - Role = user.Role; + Roles = user.Roles; Password = user.Password; CreatedAt = user.CreatedAt; Permissions = user.Permissions; Locked = user.Locked; } - public User ToEntity() => new User(Id, Email, Name, Password, Role, CreatedAt, Permissions, Locked); + public User ToEntity() => new(Id, Email, Name, Password, Roles, CreatedAt, Permissions, Locked); public bool IsTransient() { diff --git a/src/apps/identity/Genocs.Identities.Application/Mongo/Queries/Handlers/BrowseUsersHandler.cs b/src/apps/identity/Genocs.Identities.Application/Mongo/Queries/Handlers/BrowseUsersHandler.cs index 1f716a2f..97d64e29 100644 --- a/src/apps/identity/Genocs.Identities.Application/Mongo/Queries/Handlers/BrowseUsersHandler.cs +++ b/src/apps/identity/Genocs.Identities.Application/Mongo/Queries/Handlers/BrowseUsersHandler.cs @@ -8,14 +8,9 @@ namespace Genocs.Identities.Application.Mongo.Queries.Handlers; -public class BrowseUsersHandler : IQueryHandler> +public class BrowseUsersHandler(IMongoDatabase database) : IQueryHandler> { - private readonly IMongoDatabase _database; - - public BrowseUsersHandler(IMongoDatabase database) - { - _database = database; - } + private readonly IMongoDatabase _database = database; public async Task> HandleAsync(BrowseUsers query, CancellationToken cancellationToken = default) { @@ -25,6 +20,7 @@ public async Task> HandleAsync(BrowseUsers query, Cancellation .PaginateAsync(query); var pagedResult = PagedResult.From(result, result.Items.Select(x => Map(x))); + return new PagedDto { CurrentPage = pagedResult.CurrentPage, @@ -36,7 +32,7 @@ public async Task> HandleAsync(BrowseUsers query, Cancellation } private static UserDto Map(UserDocument user) - => new UserDto + => new() { Id = user.Id, Name = user.Name, diff --git a/src/apps/identity/Genocs.Identities.Application/Mongo/Queries/Handlers/GetUserHandler.cs b/src/apps/identity/Genocs.Identities.Application/Mongo/Queries/Handlers/GetUserHandler.cs index ff43d3fb..f3719321 100644 --- a/src/apps/identity/Genocs.Identities.Application/Mongo/Queries/Handlers/GetUserHandler.cs +++ b/src/apps/identity/Genocs.Identities.Application/Mongo/Queries/Handlers/GetUserHandler.cs @@ -26,7 +26,7 @@ public GetUserHandler(IMongoDbBaseRepository userRepository) Id = user.Id, Email = user.Email, Name = user.Name, - Role = user.Role, + Roles = user.Roles, CreatedAt = user.CreatedAt, Locked = user.Locked, Permissions = user.Permissions, diff --git a/src/apps/identity/Genocs.Identities.Application/Services/IJwtProvider.cs b/src/apps/identity/Genocs.Identities.Application/Services/IJwtProvider.cs index eca33933..0dafc627 100644 --- a/src/apps/identity/Genocs.Identities.Application/Services/IJwtProvider.cs +++ b/src/apps/identity/Genocs.Identities.Application/Services/IJwtProvider.cs @@ -7,11 +7,5 @@ namespace Genocs.Identities.Application.Services; /// public interface IJwtProvider { - AuthDto Create( - Guid userId, - string username, - string role, - string? audience = null, - IDictionary>? claims = null); + AuthDto Create(Guid userId, string username, IEnumerable roles, string? audience = null, IDictionary>? claims = null); } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Services/JwtProvider.cs b/src/apps/identity/Genocs.Identities.Application/Services/JwtProvider.cs index 093b1a16..71a3bdd2 100644 --- a/src/apps/identity/Genocs.Identities.Application/Services/JwtProvider.cs +++ b/src/apps/identity/Genocs.Identities.Application/Services/JwtProvider.cs @@ -3,30 +3,20 @@ namespace Genocs.Identities.Application.Services; -public class JwtProvider : IJwtProvider +public class JwtProvider(IJwtHandler jwtHandler) : IJwtProvider { - private readonly IJwtHandler _jwtHandler; + private readonly IJwtHandler _jwtHandler = jwtHandler; - public JwtProvider(IJwtHandler jwtHandler) + public AuthDto Create(Guid userId, string username, IEnumerable roles, string? audience = null, IDictionary>? claims = null) { - _jwtHandler = jwtHandler; - } - - public AuthDto Create( - Guid userId, - string username, - string role, - string? audience = null, - IDictionary>? claims = null) - { - var jwt = _jwtHandler.CreateToken(userId.ToString("N"), role, audience, claims); + var jwt = _jwtHandler.CreateToken(userId.ToString("N"), roles, audience, claims); return new AuthDto { UserId = userId, Username = username, AccessToken = jwt.AccessToken, - Role = jwt.Role, + Roles = jwt.Roles, Expires = jwt.Expires }; } diff --git a/src/apps/identity/Genocs.Identities.Application/Services/MessageBroker.cs b/src/apps/identity/Genocs.Identities.Application/Services/MessageBroker.cs index d2ae9507..d8f42b85 100644 --- a/src/apps/identity/Genocs.Identities.Application/Services/MessageBroker.cs +++ b/src/apps/identity/Genocs.Identities.Application/Services/MessageBroker.cs @@ -31,10 +31,7 @@ public MessageBroker( RabbitMQOptions options, ILogger logger) { - if (options is null) - { - throw new ArgumentNullException(nameof(options)); - } + ArgumentNullException.ThrowIfNull(options); _busPublisher = busPublisher ?? throw new ArgumentNullException(nameof(busPublisher)); _outbox = outbox ?? throw new ArgumentNullException(nameof(outbox)); @@ -61,8 +58,7 @@ private async Task PublishAsync(IEnumerable? events) string? originatedMessageId = messageProperties?.MessageId; string? correlationId = _correlationIdFactory.Create(); string? spanContext = messageProperties?.GetSpanContext(_spanContextHeader); - object correlationContext = _contextAccessor.CorrelationContext ?? - _httpContextAccessor.GetCorrelationContext(); + object correlationContext = _contextAccessor.CorrelationContext ?? _httpContextAccessor.GetCorrelationContext(); var headers = new Dictionary(); @@ -77,24 +73,11 @@ private async Task PublishAsync(IEnumerable? events) _logger.LogTrace($"Publishing integration event: {@event.GetType().Name.Underscore()} [ID: '{messageId}']."); if (_outbox.Enabled) { - await _outbox.SendAsync( - @event, - originatedMessageId, - messageId, - correlationId, - spanContext, - correlationContext, - headers); + await _outbox.SendAsync(@event, originatedMessageId, messageId, correlationId, spanContext, correlationContext, headers); continue; } - await _busPublisher.PublishAsync( - @event, - messageId, - correlationId, - spanContext, - correlationContext, - headers); + await _busPublisher.PublishAsync(@event, messageId, correlationId, spanContext, correlationContext, headers); } } } \ No newline at end of file diff --git a/src/apps/identity/Genocs.Identities.Application/Services/Rng.cs b/src/apps/identity/Genocs.Identities.Application/Services/Rng.cs index e7b34ac6..3faef575 100644 --- a/src/apps/identity/Genocs.Identities.Application/Services/Rng.cs +++ b/src/apps/identity/Genocs.Identities.Application/Services/Rng.cs @@ -4,14 +4,14 @@ namespace Genocs.Identities.Application.Services; public class Rng : IRng { - private static readonly string[] SpecialChars = new[] { "/", "\\", "=", "+", "?", ":", "&" }; + private static readonly string[] SpecialChars = ["/", "\\", "=", "+", "?", ":", "&"]; public string Generate(int length = 50, bool removeSpecialChars = true) { using var rng = RandomNumberGenerator.Create(); - var bytes = new byte[length]; + byte[] bytes = new byte[length]; rng.GetBytes(bytes); - var result = Convert.ToBase64String(bytes); + string result = Convert.ToBase64String(bytes); return removeSpecialChars ? SpecialChars.Aggregate(result, (current, chars) => current.Replace(chars, string.Empty)) diff --git a/src/apps/identity/Genocs.Identities.WebApi/Genocs.Identities.WebApi.csproj b/src/apps/identity/Genocs.Identities.WebApi/Genocs.Identities.WebApi.csproj index 2547ab54..5956cc38 100644 --- a/src/apps/identity/Genocs.Identities.WebApi/Genocs.Identities.WebApi.csproj +++ b/src/apps/identity/Genocs.Identities.WebApi/Genocs.Identities.WebApi.csproj @@ -4,26 +4,27 @@ net9.0 false false - _genocs - Linux - ..\.. + Genocs - + - + + + + - + - + - + diff --git a/src/apps/identity/Genocs.Identities.WebApi/Program.cs b/src/apps/identity/Genocs.Identities.WebApi/Program.cs index 37e23570..29f53077 100644 --- a/src/apps/identity/Genocs.Identities.WebApi/Program.cs +++ b/src/apps/identity/Genocs.Identities.WebApi/Program.cs @@ -1,39 +1,39 @@ +using Genocs.Auth; using Genocs.Core.Builders; using Genocs.Identities.Application; using Genocs.Identities.Application.Commands; +using Genocs.Identities.Application.Domain.Constants; using Genocs.Identities.Application.DTO; using Genocs.Identities.Application.Queries; using Genocs.Identities.Application.Services; -using Genocs.LoadBalancing.Fabio; using Genocs.Logging; -using Genocs.Secrets.Vault; using Genocs.WebApi; using Genocs.WebApi.CQRS; using Serilog; -using Genocs.Discovery.Consul; StaticLogger.EnsureInitialized(); var builder = WebApplication.CreateBuilder(args); builder.Host - .UseLogging() - .UseVault(); + .UseLogging(); IGenocsBuilder gnxBuilder = await builder .AddGenocs() + .AddJwt() .AddWebApi() - .AddConsul() - .AddFabio() .AddCoreAsync(); gnxBuilder.Build(); -var app = builder.Build(); -app.MapDefaultEndpoints(); +var app = builder.Build(); +// Setup the Core services. The order matters, please keep it as is. app.UseCore(); +// Map default endpoints to provide support for health checks +app.MapDefaultEndpoints(); + app.UseDispatcherEndpoints(endpoints => endpoints .Post("sign-in", afterDispatch: (cmd, ctx) => { @@ -45,17 +45,22 @@ ctx.Response.Headers.Append("user-id", cmd.UserId.ToString()); return Task.CompletedTask; }) - .Post("access-tokens/revoke", auth: true, roles: "admin") + .Post("onboarding", auth: true, policies: [Policies.AdminOnly], afterDispatch: (cmd, ctx) => + { + ctx.Response.Headers.Append("user-id", cmd.UserId.ToString()); + return Task.CompletedTask; + }) + .Post("access-tokens/revoke", auth: true, policies: [Policies.AdminOnly]) .Post("refresh-tokens/use", afterDispatch: (cmd, ctx) => { var auth = ctx.RequestServices.GetRequiredService().Get(cmd.Id); return ctx.Response.WriteJsonAsync(auth); }) - .Post("refresh-tokens/revoke", auth: true, roles: "admin") + .Post("refresh-tokens/revoke", auth: true, policies: [Policies.AdminOnly]) .Get("users/{userId:guid}", auth: true) .Get>("users", auth: true) - .Put("users/{userId:guid}/lock", auth: true, roles: "admin") - .Put("users/{userId:guid}/unlock", auth: true, roles: "admin")); + .Put("users/{userId:guid}/lock", auth: true, policies: [Policies.AdminOnly]) + .Put("users/{userId:guid}/unlock", auth: true, policies: [Policies.AdminOnly])); app.Run(); diff --git a/src/apps/identity/Genocs.Identities.WebApi/Properties/launchSettings.json b/src/apps/identity/Genocs.Identities.WebApi/Properties/launchSettings.json index e1d97584..c05da594 100644 --- a/src/apps/identity/Genocs.Identities.WebApi/Properties/launchSettings.json +++ b/src/apps/identity/Genocs.Identities.WebApi/Properties/launchSettings.json @@ -2,7 +2,6 @@ "profiles": { "Local": { "commandName": "Project", - "launchBrowser": true, "applicationUrl": "https://localhost:5510;http://localhost:5511", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/src/apps/identity/Genocs.Identities.WebApi/appsettings.json b/src/apps/identity/Genocs.Identities.WebApi/appsettings.json index 0f06a4c6..a20c6d67 100644 --- a/src/apps/identity/Genocs.Identities.WebApi/appsettings.json +++ b/src/apps/identity/Genocs.Identities.WebApi/appsettings.json @@ -8,29 +8,30 @@ "displayVersion": true }, "consul": { - "enabled": true, + "enabled": false, "url": "http://localhost:8500", "service": "identity-service", "address": "docker.for.mac.localhost", "port": "5511", - "pingEnabled": true, + "pingEnabled": false, "pingEndpoint": "healthz", "pingInterval": 3, - "removeAfterInterval": 3 + "removeAfterInterval": 3, + "requestRetries": 3 }, "fabio": { - "enabled": true, + "enabled": false, "url": "http://localhost:9999", "service": "identity-service", "requestRetries": 3 }, "httpClient": { - "type": "fabio", + "type": "", "retries": 3, "services": { }, "requestMasking": { - "enabled": true, + "enabled": false, "maskTemplate": "*****" }, "correlationIdHeader": "x-correlation-id" @@ -83,7 +84,7 @@ "jaeger": { "enabled": true, "serviceName": "identity", - "udpHost": "localhost", + "udpHost": "http://localhost:4317", "udpPort": 6831, "maxPacketSize": 65000, "sampler": "const", @@ -94,17 +95,50 @@ "/metrics" ] }, + "openTelemetry": { + "enabled": true, + "exporter": { + "enabled": true, + "otlpEndpoint": "http://localhost:4317", + "protocol": "Grpc", + "processorType": "Batch", + "maxQueueSize": 2048, + "scheduledDelayMilliseconds": 5000, + "exporterTimeoutMilliseconds": 30000, + "maxExportBatchSize": 512 + }, + "console": { + "enabled": true, + "enableTracing": true, + "enableMetrics": true, + "enableLogging": true + }, + "azure": { + "enabled": true, + "enableTracing": true, + "enableMetrics": true, + "enableLogging": true, + "connectionString": "InstrumentationKey=1496274b-bda7-4ac6-88ab-9f73b4d3c7b8;IngestionEndpoint=https://italynorth-0.in.applicationinsights.azure.com/;LiveEndpoint=https://italynorth.livediagnostics.monitor.azure.com/;ApplicationId=c417f66d-3611-48a2-80fe-5a6d302bed4f" + } + }, "jwt": { - "certificate": { + "enabled": true, + "allowAnonymousEndpoints": [], + "_certificate": { "location": "certs/localhost.pfx", "password": "test", "rawData": "" }, + "expiryMinutes": 60, "issuer": "genocs-identity-service", + "issuerSigningKey": "THIS IS USED TO SIGN AND VERIFY JWT TOKENS, REPLACE IT WITH YOUR OWN SECRET, IT CAN BE ANY STRING", "validIssuer": "genocs-identity-service", "validateAudience": false, - "validateIssuer": true, - "validateLifetime": true, + "validateIssuer": false, + "validateLifetime": false, + "requireExpirationTime": false, + "requireSignedTokens": false, + "validateIssuerSigningKey": false, "expiry": "01:00:00" }, "metrics": { @@ -188,19 +222,45 @@ }, "maxProducerChannels": 1000 }, + "elk": { + "enabled": false, + "url": "http://localhost:9200", + "indexFormat": "identity-service-{0:yyyy.MM.dd}", + "basicAuthEnabled": false, + "username": "user", + "password": "secret" + }, "redis": { "connectionString": "localhost", "instance": "identity:", "database": 0 }, + "restEase": { + "loadBalancer": "", + "services": [] + }, "swagger": { "enabled": true, "reDocEnabled": false, - "name": "v1", - "title": "Identities", + "name": "Identities", + "title": "Identities Service", "version": "v1", + "description": "identity Service", "routePrefix": "swagger", - "includeSecurity": true + "includeSecurity": true, + "contactName": "Giovanni Nocco", + "contactEmail": "giovanni.nocco@genocs.com", + "contactUrl": "https://www.genocs.com", + "licenseName": "MIT", + "licenseUrl": "https://www.genocs.com/license.html", + "termsOfService": "https://www.genocs.com/terms_and_conditions.html", + "serializeAsOpenApiV2": true, + "servers": [ + { + "url": "http://localhost:5511", + "description": "Local version to be used for development" + } + ] }, "security": { "certificate": { @@ -231,7 +291,7 @@ "mongo": { "type": "database", "roleName": "identity-service", - "enabled": true, + "enabled": false, "autoRenewal": true, "templates": { "connectionString": "mongodb://{{username}}:{{password}}@localhost:27017" @@ -241,5 +301,11 @@ }, "webApi": { "bindRequestFromRoute": true + }, + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + } } } \ No newline at end of file diff --git a/src/apps/local copy.env b/src/apps/local copy.env new file mode 100644 index 00000000..14349d0a --- /dev/null +++ b/src/apps/local copy.env @@ -0,0 +1,10 @@ +# Compose supports declaring default environment variables in an environment file named .env placed in the folder docker-compose command is executed from (current working directory). +# Compose expects each line in an env file to be in VAR=VAL format. Lines beginning with # (i.e. comments) are ignored, as are blank lines. +# Note: Values present in the environment at runtime will always override those defined inside the .env file. Similarly, values passed via command-line arguments take precedence as well. + +# The IP below should be swapped to your real IP or DNS name, like 192.168.88.248, etc. if testing from remote browsers or mobile devices + +PROJECT_EXTERNAL_DNS_NAME_OR_IP=localhost + +# The docker image version +DOCKER_IMAGE_TAG=6.0.0 diff --git a/src/apps/order-webapi.dockerfile b/src/apps/order-webapi.dockerfile index ad956d91..7777f490 100644 --- a/src/apps/order-webapi.dockerfile +++ b/src/apps/order-webapi.dockerfile @@ -8,6 +8,11 @@ EXPOSE 443 FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env WORKDIR /src COPY ["orders/Genocs.Orders.WebApi", "Genocs.Orders.WebApi/"] +COPY ["Directory.Build.props", "Directory.Build.props"] +COPY ["Directory.Build.targets", "Directory.Build.targets"] +COPY ["NuGet.config", "NuGet.config"] +COPY ["dotnet.ruleset", "dotnet.ruleset"] +COPY ["stylecop.json", "stylecop.json"] WORKDIR "/src/Genocs.Orders.WebApi" diff --git a/src/apps/orders/Genocs.Orders.WebApi/Commands/Handlers/CreateOrderHandler.cs b/src/apps/orders/Genocs.Orders.WebApi/Commands/Handlers/CreateOrderHandler.cs index 880aa814..29bc880d 100644 --- a/src/apps/orders/Genocs.Orders.WebApi/Commands/Handlers/CreateOrderHandler.cs +++ b/src/apps/orders/Genocs.Orders.WebApi/Commands/Handlers/CreateOrderHandler.cs @@ -39,12 +39,7 @@ public async Task HandleAsync(CreateOrder command, CancellationToken cancellatio } _logger.LogInformation($"Fetching the product for order with id: {command.OrderId}..."); - var productDto = await _productServiceClient.GetAsync(command.ProductId); - if (productDto is null) - { - throw new InvalidOperationException($"Product '{command.ProductId}' was not found. Requested for order '{command.OrderId}'"); - } - + var productDto = await _productServiceClient.GetAsync(command.ProductId) ?? throw new InvalidOperationException($"Product '{command.ProductId}' was not found. Requested for order '{command.OrderId}'"); _logger.LogInformation($"Order '{command.OrderId}' will cost '{productDto.UnitPrice}'$."); var order = new Order(command.OrderId, command.CustomerId, productDto.UnitPrice); @@ -52,7 +47,7 @@ public async Task HandleAsync(CreateOrder command, CancellationToken cancellatio _logger.LogInformation($"Created order '{command.OrderId}' for customer '{command.CustomerId}'."); - string? spanContext = "TODO: Genocs"; + string? spanContext = $"Genocs: CreateOrder-{command.OrderId}"; var @event = new OrderCreated(order.Id); if (_outbox.Enabled) { diff --git a/src/apps/orders/Genocs.Orders.WebApi/Genocs.Orders.WebApi.csproj b/src/apps/orders/Genocs.Orders.WebApi/Genocs.Orders.WebApi.csproj index 2481452e..4622c3d5 100644 --- a/src/apps/orders/Genocs.Orders.WebApi/Genocs.Orders.WebApi.csproj +++ b/src/apps/orders/Genocs.Orders.WebApi/Genocs.Orders.WebApi.csproj @@ -51,4 +51,8 @@ + + + + diff --git a/src/apps/orders/Genocs.Orders.WebApi/Properties/launchSettings.json b/src/apps/orders/Genocs.Orders.WebApi/Properties/launchSettings.json index 24d9a114..e396d784 100644 --- a/src/apps/orders/Genocs.Orders.WebApi/Properties/launchSettings.json +++ b/src/apps/orders/Genocs.Orders.WebApi/Properties/launchSettings.json @@ -2,7 +2,6 @@ "profiles": { "Local": { "commandName": "Project", - "launchBrowser": true, "applicationUrl": "https://localhost:5530;http://localhost:5531", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "development" diff --git a/src/apps/orders/Genocs.Orders.WebApi/Queries/GetOrder.cs b/src/apps/orders/Genocs.Orders.WebApi/Queries/GetOrder.cs index 73507e32..80387639 100644 --- a/src/apps/orders/Genocs.Orders.WebApi/Queries/GetOrder.cs +++ b/src/apps/orders/Genocs.Orders.WebApi/Queries/GetOrder.cs @@ -1,6 +1,5 @@ using Genocs.Core.CQRS.Queries; using Genocs.Orders.WebApi.DTO; -using System; namespace Genocs.Orders.WebApi.Queries; diff --git a/src/apps/orders/Genocs.Orders.WebApi/appsettings.json b/src/apps/orders/Genocs.Orders.WebApi/appsettings.json index a2ad9a9e..024b63f3 100644 --- a/src/apps/orders/Genocs.Orders.WebApi/appsettings.json +++ b/src/apps/orders/Genocs.Orders.WebApi/appsettings.json @@ -26,13 +26,13 @@ "requestRetries": 3 }, "httpClient": { - "type": "", + "type": "fabio", "retries": 3, "services": { - "products": "https://localhost:5520" + "products": "products-service" }, "requestMasking": { - "enabled": true, + "enabled": false, "maskTemplate": "*****" }, "correlationIdHeader": "x-correlation-id" @@ -91,22 +91,58 @@ "maxQueueSize": 2048, "scheduledDelayMilliseconds": 5000, "exporterTimeoutMilliseconds": 30000, - "maxExportBatchSize": 512 + "maxExportBatchSize": 512, + "excludePaths": [ + "/", + "/healthz", + "/alive", + "/metrics" + ] + }, + "openTelemetry": { + "enabled": true, + "exporter": { + "enabled": true, + "otlpEndpoint": "http://localhost:4317", + "protocol": "Grpc", + "processorType": "Batch", + "maxQueueSize": 2048, + "scheduledDelayMilliseconds": 5000, + "exporterTimeoutMilliseconds": 30000, + "maxExportBatchSize": 512 + }, + "console": { + "enabled": true, + "enableTracing": true, + "enableMetrics": true, + "enableLogging": true + }, + "azure": { + "enabled": true, + "enableTracing": true, + "enableMetrics": true, + "enableLogging": true, + "connectionString": "InstrumentationKey=1496274b-bda7-4ac6-88ab-9f73b4d3c7b8;IngestionEndpoint=https://italynorth-0.in.applicationinsights.azure.com/;LiveEndpoint=https://italynorth.livediagnostics.monitor.azure.com/;ApplicationId=c417f66d-3611-48a2-80fe-5a6d302bed4f" + } }, "jwt": { "enabled": true, "allowAnonymousEndpoints": [], - "certificate": { + "_certificate": { "location": "certs/localhost.pfx", "password": "test", "rawData": "" }, - "expiryMinutes": 30, + "expiryMinutes": 60, "issuer": "genocs-identity-service", + "issuerSigningKey": "THIS IS USED TO SIGN AND VERIFY JWT TOKENS, REPLACE IT WITH YOUR OWN SECRET, IT CAN BE ANY STRING", "validIssuer": "genocs-identity-service", "validateAudience": false, - "validateIssuer": true, - "validateLifetime": true, + "validateIssuer": false, + "validateLifetime": false, + "requireExpirationTime": false, + "requireSignedTokens": false, + "validateIssuerSigningKey": false, "expiry": "01:00:00" }, "metrics": { @@ -121,7 +157,7 @@ }, "mongodb": { "connectionString": "mongodb://localhost:27017", - "database": "orders-service", + "database": "orders", "seed": false, "enableTracing": true }, @@ -212,8 +248,8 @@ "reDocEnabled": false, "name": "Orders", "title": "Orders Service", - "version": "v01", - "description": "Orders Service", + "version": "v1", + "description": "Order Service", "routePrefix": "swagger", "includeSecurity": true, "contactName": "Giovanni Nocco", @@ -269,5 +305,11 @@ }, "webApi": { "bindRequestFromRoute": true + }, + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + } } } \ No newline at end of file diff --git a/src/apps/product-webapi.dockerfile b/src/apps/product-webapi.dockerfile index 0ca2c5f9..7adc2bf9 100644 --- a/src/apps/product-webapi.dockerfile +++ b/src/apps/product-webapi.dockerfile @@ -8,6 +8,11 @@ EXPOSE 443 FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env WORKDIR /src COPY ["products/Genocs.Products.WebApi", "Genocs.Products.WebApi/"] +COPY ["Directory.Build.props", "Directory.Build.props"] +COPY ["Directory.Build.targets", "Directory.Build.targets"] +COPY ["NuGet.config", "NuGet.config"] +COPY ["dotnet.ruleset", "dotnet.ruleset"] +COPY ["stylecop.json", "stylecop.json"] WORKDIR "/src/Genocs.Products.WebApi" diff --git a/src/apps/products/Genocs.Products.WebApi/Genocs.Products.WebApi.csproj b/src/apps/products/Genocs.Products.WebApi/Genocs.Products.WebApi.csproj index ce33a868..9e09841f 100644 --- a/src/apps/products/Genocs.Products.WebApi/Genocs.Products.WebApi.csproj +++ b/src/apps/products/Genocs.Products.WebApi/Genocs.Products.WebApi.csproj @@ -4,7 +4,7 @@ net9.0 false false - _Genocs + Genocs @@ -50,9 +50,4 @@ - - - - - diff --git a/src/apps/products/Genocs.Products.WebApi/Program.cs b/src/apps/products/Genocs.Products.WebApi/Program.cs index d3982b3b..b3bec71a 100644 --- a/src/apps/products/Genocs.Products.WebApi/Program.cs +++ b/src/apps/products/Genocs.Products.WebApi/Program.cs @@ -64,7 +64,7 @@ // Build the Genocs builder gnxBuilder.Build(); -// Build the Application +// Build the Application var app = builder.Build(); app.UseGenocs() diff --git a/src/apps/products/Genocs.Products.WebApi/Properties/launchSettings.json b/src/apps/products/Genocs.Products.WebApi/Properties/launchSettings.json index d6e07839..c4dbac95 100644 --- a/src/apps/products/Genocs.Products.WebApi/Properties/launchSettings.json +++ b/src/apps/products/Genocs.Products.WebApi/Properties/launchSettings.json @@ -2,7 +2,6 @@ "profiles": { "Local": { "commandName": "Project", - "launchBrowser": true, "applicationUrl": "https://localhost:5520;http://localhost:5521", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "development" diff --git a/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/GetProductHandler.cs b/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/GetProductHandler.cs index 2fae624e..4053be04 100644 --- a/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/GetProductHandler.cs +++ b/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/GetProductHandler.cs @@ -8,6 +8,8 @@ public class GetProductHandler : IQueryHandler { private readonly IMongoDbBaseRepository _repository; + private static readonly Random _random = new(); + public GetProductHandler(IMongoDbBaseRepository repository) { _repository = repository; @@ -15,6 +17,13 @@ public GetProductHandler(IMongoDbBaseRepository repository public async Task HandleAsync(GetProduct query, CancellationToken cancellationToken = default) { + int currentValue = _random.Next(0, 100); + + if (currentValue < 5) + { + throw new Exception("Random exception"); + } + var product = await _repository.GetAsync(query.ProductId); return product is null diff --git a/src/apps/products/Genocs.Products.WebApi/appsettings.json b/src/apps/products/Genocs.Products.WebApi/appsettings.json index b27baedf..03cfeee9 100644 --- a/src/apps/products/Genocs.Products.WebApi/appsettings.json +++ b/src/apps/products/Genocs.Products.WebApi/appsettings.json @@ -31,7 +31,7 @@ "services": { }, "requestMasking": { - "enabled": true, + "enabled": false, "maskTemplate": "*****" }, "correlationIdHeader": "x-correlation-id" @@ -90,22 +90,58 @@ "maxQueueSize": 2048, "scheduledDelayMilliseconds": 5000, "exporterTimeoutMilliseconds": 30000, - "maxExportBatchSize": 512 + "maxExportBatchSize": 512, + "excludePaths": [ + "/", + "/healthz", + "/alive", + "/metrics" + ] + }, + "openTelemetry": { + "enabled": true, + "exporter": { + "enabled": true, + "otlpEndpoint": "http://localhost:4317", + "protocol": "Grpc", + "processorType": "Batch", + "maxQueueSize": 2048, + "scheduledDelayMilliseconds": 5000, + "exporterTimeoutMilliseconds": 30000, + "maxExportBatchSize": 512 + }, + "console": { + "enabled": true, + "enableTracing": true, + "enableMetrics": true, + "enableLogging": true + }, + "azure": { + "enabled": true, + "enableTracing": true, + "enableMetrics": true, + "enableLogging": true, + "connectionString": "InstrumentationKey=1496274b-bda7-4ac6-88ab-9f73b4d3c7b8;IngestionEndpoint=https://italynorth-0.in.applicationinsights.azure.com/;LiveEndpoint=https://italynorth.livediagnostics.monitor.azure.com/;ApplicationId=c417f66d-3611-48a2-80fe-5a6d302bed4f" + } }, "jwt": { "enabled": true, "allowAnonymousEndpoints": [], - "certificate": { + "_certificate": { "location": "certs/localhost.pfx", "password": "test", "rawData": "" }, - "expiryMinutes": 30, + "expiryMinutes": 60, "issuer": "genocs-identity-service", + "issuerSigningKey": "THIS IS USED TO SIGN AND VERIFY JWT TOKENS, REPLACE IT WITH YOUR OWN SECRET, IT CAN BE ANY STRING", "validIssuer": "genocs-identity-service", "validateAudience": false, - "validateIssuer": true, - "validateLifetime": true, + "validateIssuer": false, + "validateLifetime": false, + "requireExpirationTime": false, + "requireSignedTokens": false, + "validateIssuerSigningKey": false, "expiry": "01:00:00" }, "metrics": { @@ -192,7 +228,7 @@ "elk": { "enabled": false, "url": "http://localhost:9200", - "indexFormat": "signalr-service-{0:yyyy.MM.dd}", + "indexFormat": "products-service-{0:yyyy.MM.dd}", "basicAuthEnabled": false, "username": "user", "password": "secret" @@ -211,7 +247,7 @@ "reDocEnabled": false, "name": "Products", "title": "Products Service", - "version": "v01", + "version": "v1", "description": "Product Service", "routePrefix": "swagger", "includeSecurity": true, @@ -268,5 +304,11 @@ }, "webApi": { "bindRequestFromRoute": true + }, + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + } } } \ No newline at end of file diff --git a/src/apps/signalr-webapi.dockerfile b/src/apps/signalr-webapi.dockerfile index 22706da1..abf8892c 100644 --- a/src/apps/signalr-webapi.dockerfile +++ b/src/apps/signalr-webapi.dockerfile @@ -23,6 +23,11 @@ FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env WORKDIR /src COPY ["signalr/Genocs.SignalR.WebApi", "Genocs.SignalR.WebApi/"] +COPY ["Directory.Build.props", "Directory.Build.props"] +COPY ["Directory.Build.targets", "Directory.Build.targets"] +COPY ["NuGet.config", "NuGet.config"] +COPY ["dotnet.ruleset", "dotnet.ruleset"] +COPY ["stylecop.json", "stylecop.json"] WORKDIR "/src/Genocs.SignalR.WebApi" diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Genocs.SignalR.WebApi.csproj b/src/apps/signalr/Genocs.SignalR.WebApi/Genocs.SignalR.WebApi.csproj index 43408df3..c3206cd4 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Genocs.SignalR.WebApi.csproj +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Genocs.SignalR.WebApi.csproj @@ -1,10 +1,10 @@  - + net9.0 false false - _Genocs + Genocs @@ -53,6 +53,6 @@ - + \ No newline at end of file diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Hubs/GenocsHub.cs b/src/apps/signalr/Genocs.SignalR.WebApi/Hubs/GenocsHub.cs index 7567b642..3dab5d05 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Hubs/GenocsHub.cs +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Hubs/GenocsHub.cs @@ -4,12 +4,9 @@ namespace Genocs.SignalR.WebApi.Hubs; -public class GenocsHub : Hub +public class GenocsHub(IJwtHandler jwtHandler) : Hub { - private readonly IJwtHandler _jwtHandler; - - public GenocsHub(IJwtHandler jwtHandler) - => _jwtHandler = jwtHandler ?? throw new ArgumentNullException(nameof(jwtHandler)); + private readonly IJwtHandler _jwtHandler = jwtHandler ?? throw new ArgumentNullException(nameof(jwtHandler)); public async Task InitializeAsync(string token) { diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Program.cs b/src/apps/signalr/Genocs.SignalR.WebApi/Program.cs index 16f701fc..b563d9bb 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Program.cs +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Program.cs @@ -76,7 +76,7 @@ app.MapDefaultEndpoints(); app.UseHttpsRedirection(); -app.UseStaticFiles(); +app.MapStaticAssets(); app.Run(); diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/Properties/launchSettings.json b/src/apps/signalr/Genocs.SignalR.WebApi/Properties/launchSettings.json index b2b66217..727da6fc 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Properties/launchSettings.json +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Properties/launchSettings.json @@ -2,7 +2,6 @@ "profiles": { "Local": { "commandName": "Project", - "launchBrowser": true, "applicationUrl": "https://localhost:5540;http://localhost:5541", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "development" diff --git a/src/apps/signalr/Genocs.SignalR.WebApi/appsettings.json b/src/apps/signalr/Genocs.SignalR.WebApi/appsettings.json index 87a5e43f..50e02e53 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/appsettings.json +++ b/src/apps/signalr/Genocs.SignalR.WebApi/appsettings.json @@ -26,13 +26,13 @@ "requestRetries": 3 }, "httpClient": { - "type": "", + "type": "fabio", "retries": 3, "services": { - "pricing": "localhost:5003" + "pricing": "prices-service" }, "requestMasking": { - "enabled": true, + "enabled": false, "maskTemplate": "*****" }, "correlationIdHeader": "x-correlation-id" @@ -91,22 +91,58 @@ "maxQueueSize": 2048, "scheduledDelayMilliseconds": 5000, "exporterTimeoutMilliseconds": 30000, - "maxExportBatchSize": 512 + "maxExportBatchSize": 512, + "excludePaths": [ + "/", + "/healthz", + "/alive", + "/metrics" + ] + }, + "openTelemetry": { + "enabled": true, + "exporter": { + "enabled": true, + "otlpEndpoint": "http://localhost:4317", + "protocol": "Grpc", + "processorType": "Batch", + "maxQueueSize": 2048, + "scheduledDelayMilliseconds": 5000, + "exporterTimeoutMilliseconds": 30000, + "maxExportBatchSize": 512 + }, + "console": { + "enabled": true, + "enableTracing": true, + "enableMetrics": true, + "enableLogging": true + }, + "azure": { + "enabled": true, + "enableTracing": true, + "enableMetrics": true, + "enableLogging": true, + "connectionString": "InstrumentationKey=1496274b-bda7-4ac6-88ab-9f73b4d3c7b8;IngestionEndpoint=https://italynorth-0.in.applicationinsights.azure.com/;LiveEndpoint=https://italynorth.livediagnostics.monitor.azure.com/;ApplicationId=c417f66d-3611-48a2-80fe-5a6d302bed4f" + } }, "jwt": { "enabled": true, "allowAnonymousEndpoints": [], - "certificate": { - "location": "certs/localhost.cer", + "_certificate": { + "location": "certs/localhost.pfx", "password": "test", "rawData": "" }, - "expiryMinutes": 30, + "expiryMinutes": 60, "issuer": "genocs-identity-service", + "issuerSigningKey": "THIS IS USED TO SIGN AND VERIFY JWT TOKENS, REPLACE IT WITH YOUR OWN SECRET, IT CAN BE ANY STRING", "validIssuer": "genocs-identity-service", "validateAudience": false, - "validateIssuer": true, - "validateLifetime": true, + "validateIssuer": false, + "validateLifetime": false, + "requireExpirationTime": false, + "requireSignedTokens": false, + "validateIssuerSigningKey": false, "expiry": "01:00:00" }, "metrics": { @@ -212,7 +248,7 @@ "reDocEnabled": false, "name": "SignalR", "title": "SignalR Service", - "version": "v01", + "version": "v1", "description": "SignalR Service", "routePrefix": "swagger", "includeSecurity": true, @@ -269,5 +305,11 @@ }, "webApi": { "bindRequestFromRoute": true + }, + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + } } } \ No newline at end of file diff --git a/src/apps/stylecop.json b/src/apps/stylecop.json new file mode 100644 index 00000000..5a2de48a --- /dev/null +++ b/src/apps/stylecop.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "orderingRules": { + "systemUsingDirectivesFirst": true, + "usingDirectivesPlacement": "outsideNamespace" + }, + "layoutRules": { + "newlineAtEndOfFile": "omit" + } + } +} diff --git a/stylecop.json b/stylecop.json index 95ceebe8..5a2de48a 100644 --- a/stylecop.json +++ b/stylecop.json @@ -9,4 +9,4 @@ "newlineAtEndOfFile": "omit" } } -} \ No newline at end of file +} diff --git a/testEnvironments.json b/testEnvironments.json index a110b57d..291ac54a 100644 --- a/testEnvironments.json +++ b/testEnvironments.json @@ -14,4 +14,4 @@ // "dockerImage": "mcr.microsoft.com/dotnet/sdk" //} ] -} \ No newline at end of file +}