From bbb923f2c15dc5bd99237d76a0114a2984f9d168 Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Sat, 30 Nov 2024 11:44:47 +0100 Subject: [PATCH 01/24] Update versions, remove Polly, and add configuration files Updated `Directory.Build.props` to version 7.1.0 and `global.json` to .NET SDK 9.0.0. Removed `Polly` package references from multiple projects. Made formatting changes to `launchSettings.json`, `stylecop.json`, and `testEnvironments.json`. Updated `MongoDbExtensions.cs` with new service registrations and method changes. Fixed a typo in `MongoDatabaseProvider.cs`. Updated lambda expression in `Extensions.cs`. Modified Dockerfiles to copy additional configuration files. Added `.editorconfig` for coding style rules and naming conventions. Added `Directory.Build.targets`, `dotnet.ruleset`, `local copy.env`, and `NuGet.config` for build and environment configuration. --- Directory.Build.props | 2 +- global.json | 4 +- launchSettings.json | 2 +- .../Genocs.Core.Demo.WebApi.csproj | 1 - .../Genocs.Core.Demo.Worker.csproj | 3 +- .../Genocs.HTTP.RestEase.csproj | 1 - .../Extensions/MongoDbExtensions.cs | 31 +- .../MongoDatabaseProvider.cs | 3 +- src/Genocs.Tracing/Extensions.cs | 2 +- src/apps/.editorconfig | 270 ++++++++++++++++++ src/apps/Directory.Build.props | 77 +++++ src/apps/Directory.Build.targets | 5 + src/apps/NuGet.config | 7 + .../Genocs.APIGateway.csproj | 1 - src/apps/apigateway.dockerfile | 5 + src/apps/dotnet.ruleset | 164 +++++++++++ src/apps/identity-webapi.dockerfile | 5 + .../Genocs.Identities.Application.csproj | 4 - .../Genocs.Identities.WebApi.csproj | 17 +- src/apps/local copy.env | 10 + src/apps/order-webapi.dockerfile | 5 + src/apps/product-webapi.dockerfile | 5 + .../Genocs.Products.WebApi.csproj | 7 +- src/apps/signalr-webapi.dockerfile | 5 + .../Genocs.SignalR.WebApi.csproj | 8 +- src/apps/stylecop.json | 12 + stylecop.json | 2 +- testEnvironments.json | 2 +- 28 files changed, 600 insertions(+), 60 deletions(-) create mode 100644 src/apps/.editorconfig create mode 100644 src/apps/Directory.Build.props create mode 100644 src/apps/Directory.Build.targets create mode 100644 src/apps/NuGet.config create mode 100644 src/apps/dotnet.ruleset create mode 100644 src/apps/local copy.env create mode 100644 src/apps/stylecop.json 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/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.Core.Demo.WebApi/Genocs.Core.Demo.WebApi.csproj b/src/Genocs.Core.Demo.WebApi/Genocs.Core.Demo.WebApi.csproj index 85e7e8b6..389dbaf5 100644 --- a/src/Genocs.Core.Demo.WebApi/Genocs.Core.Demo.WebApi.csproj +++ b/src/Genocs.Core.Demo.WebApi/Genocs.Core.Demo.WebApi.csproj @@ -34,7 +34,6 @@ - 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..482854e1 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 @@ -23,7 +23,6 @@ - diff --git a/src/Genocs.HTTP.RestEase/Genocs.HTTP.RestEase.csproj b/src/Genocs.HTTP.RestEase/Genocs.HTTP.RestEase.csproj index fbd22554..898d91cf 100644 --- a/src/Genocs.HTTP.RestEase/Genocs.HTTP.RestEase.csproj +++ b/src/Genocs.HTTP.RestEase/Genocs.HTTP.RestEase.csproj @@ -32,7 +32,6 @@ - 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/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.Tracing/Extensions.cs b/src/Genocs.Tracing/Extensions.cs index 1f91c164..0c37243d 100644 --- a/src/Genocs.Tracing/Extensions.cs +++ b/src/Genocs.Tracing/Extensions.cs @@ -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/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/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/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/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.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/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/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/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..16de3ace 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 @@ -51,8 +51,4 @@ - - - - \ 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 +} From 86875f5d53e372be8a82f1bd2030aaacda6a7914 Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Mon, 2 Dec 2024 17:44:26 +0100 Subject: [PATCH 02/24] Enhance logging, JWT options, and health checks Added new properties to JwtOptions and CertificateOptions classes. Removed commented-out code in Program.cs. Updated and clarified summary comments in Extensions.cs. Changed health check endpoint to /healthz and reorganized its logic. Enhanced logger configuration with structured logging and exception details. Added new Serilog package references. Refactored Startup class for conciseness. --- src/Genocs.Auth/Configurations/JwtOptions.cs | 12 +++++ src/Genocs.Core.Demo.WebApi/Program.cs | 1 - src/Genocs.Core/Builders/Extensions.cs | 47 +++++++++++++------ .../Configurations/ConsoleOptions.cs | 5 ++ src/Genocs.Logging/Extensions.cs | 17 ++++++- src/Genocs.Logging/Genocs.Logging.csproj | 8 +++- src/Genocs.Tracing/Extensions.cs | 2 +- .../api-gateway/Genocs.APIGateway/Startup.cs | 9 +--- 8 files changed, 74 insertions(+), 27 deletions(-) diff --git a/src/Genocs.Auth/Configurations/JwtOptions.cs b/src/Genocs.Auth/Configurations/JwtOptions.cs index 79566a5c..b7cb1ecd 100644 --- a/src/Genocs.Auth/Configurations/JwtOptions.cs +++ b/src/Genocs.Auth/Configurations/JwtOptions.cs @@ -65,6 +65,7 @@ public class JwtOptions /// Defaults to true. /// public bool IncludeErrorDetails { get; set; } = true; + public string? AuthenticationType { get; set; } public string? NameClaimType { get; set; } @@ -76,8 +77,19 @@ public class JwtOptions 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.Core.Demo.WebApi/Program.cs b/src/Genocs.Core.Demo.WebApi/Program.cs index 33d6eedd..a993d0ae 100644 --- a/src/Genocs.Core.Demo.WebApi/Program.cs +++ b/src/Genocs.Core.Demo.WebApi/Program.cs @@ -22,7 +22,6 @@ builder.AddGenocs() .AddJwt() -// .AddOpenIdJwt() .AddOpenTelemetry() .AddMongoFast() .RegisterMongoRepositories(Assembly.GetExecutingAssembly()) diff --git a/src/Genocs.Core/Builders/Extensions.cs b/src/Genocs.Core/Builders/Extensions.cs index 4c82f9eb..e63a36f9 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 @@ -155,6 +146,16 @@ public static WebApplication MapDefaultEndpoints(this WebApplication app) await context.Response.WriteAsync(context.RequestServices.GetService()?.Name ?? message); }); + + // 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") + }); + 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.Logging/Configurations/ConsoleOptions.cs b/src/Genocs.Logging/Configurations/ConsoleOptions.cs index 4112f484..14fb18e2 100644 --- a/src/Genocs.Logging/Configurations/ConsoleOptions.cs +++ b/src/Genocs.Logging/Configurations/ConsoleOptions.cs @@ -10,6 +10,11 @@ public class ConsoleOptions /// public bool Enabled { get; set; } + /// + /// It define whether the console logger will use structured logging or not. + /// + public bool StructuredConsoleLogging { get; set; } + /// /// It define whether the console logger and tracing are enabled or not. /// diff --git a/src/Genocs.Logging/Extensions.cs b/src/Genocs.Logging/Extensions.cs index 41997920..95208eae 100644 --- a/src/Genocs.Logging/Extensions.cs +++ b/src/Genocs.Logging/Extensions.cs @@ -10,7 +10,9 @@ using Serilog; using Serilog.Core; using Serilog.Events; +using Serilog.Exceptions; using Serilog.Filters; +using Serilog.Formatting.Compact; using Serilog.Sinks.Elasticsearch; using Serilog.Sinks.Grafana.Loki; @@ -64,7 +66,11 @@ 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(); foreach (var (key, value) in loggerOptions.Tags ?? new Dictionary()) { @@ -98,7 +104,14 @@ private static void Configure(LoggerConfiguration loggerConfiguration, LoggerOpt // console if (consoleOptions.Enabled) { - loggerConfiguration.WriteTo.Console(); + if (consoleOptions.StructuredConsoleLogging) + { + 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..2e8e7e93 100644 --- a/src/Genocs.Logging/Genocs.Logging.csproj +++ b/src/Genocs.Logging/Genocs.Logging.csproj @@ -16,7 +16,7 @@ True latest - + @@ -28,7 +28,13 @@ + + + + + + diff --git a/src/Genocs.Tracing/Extensions.cs b/src/Genocs.Tracing/Extensions.cs index 0c37243d..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. diff --git a/src/apps/api-gateway/Genocs.APIGateway/Startup.cs b/src/apps/api-gateway/Genocs.APIGateway/Startup.cs index 379c37d9..8ba5bdd1 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) { From 8e48cfa891777398b276965dd3f02d3b536e75ad Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Mon, 2 Dec 2024 18:12:17 +0100 Subject: [PATCH 03/24] Standardize property names and improve documentation - Set default values for `ExpiryMinutes` and `Expiry` in `JwtOptions.cs` - Correct summary comments in multiple files for better grammar - Rename `StructuredConsoleLogging` to `EnableStructured` in `ConsoleOptions.cs` - Update `Extensions.cs` to use the renamed property `EnableStructured` --- src/Genocs.Auth/Configurations/JwtOptions.cs | 4 ++-- src/Genocs.Logging/Configurations/AzureOptions.cs | 7 +++---- src/Genocs.Logging/Configurations/ConsoleOptions.cs | 10 +++++----- src/Genocs.Logging/Configurations/ElkOptions.cs | 4 ++-- src/Genocs.Logging/Configurations/MongoOptions.cs | 2 +- src/Genocs.Logging/Configurations/SeqOptions.cs | 2 +- src/Genocs.Logging/Extensions.cs | 2 +- 7 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/Genocs.Auth/Configurations/JwtOptions.cs b/src/Genocs.Auth/Configurations/JwtOptions.cs index b7cb1ecd..53964d10 100644 --- a/src/Genocs.Auth/Configurations/JwtOptions.cs +++ b/src/Genocs.Auth/Configurations/JwtOptions.cs @@ -31,8 +31,8 @@ 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; } - public TimeSpan? Expiry { get; set; } + public int ExpiryMinutes { get; set; } = 60; + public TimeSpan Expiry { get; set; } = TimeSpan.FromMinutes(60); public string? ValidAudience { get; set; } public IEnumerable? ValidAudiences { get; set; } public string? ValidIssuer { get; set; } 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 14fb18e2..383d3634 100644 --- a/src/Genocs.Logging/Configurations/ConsoleOptions.cs +++ b/src/Genocs.Logging/Configurations/ConsoleOptions.cs @@ -6,22 +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 will use structured logging or not. + /// It defines whether the console logger will use structured logging or not. /// - public bool StructuredConsoleLogging { get; set; } + public bool EnableStructured { get; set; } /// - /// 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 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/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 95208eae..05cc8517 100644 --- a/src/Genocs.Logging/Extensions.cs +++ b/src/Genocs.Logging/Extensions.cs @@ -104,7 +104,7 @@ private static void Configure(LoggerConfiguration loggerConfiguration, LoggerOpt // console if (consoleOptions.Enabled) { - if (consoleOptions.StructuredConsoleLogging) + if (consoleOptions.EnableStructured) { loggerConfiguration.WriteTo.Console(new RenderedCompactJsonFormatter()); } From 3fbbc7e5597c7bb0b59383b39b0b4aedbf99bc8b Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Mon, 2 Dec 2024 19:15:22 +0100 Subject: [PATCH 04/24] Upgrade to .NET 9.0 and refine project configurations Updated target framework to include net9.0 across multiple projects. Modified Title and Description properties in .csproj files for clarity. Updated PackageReleaseNotes to indicate the upgrade to NET9.0. Reordered and refined PackageTags for better categorization. Removed redundant tags from PackageTags in some .csproj files. Adjusted ItemGroup conditions in Genocs.Auth.csproj for proper configuration. Refined AddMetrics method in Extensions.cs to use configuration positions. Corrected a typo in XML documentation in MongoDbBaseRepositoryOfType.cs. Simplified constructor logic in MongoSessionFactory.cs and MongoDbInitializer.cs. Updated IMongoDbEntity and IMongoDbInitializer interfaces to remove braces. Modified MongoDbSeeder class for a more straightforward collection check. Removed Polly package reference from Genocs.Tracing.csproj. --- src/Genocs.Auth/Genocs.Auth.csproj | 11 +++++------ src/Genocs.Common/Genocs.Common.csproj | 4 ++-- src/Genocs.Core/Genocs.Core.csproj | 2 +- .../Genocs.Discovery.Consul.csproj | 8 ++++---- .../Genocs.HTTP.RestEase.csproj | 8 ++++---- src/Genocs.HTTP/Genocs.HTTP.csproj | 8 ++++---- .../Genocs.LoadBalancing.Fabio.csproj | 8 ++++---- src/Genocs.Logging/Genocs.Logging.csproj | 6 +++--- .../Genocs.MessageBrokers.Outbox.MongoDB.csproj | 4 ++-- .../Genocs.MessageBrokers.Outbox.csproj | 4 ++-- .../Genocs.MessageBrokers.RabbitMQ.csproj | 2 +- .../Genocs.MessageBrokers.csproj | 2 +- src/Genocs.Metrics/AppMetrics/Extensions.cs | 12 +++++------- src/Genocs.Metrics/Genocs.Metrics.csproj | 6 +++--- .../Domain/Entities/IMongoDbEntity.cs | 5 +---- .../Repositories/MongoDbBaseRepositoryOfType.cs | 2 +- .../Factories/MongoSessionFactory.cs | 7 ++----- .../Genocs.Persistence.MongoDb.csproj | 4 ++-- .../Initializers/MongoDbInitializer.cs | 16 +++++----------- .../Repositories/IMongoDbInitializer.cs | 4 +--- .../Seeders/MongoDbSeeder.cs | 2 +- .../Genocs.Persistence.Redis.csproj | 2 +- .../Genocs.QueryBuilder.csproj | 2 +- .../Genocs.Secrets.AzureKeyVault.csproj | 2 +- .../Genocs.Secrets.Vault.csproj | 2 +- src/Genocs.Security/Genocs.Security.csproj | 2 +- .../Genocs.ServiceBusAzure.csproj | 2 +- src/Genocs.Tracing/Genocs.Tracing.csproj | 7 +++---- src/Genocs.WebApi.CQRS/Genocs.WebApi.CQRS.csproj | 2 +- .../Genocs.WebApi.Security.csproj | 2 +- .../Genocs.WebApi.Swagger.csproj | 2 +- src/Genocs.WebApi/Genocs.WebApi.csproj | 2 +- 32 files changed, 67 insertions(+), 85 deletions(-) diff --git a/src/Genocs.Auth/Genocs.Auth.csproj b/src/Genocs.Auth/Genocs.Auth.csproj index f8482eb1..6f732d10 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 @@ - @@ -31,7 +30,7 @@ - + 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/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 898d91cf..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 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.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/Genocs.Logging.csproj b/src/Genocs.Logging/Genocs.Logging.csproj index 2e8e7e93..e632b698 100644 --- a/src/Genocs.Logging/Genocs.Logging.csproj +++ b/src/Genocs.Logging/Genocs.Logging.csproj @@ -5,14 +5,14 @@ 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 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/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.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/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..bacc5ac9 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 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/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..377eefcb 100644 --- a/src/Genocs.Security/Genocs.Security.csproj +++ b/src/Genocs.Security/Genocs.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 - 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/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/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.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/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 From 0e6140ba2ebb2bac7671e28e925546cca4611009 Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Tue, 3 Dec 2024 22:43:36 +0100 Subject: [PATCH 05/24] Add OpenTelemetry support and update various configurations Updated genocs.sln: - Removed and re-added order-webapi.dockerfile entry - Added Genocs.OpenTelemetry project - Added configuration entries for Genocs.OpenTelemetry Modified JwtOptions.cs: - Changed Expiry property type from TimeSpan to TimeSpan? Updated appsettings.json: - Set enabled property to false for consul and fabio sections Enhanced GetProductHandler.cs: - Added static Random instance _random - Added logic to throw exception for random values > 6 Added Extensions.cs: - Introduced OpenTelemetryExtensions class with AddOpenTelemetry method Added Genocs.OpenTelemetry.csproj: - Targets net9.0, net8.0, net7.0, and net6.0 - Includes metadata for package generation and necessary references Added OpenTelemetryOptions.cs: - Introduced OpenTelemetryOptions class with properties for enabling section and specifying OTLP endpoint --- genocs.sln | 8 +- src/Genocs.Auth/Configurations/JwtOptions.cs | 2 +- .../Configurations/OpenTelemetryOptions.cs | 20 +++++ src/Genocs.OpenTelemetry/Extensions.cs | 75 +++++++++++++++++++ .../Genocs.OpenTelemetry.csproj | 41 ++++++++++ .../Genocs.Orders.WebApi/appsettings.json | 4 +- .../Queries/Handlers/GetProductHandler.cs | 9 +++ 7 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 src/Genocs.OpenTelemetry/Configurations/OpenTelemetryOptions.cs create mode 100644 src/Genocs.OpenTelemetry/Extensions.cs create mode 100644 src/Genocs.OpenTelemetry/Genocs.OpenTelemetry.csproj diff --git a/genocs.sln b/genocs.sln index dac56edb..58b2a001 100644 --- a/genocs.sln +++ b/genocs.sln @@ -123,14 +123,16 @@ 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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -297,6 +299,10 @@ 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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Genocs.Auth/Configurations/JwtOptions.cs b/src/Genocs.Auth/Configurations/JwtOptions.cs index 53964d10..1f53d103 100644 --- a/src/Genocs.Auth/Configurations/JwtOptions.cs +++ b/src/Genocs.Auth/Configurations/JwtOptions.cs @@ -32,7 +32,7 @@ public class JwtOptions public bool RequireExpirationTime { get; set; } = true; public bool RequireSignedTokens { get; set; } = true; public int ExpiryMinutes { get; set; } = 60; - public TimeSpan Expiry { get; set; } = TimeSpan.FromMinutes(60); + public TimeSpan? Expiry { get; set; } public string? ValidAudience { get; set; } public IEnumerable? ValidAudiences { get; set; } public string? ValidIssuer { get; set; } diff --git a/src/Genocs.OpenTelemetry/Configurations/OpenTelemetryOptions.cs b/src/Genocs.OpenTelemetry/Configurations/OpenTelemetryOptions.cs new file mode 100644 index 00000000..c04010db --- /dev/null +++ b/src/Genocs.OpenTelemetry/Configurations/OpenTelemetryOptions.cs @@ -0,0 +1,20 @@ +namespace Genocs.OpenTelemetry.Configurations; + +/// +/// OpenTelemetry Settings. +/// +public class OpenTelemetryOptions +{ + /// + /// Default section name. + /// + public const string Position = "jaeger"; + + /// + /// It defines whether the section is enabled or not. + /// + /// + public bool Enabled { get; set; } + + public string? OtlpEndpoint { get; set; } +} diff --git a/src/Genocs.OpenTelemetry/Extensions.cs b/src/Genocs.OpenTelemetry/Extensions.cs new file mode 100644 index 00000000..5eb3e3aa --- /dev/null +++ b/src/Genocs.OpenTelemetry/Extensions.cs @@ -0,0 +1,75 @@ +using Genocs.Common.Configurations; +using Genocs.Core.Builders; +using Genocs.OpenTelemetry.Configurations; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; +using static Genocs.Core.Exceptions.GenocsException; + +namespace Genocs.OpenTelemetry; + +public static class OpenTelemetryExtensions +{ + public static IGenocsBuilder AddOpenTelemetry( + this IGenocsBuilder builder) + { + AppOptions appOptions = builder.GetOptions(AppOptions.Position) + ?? throw new 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); + + //LoggerOptions loggerOptions = builder.GetOptions(LoggerOptions.Position); + + //if (loggerOptions is null) + //{ + // return builder; + //} + + builder.Services.AddOpenTelemetry() + .ConfigureResource(resource => resource.AddService(serviceName: appOptions.Service)) + .WithMetrics(metrics => + { + metrics + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation(); + + metrics + .AddOtlpExporter(otlpOptions => + { + otlpOptions.Endpoint = new Uri(openTelemetryOptions.OtlpEndpoint!); + }); + }) + .WithTracing(tracing => + { + tracing + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation(); + + tracing + .AddOtlpExporter(otlpOptions => + { + otlpOptions.Endpoint = new Uri(openTelemetryOptions.OtlpEndpoint!); + }); + }); + + builder.WebApplicationBuilder?.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + + logging.AddOtlpExporter(); + }); + + return builder; + } + +} diff --git a/src/Genocs.OpenTelemetry/Genocs.OpenTelemetry.csproj b/src/Genocs.OpenTelemetry/Genocs.OpenTelemetry.csproj new file mode 100644 index 00000000..173bc638 --- /dev/null +++ b/src/Genocs.OpenTelemetry/Genocs.OpenTelemetry.csproj @@ -0,0 +1,41 @@ + + + + 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/apps/orders/Genocs.Orders.WebApi/appsettings.json b/src/apps/orders/Genocs.Orders.WebApi/appsettings.json index a2ad9a9e..c60b1f0c 100644 --- a/src/apps/orders/Genocs.Orders.WebApi/appsettings.json +++ b/src/apps/orders/Genocs.Orders.WebApi/appsettings.json @@ -8,7 +8,7 @@ "displayVersion": true }, "consul": { - "enabled": true, + "enabled": false, "url": "http://localhost:8500", "service": "orders-service", "address": "docker.for.mac.localhost", @@ -20,7 +20,7 @@ "requestRetries": 3 }, "fabio": { - "enabled": true, + "enabled": false, "url": "http://localhost:9999", "service": "orders-service", "requestRetries": 3 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..55254d62 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; @@ -17,6 +19,13 @@ public GetProductHandler(IMongoDbBaseRepository repository { var product = await _repository.GetAsync(query.ProductId); + int currentValue = _random.Next(1, 10); + + if (currentValue > 6) + { + throw new Exception("Random exception"); + } + return product is null ? null : new ProductDto { Id = product.Id, SKU = product.SKU, UnitPrice = product.UnitPrice }; From af280e0b6a7b08ab333f0ff2339aa068811f0f27 Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Wed, 4 Dec 2024 08:44:09 +0100 Subject: [PATCH 06/24] Refactor namespaces and update OpenTelemetry config Renamed namespaces to `Genocs.GnxOpenTelemetry` and constants to reflect the shift from "jaeger" to "openTelemetry". Added new `Exporter` property and `OtlpExportOptions` class in `OpenTelemetryOptions.cs`. Updated `AddOtlpExporter` method in `Extensions.cs` for additional configuration options. Modified `Genocs.APIGateway.csproj` and `Startup.cs` to use new namespaces. Updated `appsettings.json` to include new "openTelemetry" section. Added `README_NUGET.md` for OpenTelemetry setup instructions. --- .../Configurations/OpenTelemetryOptions.cs | 9 +- .../Configurations/OtlpExportOptions.cs | 29 +++++ src/Genocs.OpenTelemetry/Extensions.cs | 118 ++++++++++++++++-- src/Genocs.OpenTelemetry/README_NUGET.md | 23 ++++ .../Genocs.APIGateway.csproj | 4 +- .../api-gateway/Genocs.APIGateway/Startup.cs | 2 +- .../Genocs.APIGateway/appsettings.json | 20 +-- 7 files changed, 178 insertions(+), 27 deletions(-) create mode 100644 src/Genocs.OpenTelemetry/Configurations/OtlpExportOptions.cs create mode 100644 src/Genocs.OpenTelemetry/README_NUGET.md diff --git a/src/Genocs.OpenTelemetry/Configurations/OpenTelemetryOptions.cs b/src/Genocs.OpenTelemetry/Configurations/OpenTelemetryOptions.cs index c04010db..77676d27 100644 --- a/src/Genocs.OpenTelemetry/Configurations/OpenTelemetryOptions.cs +++ b/src/Genocs.OpenTelemetry/Configurations/OpenTelemetryOptions.cs @@ -1,4 +1,4 @@ -namespace Genocs.OpenTelemetry.Configurations; +namespace Genocs.GnxOpenTelemetry.Configurations; /// /// OpenTelemetry Settings. @@ -8,7 +8,7 @@ public class OpenTelemetryOptions /// /// Default section name. /// - public const string Position = "jaeger"; + public const string Position = "openTelemetry"; /// /// It defines whether the section is enabled or not. @@ -16,5 +16,10 @@ public class OpenTelemetryOptions /// public bool Enabled { get; set; } + /// + /// The Otlp Exporter endpoint. + /// public string? OtlpEndpoint { get; set; } + + public OtlpExportOptions? Exporter { get; set; } } diff --git a/src/Genocs.OpenTelemetry/Configurations/OtlpExportOptions.cs b/src/Genocs.OpenTelemetry/Configurations/OtlpExportOptions.cs new file mode 100644 index 00000000..f268f706 --- /dev/null +++ b/src/Genocs.OpenTelemetry/Configurations/OtlpExportOptions.cs @@ -0,0 +1,29 @@ +namespace Genocs.GnxOpenTelemetry.Configurations; + +/// +/// OtlpExportOptions Settings. +/// +public class OtlpExportOptions +{ + /// + /// It defines whether the section is enabled or not. + /// + public bool Enabled { 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 index 5eb3e3aa..39caa241 100644 --- a/src/Genocs.OpenTelemetry/Extensions.cs +++ b/src/Genocs.OpenTelemetry/Extensions.cs @@ -1,15 +1,16 @@ using Genocs.Common.Configurations; using Genocs.Core.Builders; -using Genocs.OpenTelemetry.Configurations; +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; using static Genocs.Core.Exceptions.GenocsException; -namespace Genocs.OpenTelemetry; +namespace Genocs.GnxOpenTelemetry; public static class OpenTelemetryExtensions { @@ -42,11 +43,33 @@ public static IGenocsBuilder AddOpenTelemetry( .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation(); - metrics - .AddOtlpExporter(otlpOptions => - { - otlpOptions.Endpoint = new Uri(openTelemetryOptions.OtlpEndpoint!); - }); + // Setup the exporter + if (!string.IsNullOrWhiteSpace(openTelemetryOptions.OtlpEndpoint)) + { + metrics + .AddOtlpExporter(otlpOptions => + { + otlpOptions.Endpoint = new Uri(openTelemetryOptions.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 + }; + } + }); + } + }) .WithTracing(tracing => { @@ -54,22 +77,91 @@ public static IGenocsBuilder AddOpenTelemetry( .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation(); - tracing - .AddOtlpExporter(otlpOptions => - { - otlpOptions.Endpoint = new Uri(openTelemetryOptions.OtlpEndpoint!); - }); + // Setup the exporter + if (!string.IsNullOrWhiteSpace(openTelemetryOptions.OtlpEndpoint)) + { + tracing + .AddOtlpExporter(otlpOptions => + { + otlpOptions.Endpoint = new Uri(openTelemetryOptions.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 + }; + } + }); + } }); + // Add the OpenTelemetry logging provider builder.WebApplicationBuilder?.Logging.AddOpenTelemetry(logging => { logging.IncludeFormattedMessage = true; logging.IncludeScopes = true; - logging.AddOtlpExporter(); + // Setup the exporter + if (!string.IsNullOrWhiteSpace(openTelemetryOptions.OtlpEndpoint)) + { + logging.AddOtlpExporter(otlpOptions => + { + otlpOptions.Endpoint = new Uri(openTelemetryOptions.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 + }; + } + }); + } }); 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/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/apps/api-gateway/Genocs.APIGateway/Genocs.APIGateway.csproj b/src/apps/api-gateway/Genocs.APIGateway/Genocs.APIGateway.csproj index 2584da73..fd0b5e1a 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Genocs.APIGateway.csproj +++ b/src/apps/api-gateway/Genocs.APIGateway/Genocs.APIGateway.csproj @@ -14,7 +14,7 @@ - + @@ -26,7 +26,7 @@ - + diff --git a/src/apps/api-gateway/Genocs.APIGateway/Startup.cs b/src/apps/api-gateway/Genocs.APIGateway/Startup.cs index 8ba5bdd1..71428c8d 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Startup.cs +++ b/src/apps/api-gateway/Genocs.APIGateway/Startup.cs @@ -5,7 +5,7 @@ using Genocs.MessageBrokers.RabbitMQ; using Genocs.Metrics.Prometheus; using Genocs.Security; -using Genocs.Tracing; +using Genocs.GnxOpenTelemetry; using Genocs.WebApi; using Yarp.ReverseProxy.Forwarder; diff --git a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json index 4b117e2c..3fb9815d 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json +++ b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json @@ -81,16 +81,18 @@ }, "tags": {} }, - "jaeger": { + "openTelemetry": { "enabled": true, - "serviceName": "api-gateway", - "endpoint": "http://localhost:4317", - "protocol": "Grpc", - "processorType": "Batch", - "maxQueueSize": 2048, - "scheduledDelayMilliseconds": 5000, - "exporterTimeoutMilliseconds": 30000, - "maxExportBatchSize": 512 + "otlpEndpoint": "http://localhost:4317", + "exporter": { + "enabled": true, + "protocol": "Grpc", + "processorType": "Batch", + "maxQueueSize": 2048, + "scheduledDelayMilliseconds": 5000, + "exporterTimeoutMilliseconds": 30000, + "maxExportBatchSize": 512 + } }, "jwt": { "enabled": true, From 5f0ca21d0f36032c62102833ea95ad63c1b809aa Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Wed, 4 Dec 2024 09:07:42 +0100 Subject: [PATCH 07/24] Add console exporter support for OpenTelemetry - Added `Console` property to `OpenTelemetryOptions` for console settings. - Updated `OpenTelemetryExtensions` to configure console exporter. - Added `OpenTelemetry.Exporter.Console` NuGet package. - Updated `appsettings.json` with console exporter settings. - Created `ConsoleOptions` class for console tracing, metrics, and logging. --- .../Configurations/ConsoleOptions.cs | 27 +++++++++++++++++++ .../Configurations/OpenTelemetryOptions.cs | 5 ++++ src/Genocs.OpenTelemetry/Extensions.cs | 20 ++++++++++++++ .../Genocs.OpenTelemetry.csproj | 2 ++ .../Genocs.APIGateway/appsettings.json | 6 +++++ 5 files changed, 60 insertions(+) create mode 100644 src/Genocs.OpenTelemetry/Configurations/ConsoleOptions.cs 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 index 77676d27..c5581de4 100644 --- a/src/Genocs.OpenTelemetry/Configurations/OpenTelemetryOptions.cs +++ b/src/Genocs.OpenTelemetry/Configurations/OpenTelemetryOptions.cs @@ -22,4 +22,9 @@ public class OpenTelemetryOptions public string? OtlpEndpoint { get; set; } public OtlpExportOptions? Exporter { get; set; } + + /// + /// Console OpenTelemetry settings. + /// + public ConsoleOptions? Console { get; set; } } diff --git a/src/Genocs.OpenTelemetry/Extensions.cs b/src/Genocs.OpenTelemetry/Extensions.cs index 39caa241..e67dc3b6 100644 --- a/src/Genocs.OpenTelemetry/Extensions.cs +++ b/src/Genocs.OpenTelemetry/Extensions.cs @@ -70,6 +70,12 @@ public static IGenocsBuilder AddOpenTelemetry( }); } + // Setup Console exporter + if (openTelemetryOptions.Console?.Enabled == true && openTelemetryOptions.Console.EnableMetrics) + { + // you should add OpenTelemetry.Exporter.Console NuGet package + metrics.AddConsoleExporter(); + } }) .WithTracing(tracing => { @@ -103,6 +109,13 @@ public static IGenocsBuilder AddOpenTelemetry( } }); } + + // Setup Console exporter + if (openTelemetryOptions.Console?.Enabled == true && openTelemetryOptions.Console.EnableTracing) + { + // you should add OpenTelemetry.Exporter.Console NuGet package + tracing.AddConsoleExporter(); + } }); // Add the OpenTelemetry logging provider @@ -136,6 +149,13 @@ public static IGenocsBuilder AddOpenTelemetry( } }); } + + // Setup Console exporter + if (openTelemetryOptions.Console?.Enabled == true && openTelemetryOptions.Console.EnableLogging) + { + // you should add OpenTelemetry.Exporter.Console NuGet package + logging.AddConsoleExporter(); + } }); return builder; diff --git a/src/Genocs.OpenTelemetry/Genocs.OpenTelemetry.csproj b/src/Genocs.OpenTelemetry/Genocs.OpenTelemetry.csproj index 173bc638..c2895ed9 100644 --- a/src/Genocs.OpenTelemetry/Genocs.OpenTelemetry.csproj +++ b/src/Genocs.OpenTelemetry/Genocs.OpenTelemetry.csproj @@ -32,7 +32,9 @@ + + diff --git a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json index 3fb9815d..aec14b7a 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json +++ b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json @@ -92,6 +92,12 @@ "scheduledDelayMilliseconds": 5000, "exporterTimeoutMilliseconds": 30000, "maxExportBatchSize": 512 + }, + "console": { + "enabled": true, + "enableTracing": true, + "enableMetrics": true, + "enableLogging": true } }, "jwt": { From 42292cffc5b4b329377bf6adaf243504b3bc6073 Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Wed, 4 Dec 2024 11:29:02 +0100 Subject: [PATCH 08/24] Add new service, project, and OpenTelemetry config - Added a new service `aspire` to `infrastructure-monitoring.yml`. - Added `Genocs.Core.Demo.HelloWorld` project to `genocs.sln`. - Updated `Extensions.cs` to include `Serilog.Enrichers.Span` and OpenTelemetry configuration. - Updated `Genocs.Logging.csproj` to include `Serilog.Enrichers.Span` and `Serilog.Sinks.OpenTelemetry`. - Modified `OpenTelemetryOptions.cs` and `OtlpExportOptions.cs` for Azure settings. - Added `Azure.Monitor.OpenTelemetry.Exporter` to `Genocs.OpenTelemetry.csproj`. - Updated `appsettings.json` and added `appsettings.Development.json` for new settings. - Created `Genocs.Core.Demo.HelloWorld` project with necessary files. - Added `launchSettings.json` for the new project. - Added `AzureOptions.cs` for Azure OpenTelemetry settings. --- containers/infrastructure-monitoring.yml | 15 ++++ genocs.sln | 7 ++ .../Controllers/WeatherForecastController.cs | 31 ++++++++ .../Genocs.Core.Demo.HelloWorld.csproj | 18 +++++ .../Genocs.Core.Demo.HelloWorld.http | 6 ++ src/Genocs.Core.Demo.HelloWorld/Program.cs | 36 +++++++++ .../Properties/launchSettings.json | 23 ++++++ .../WeatherForecast.cs | 12 +++ .../appsettings.Development.json | 8 ++ .../appsettings.json | 75 +++++++++++++++++++ src/Genocs.Logging/Extensions.cs | 35 +++++---- src/Genocs.Logging/Genocs.Logging.csproj | 2 + .../Configurations/AzureOptions.cs | 32 ++++++++ .../Configurations/OpenTelemetryOptions.cs | 10 +-- .../Configurations/OtlpExportOptions.cs | 5 ++ src/Genocs.OpenTelemetry/Extensions.cs | 57 ++++++++++---- .../Genocs.OpenTelemetry.csproj | 18 +++-- .../Genocs.APIGateway/appsettings.json | 15 +++- 18 files changed, 362 insertions(+), 43 deletions(-) create mode 100644 src/Genocs.Core.Demo.HelloWorld/Controllers/WeatherForecastController.cs create mode 100644 src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.csproj create mode 100644 src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.http create mode 100644 src/Genocs.Core.Demo.HelloWorld/Program.cs create mode 100644 src/Genocs.Core.Demo.HelloWorld/Properties/launchSettings.json create mode 100644 src/Genocs.Core.Demo.HelloWorld/WeatherForecast.cs create mode 100644 src/Genocs.Core.Demo.HelloWorld/appsettings.Development.json create mode 100644 src/Genocs.Core.Demo.HelloWorld/appsettings.json create mode 100644 src/Genocs.OpenTelemetry/Configurations/AzureOptions.cs 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 58b2a001..53366e9e 100644 --- a/genocs.sln +++ b/genocs.sln @@ -133,6 +133,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Genocs.Secrets.AzureKeyVaul 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 @@ -303,6 +305,10 @@ Global {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 @@ -331,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/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..346fc30a --- /dev/null +++ b/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.csproj @@ -0,0 +1,18 @@ + + + + net9.0 + enable + enable + + + + + + + + + + + + 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..d52a8481 --- /dev/null +++ b/src/Genocs.Core.Demo.HelloWorld/Program.cs @@ -0,0 +1,36 @@ +using Genocs.Core.Builders; +using Genocs.GnxOpenTelemetry; +using Genocs.Logging; + +StaticLogger.EnsureInitialized(); + +var builder = WebApplication.CreateBuilder(args); + +builder.Host + .UseLogging(); + +builder.AddGenocs() + .AddOpenTelemetry() + .Build(); + +// Add services to the container. + +builder.Services.AddControllers(); +// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi +builder.Services.AddOpenApi(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.MapOpenApi(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +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..5792270e --- /dev/null +++ b/src/Genocs.Core.Demo.HelloWorld/appsettings.json @@ -0,0 +1,75 @@ +{ + "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": "*" +} diff --git a/src/Genocs.Logging/Extensions.cs b/src/Genocs.Logging/Extensions.cs index 05cc8517..ef5854af 100644 --- a/src/Genocs.Logging/Extensions.cs +++ b/src/Genocs.Logging/Extensions.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.Hosting; using Serilog; using Serilog.Core; +using Serilog.Enrichers.Span; using Serilog.Events; using Serilog.Exceptions; using Serilog.Filters; @@ -30,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, @@ -70,7 +71,8 @@ private static void MapOptions( .Enrich.WithExceptionDetails() .Enrich.WithMachineName() .Enrich.WithProcessId() - .Enrich.WithThreadId(); + .Enrich.WithThreadId() + .Enrich.WithSpan(); foreach (var (key, value) in loggerOptions.Tags ?? new Dictionary()) { @@ -101,6 +103,11 @@ private static void Configure(LoggerConfiguration loggerConfiguration, LoggerOpt var lokiOptions = options.Loki ?? new LokiOptions(); var azureOptions = options.Azure ?? new AzureOptions(); + loggerConfiguration.WriteTo.OpenTelemetry(wt => + { + wt.Endpoint = "http://localhost:4318"; + }); + // console if (consoleOptions.Enabled) { diff --git a/src/Genocs.Logging/Genocs.Logging.csproj b/src/Genocs.Logging/Genocs.Logging.csproj index e632b698..2da279e8 100644 --- a/src/Genocs.Logging/Genocs.Logging.csproj +++ b/src/Genocs.Logging/Genocs.Logging.csproj @@ -29,6 +29,7 @@ + @@ -38,6 +39,7 @@ + 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/OpenTelemetryOptions.cs b/src/Genocs.OpenTelemetry/Configurations/OpenTelemetryOptions.cs index c5581de4..3e96a949 100644 --- a/src/Genocs.OpenTelemetry/Configurations/OpenTelemetryOptions.cs +++ b/src/Genocs.OpenTelemetry/Configurations/OpenTelemetryOptions.cs @@ -16,15 +16,15 @@ public class OpenTelemetryOptions /// public bool Enabled { get; set; } - /// - /// The Otlp Exporter endpoint. - /// - public string? OtlpEndpoint { 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 index f268f706..12ae4f41 100644 --- a/src/Genocs.OpenTelemetry/Configurations/OtlpExportOptions.cs +++ b/src/Genocs.OpenTelemetry/Configurations/OtlpExportOptions.cs @@ -10,6 +10,11 @@ public class OtlpExportOptions /// public bool Enabled { get; set; } + /// + /// The Otlp Exporter endpoint. + /// + public string? OtlpEndpoint { get; set; } + /// /// The used OtlpExportProtocol. /// IT could be [Grpc|HttpProtobuf]. diff --git a/src/Genocs.OpenTelemetry/Extensions.cs b/src/Genocs.OpenTelemetry/Extensions.cs index e67dc3b6..0ec48caa 100644 --- a/src/Genocs.OpenTelemetry/Extensions.cs +++ b/src/Genocs.OpenTelemetry/Extensions.cs @@ -1,4 +1,5 @@ -using Genocs.Common.Configurations; +using Azure.Monitor.OpenTelemetry.Exporter; +using Genocs.Common.Configurations; using Genocs.Core.Builders; using Genocs.GnxOpenTelemetry.Configurations; using Microsoft.Extensions.DependencyInjection; @@ -28,28 +29,23 @@ public static IGenocsBuilder AddOpenTelemetry( OpenTelemetryOptions? openTelemetryOptions = builder.GetOptions(OpenTelemetryOptions.Position); - //LoggerOptions loggerOptions = builder.GetOptions(LoggerOptions.Position); - - //if (loggerOptions is null) - //{ - // return builder; - //} - builder.Services.AddOpenTelemetry() .ConfigureResource(resource => resource.AddService(serviceName: appOptions.Service)) .WithMetrics(metrics => { + // Setup standard instrumentations metrics .AddAspNetCoreInstrumentation() - .AddHttpClientInstrumentation(); + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); // Setup the exporter - if (!string.IsNullOrWhiteSpace(openTelemetryOptions.OtlpEndpoint)) + if (openTelemetryOptions.Exporter?.Enabled == true) { metrics .AddOtlpExporter(otlpOptions => { - otlpOptions.Endpoint = new Uri(openTelemetryOptions.OtlpEndpoint!); + otlpOptions.Endpoint = new Uri(openTelemetryOptions.Exporter.OtlpEndpoint!); // Check if Jaeger is enabled if (openTelemetryOptions.Exporter?.Enabled == true) @@ -76,6 +72,16 @@ public static IGenocsBuilder AddOpenTelemetry( // 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 => { @@ -84,12 +90,12 @@ public static IGenocsBuilder AddOpenTelemetry( .AddHttpClientInstrumentation(); // Setup the exporter - if (!string.IsNullOrWhiteSpace(openTelemetryOptions.OtlpEndpoint)) + if (openTelemetryOptions.Exporter?.Enabled == true) { tracing .AddOtlpExporter(otlpOptions => { - otlpOptions.Endpoint = new Uri(openTelemetryOptions.OtlpEndpoint!); + otlpOptions.Endpoint = new Uri(openTelemetryOptions.Exporter.OtlpEndpoint!); // Check if Jaeger is enabled if (openTelemetryOptions.Exporter?.Enabled == true) @@ -116,6 +122,16 @@ public static IGenocsBuilder AddOpenTelemetry( // 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 @@ -124,12 +140,13 @@ public static IGenocsBuilder AddOpenTelemetry( logging.IncludeFormattedMessage = true; logging.IncludeScopes = true; + // Setup the exporter - if (!string.IsNullOrWhiteSpace(openTelemetryOptions.OtlpEndpoint)) + if (openTelemetryOptions.Exporter?.Enabled == true) { logging.AddOtlpExporter(otlpOptions => { - otlpOptions.Endpoint = new Uri(openTelemetryOptions.OtlpEndpoint!); + otlpOptions.Endpoint = new Uri(openTelemetryOptions.Exporter.OtlpEndpoint!); // Check if Jaeger is enabled if (openTelemetryOptions.Exporter?.Enabled == true) @@ -156,6 +173,16 @@ public static IGenocsBuilder AddOpenTelemetry( // 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; diff --git a/src/Genocs.OpenTelemetry/Genocs.OpenTelemetry.csproj b/src/Genocs.OpenTelemetry/Genocs.OpenTelemetry.csproj index c2895ed9..a0b88a06 100644 --- a/src/Genocs.OpenTelemetry/Genocs.OpenTelemetry.csproj +++ b/src/Genocs.OpenTelemetry/Genocs.OpenTelemetry.csproj @@ -14,7 +14,7 @@ README_NUGET.md Upgraded to NET9.0 True - latest + latest @@ -32,12 +32,14 @@ - - - - - - + + + + + + + + - + diff --git a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json index aec14b7a..122302a4 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json +++ b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json @@ -83,9 +83,9 @@ }, "openTelemetry": { "enabled": true, - "otlpEndpoint": "http://localhost:4317", "exporter": { "enabled": true, + "otlpEndpoint": "http://localhost:4318", "protocol": "Grpc", "processorType": "Batch", "maxQueueSize": 2048, @@ -98,6 +98,13 @@ "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": { @@ -351,5 +358,11 @@ } } } + }, + "Logging": { + "LogLevel": { + "Default": "Debug", + "Microsoft.AspNetCore": "Debug" + } } } \ No newline at end of file From 3df72803f83a03922523c036cfe2686cd9c0bfec Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Wed, 4 Dec 2024 11:40:27 +0100 Subject: [PATCH 09/24] Add OtlpEndpoint to LoggerOptions and update exception handling Added a new property `OtlpEndpoint` to `LoggerOptions` for specifying the OpenTelemetry exporter endpoint. Updated `Extensions` class to conditionally set up OpenTelemetry logging based on the presence of `OtlpEndpoint`. Included a new `using` directive for `Genocs.Core.Exceptions` in `Extensions`. Replaced direct usage of `InvalidConfigurationException` with `GenocsException.InvalidConfigurationException` in `OpenTelemetryExtensions` for consistent exception handling. --- src/Genocs.Logging/Configurations/LoggerOptions.cs | 5 +++++ src/Genocs.Logging/Extensions.cs | 10 +++++++--- src/Genocs.OpenTelemetry/Extensions.cs | 5 ++--- 3 files changed, 14 insertions(+), 6 deletions(-) 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/Extensions.cs b/src/Genocs.Logging/Extensions.cs index ef5854af..34a1b8ee 100644 --- a/src/Genocs.Logging/Extensions.cs +++ b/src/Genocs.Logging/Extensions.cs @@ -103,10 +103,14 @@ private static void Configure(LoggerConfiguration loggerConfiguration, LoggerOpt var lokiOptions = options.Loki ?? new LokiOptions(); var azureOptions = options.Azure ?? new AzureOptions(); - loggerConfiguration.WriteTo.OpenTelemetry(wt => + // OpenTelemetry setup + if (!string.IsNullOrWhiteSpace(options.OtlpEndpoint)) { - wt.Endpoint = "http://localhost:4318"; - }); + loggerConfiguration.WriteTo.OpenTelemetry(wt => + { + wt.Endpoint = options.OtlpEndpoint; + }); + } // console if (consoleOptions.Enabled) diff --git a/src/Genocs.OpenTelemetry/Extensions.cs b/src/Genocs.OpenTelemetry/Extensions.cs index 0ec48caa..81f4ca5a 100644 --- a/src/Genocs.OpenTelemetry/Extensions.cs +++ b/src/Genocs.OpenTelemetry/Extensions.cs @@ -1,6 +1,7 @@ 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; @@ -9,7 +10,6 @@ using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; -using static Genocs.Core.Exceptions.GenocsException; namespace Genocs.GnxOpenTelemetry; @@ -19,7 +19,7 @@ public static IGenocsBuilder AddOpenTelemetry( this IGenocsBuilder builder) { AppOptions appOptions = builder.GetOptions(AppOptions.Position) - ?? throw new InvalidConfigurationException("app config section is missing. AddOpenTelemetry requires those configuration."); + ?? 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)) @@ -140,7 +140,6 @@ public static IGenocsBuilder AddOpenTelemetry( logging.IncludeFormattedMessage = true; logging.IncludeScopes = true; - // Setup the exporter if (openTelemetryOptions.Exporter?.Enabled == true) { From eda7085847b057983269dd6fd08c237644dfe553 Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Wed, 4 Dec 2024 13:12:16 +0100 Subject: [PATCH 10/24] Update error handling, tracing, and configuration - Add error handling in GenocsHttpClient for HTTP failures - Replace Genocs.OpenTelemetry with Genocs.Tracing in csproj - Update launchSettings.json: remove launchBrowser, change URLs - Change namespace in Startup.cs to Genocs.Tracing - Add Jaeger tracing settings in appsettings.json - Remove unused using directive in GetOrder.cs - Refactor GetProductHandler: update random range, exception logic --- src/Genocs.HTTP/GenocsHttpClient.cs | 16 ++++++++++++++-- .../Genocs.APIGateway/Genocs.APIGateway.csproj | 4 ++-- .../Properties/launchSettings.json | 1 - .../api-gateway/Genocs.APIGateway/Startup.cs | 2 +- .../Genocs.APIGateway/appsettings.json | 14 ++++++++++++++ .../Properties/launchSettings.json | 1 - .../Properties/launchSettings.json | 1 - .../Genocs.Orders.WebApi/Queries/GetOrder.cs | 1 - .../Properties/launchSettings.json | 1 - .../Queries/Handlers/GetProductHandler.cs | 8 ++++---- .../Properties/launchSettings.json | 1 - 11 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/Genocs.HTTP/GenocsHttpClient.cs b/src/Genocs.HTTP/GenocsHttpClient.cs index 7ccefdad..2c3c2005 100644 --- a/src/Genocs.HTTP/GenocsHttpClient.cs +++ b/src/Genocs.HTTP/GenocsHttpClient.cs @@ -124,9 +124,14 @@ public Task SendAsync(HttpRequestMessage request, IHttpClientSerializer? s .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) { + throw new HttpRequestException($"The HTTP request failed with status code {response.StatusCode}."); + // If not successful, return the default value for the type return default!; } @@ -207,11 +212,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/apps/api-gateway/Genocs.APIGateway/Genocs.APIGateway.csproj b/src/apps/api-gateway/Genocs.APIGateway/Genocs.APIGateway.csproj index fd0b5e1a..2584da73 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Genocs.APIGateway.csproj +++ b/src/apps/api-gateway/Genocs.APIGateway/Genocs.APIGateway.csproj @@ -14,7 +14,7 @@ - + @@ -26,7 +26,7 @@ - + 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 71428c8d..8ba5bdd1 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Startup.cs +++ b/src/apps/api-gateway/Genocs.APIGateway/Startup.cs @@ -5,7 +5,7 @@ using Genocs.MessageBrokers.RabbitMQ; using Genocs.Metrics.Prometheus; using Genocs.Security; -using Genocs.GnxOpenTelemetry; +using Genocs.Tracing; using Genocs.WebApi; using Yarp.ReverseProxy.Forwarder; diff --git a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json index 122302a4..343d67b2 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json +++ b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json @@ -81,6 +81,20 @@ }, "tags": {} }, + "jaeger": { + "enabled": true, + "serviceName": "api-gateway", + "udpHost": "localhost", + "udpPort": 6831, + "maxPacketSize": 65000, + "sampler": "const", + "excludePaths": [ + "/", + "/healthz", + "/alive", + "/metrics" + ] + }, "openTelemetry": { "enabled": true, "exporter": { 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/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/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 55254d62..8b53b04e 100644 --- a/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/GetProductHandler.cs +++ b/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/GetProductHandler.cs @@ -17,15 +17,15 @@ public GetProductHandler(IMongoDbBaseRepository repository public async Task HandleAsync(GetProduct query, CancellationToken cancellationToken = default) { - var product = await _repository.GetAsync(query.ProductId); - - int currentValue = _random.Next(1, 10); + int currentValue = _random.Next(0, 100); - if (currentValue > 6) + if (currentValue < 20) { throw new Exception("Random exception"); } + var product = await _repository.GetAsync(query.ProductId); + return product is null ? null : new ProductDto { Id = product.Id, SKU = product.SKU, UnitPrice = product.UnitPrice }; 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" From 2ba4d2829a4e635e2b52b0ea022716edd75c6f1c Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Wed, 4 Dec 2024 17:35:40 +0100 Subject: [PATCH 11/24] Refactor and update configurations and dependencies - Renamed `_endpoints` to `_allowAnonymousEndpoints` in `AccessTokenValidatorMiddleware.cs` - Removed `Microsoft.AspNetCore.Authentication.JwtBearer` package references from `Genocs.Auth.csproj` - Simplified `issuerSigningKey` initialization in `JwtHandler.cs` - Refactored `JwtAuthAttribute` to use a primary constructor - Renamed `GetCurrentAsync` to `GetCurrent` in `InMemoryAccessTokenService.cs` - Refactored `builder.AddGenocs()` to fluent style and added `app.UseGenocs()` in `Program.cs` - Updated `PackageTags` in `Genocs.Security.csproj` - Improved readability of `DispatcherEndpointsBuilder` class - Simplified `CustomForwarderHttpClientFactory.cs` - Commented out `services.AddAuthorization` in `Startup.cs` - Updated `appsettings.json` to disable certain features and change logging levels - Simplified variable declarations in `CreateUserHandler.cs` - Refactored `RevokeAccessTokenHandler` and `UnlockUserHandler` to use primary constructors - Refactored `InvalidRoleException` to use a primary constructor - Updated `builder` configuration in `Program.cs` to include `AddJwt` and other services - Enabled JWT and modified settings in `appsettings.json` - Streamlined `IGenocsBuilder` initialization in `Program.cs` --- .../AccessTokenValidatorMiddleware.cs | 8 ++--- src/Genocs.Auth/Genocs.Auth.csproj | 4 --- src/Genocs.Auth/Handlers/JwtHandler.cs | 8 ++--- src/Genocs.Auth/JwtAuthAttribute.cs | 8 ++--- .../Services/InMemoryAccessTokenService.cs | 6 ++-- src/Genocs.Core.Demo.WebApi/Program.cs | 5 ++- src/Genocs.Security/Genocs.Security.csproj | 2 +- .../Builders/DispatcherEndpointsBuilder.cs | 32 ++++++++++++------- .../CustomForwarderHttpClientFactory.cs | 9 ++---- .../api-gateway/Genocs.APIGateway/Startup.cs | 10 +++--- .../Genocs.APIGateway/appsettings.json | 14 ++++---- .../Commands/Handlers/CreateUserHandler.cs | 7 ++-- .../Handlers/RevokeAccessTokenHandler.cs | 11 +++---- .../Commands/Handlers/UnlockUserHandler.cs | 21 ++++-------- .../Domain/Exceptions/InvalidRoleException.cs | 6 ++-- .../Genocs.Identities.WebApi/Program.cs | 2 ++ .../Genocs.Identities.WebApi/appsettings.json | 13 ++++---- .../Genocs.Products.WebApi/Program.cs | 2 +- 18 files changed, 77 insertions(+), 91 deletions(-) diff --git a/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs b/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs index b0de9341..3902b9ef 100644 --- a/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs +++ b/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs @@ -10,7 +10,7 @@ namespace Genocs.Auth; public class AccessTokenValidatorMiddleware : IMiddleware { private readonly IAccessTokenService _accessTokenService; - private readonly IEnumerable _endpoints; + private readonly IEnumerable _allowAnonymousEndpoints; /// /// The AccessTokenValidatorMiddleware constructor. @@ -20,7 +20,7 @@ public class AccessTokenValidatorMiddleware : IMiddleware public AccessTokenValidatorMiddleware(IAccessTokenService accessTokenService, JwtOptions options) { _accessTokenService = accessTokenService; - _endpoints = options.AllowAnonymousEndpoints ?? Enumerable.Empty(); + _allowAnonymousEndpoints = options.AllowAnonymousEndpoints ?? Enumerable.Empty(); } /// @@ -33,10 +33,10 @@ 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; } diff --git a/src/Genocs.Auth/Genocs.Auth.csproj b/src/Genocs.Auth/Genocs.Auth.csproj index 6f732d10..0fa6533c 100644 --- a/src/Genocs.Auth/Genocs.Auth.csproj +++ b/src/Genocs.Auth/Genocs.Auth.csproj @@ -28,22 +28,18 @@ - - - - diff --git a/src/Genocs.Auth/Handlers/JwtHandler.cs b/src/Genocs.Auth/Handlers/JwtHandler.cs index 36e197e0..8da52ffa 100644 --- a/src/Genocs.Auth/Handlers/JwtHandler.cs +++ b/src/Genocs.Auth/Handlers/JwtHandler.cs @@ -27,12 +27,8 @@ internal sealed class JwtHandler : IJwtHandler 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."); 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..48ef5fa8 100644 --- a/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs +++ b/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs @@ -22,10 +22,10 @@ public InMemoryAccessTokenService( } public Task IsCurrentActiveToken() - => IsActiveAsync(GetCurrentAsync()); + => IsActiveAsync(GetCurrent()); public Task DeactivateCurrentAsync() - => DeactivateAsync(GetCurrentAsync()); + => DeactivateAsync(GetCurrent()); public Task IsActiveAsync(string token) => Task.FromResult(string.IsNullOrWhiteSpace(_cache.Get(GetKey(token)))); @@ -40,7 +40,7 @@ public Task DeactivateAsync(string token) return Task.CompletedTask; } - private string GetCurrentAsync() + private string GetCurrent() { var authorizationHeader = _httpContextAccessor .HttpContext?.Request.Headers["authorization"]; diff --git a/src/Genocs.Core.Demo.WebApi/Program.cs b/src/Genocs.Core.Demo.WebApi/Program.cs index a993d0ae..8468cfd6 100644 --- a/src/Genocs.Core.Demo.WebApi/Program.cs +++ b/src/Genocs.Core.Demo.WebApi/Program.cs @@ -20,7 +20,8 @@ .UseAzureKeyVault() .UseLogging(); -builder.AddGenocs() +builder + .AddGenocs() .AddJwt() .AddOpenTelemetry() .AddMongoFast() @@ -59,6 +60,8 @@ var app = builder.Build(); +app.UseGenocs(); + // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { diff --git a/src/Genocs.Security/Genocs.Security.csproj b/src/Genocs.Security/Genocs.Security.csproj index 377eefcb..14611885 100644 --- a/src/Genocs.Security/Genocs.Security.csproj +++ b/src/Genocs.Security/Genocs.Security.csproj @@ -10,7 +10,7 @@ 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 Upgraded to NET9.0 True diff --git a/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs index 96f572d6..d7d53246 100644 --- a/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs +++ b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs @@ -65,19 +65,26 @@ 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), @@ -128,9 +135,12 @@ public IDispatcherEndpointsBuilder Delete(string path, Func(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) { diff --git a/src/apps/api-gateway/Genocs.APIGateway/Framework/CustomForwarderHttpClientFactory.cs b/src/apps/api-gateway/Genocs.APIGateway/Framework/CustomForwarderHttpClientFactory.cs index 14cc5f93..9e7d538a 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Framework/CustomForwarderHttpClientFactory.cs +++ b/src/apps/api-gateway/Genocs.APIGateway/Framework/CustomForwarderHttpClientFactory.cs @@ -11,7 +11,7 @@ public CustomForwarderHttpClientFactory(CorrelationIdFactory correlationIdFactor { _correlationIdFactory = correlationIdFactory; } - + public HttpMessageInvoker CreateClient(ForwarderHttpClientContext context) { if (context.OldClient != null && context.NewConfig == context.OldConfig) @@ -34,7 +34,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,13 +53,10 @@ 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 readonly CorrelationIdFactory _correlationIdFactory; diff --git a/src/apps/api-gateway/Genocs.APIGateway/Startup.cs b/src/apps/api-gateway/Genocs.APIGateway/Startup.cs index 8ba5bdd1..8a8d006b 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Startup.cs +++ b/src/apps/api-gateway/Genocs.APIGateway/Startup.cs @@ -47,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 343d67b2..2c8865c6 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json +++ b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json @@ -8,19 +8,19 @@ "displayVersion": true }, "consul": { - "enabled": true, + "enabled": false, "url": "http://localhost:8500", "service": "api-gateway", "address": "docker.for.mac.localhost", "port": "5501", - "pingEnabled": true, + "pingEnabled": false, "pingEndpoint": "healthz", "pingInterval": 3, "removeAfterInterval": 3, "requestRetries": 3 }, "fabio": { - "enabled": true, + "enabled": false, "url": "http://localhost:9999", "service": "api-gateway", "requestRetries": 3 @@ -31,13 +31,13 @@ "services": { }, "requestMasking": { - "enabled": true, + "enabled": false, "maskTemplate": "*****" }, "correlationIdHeader": "x-correlation-id" }, "logger": { - "level": "debug", + "level": "warning", "excludePaths": [ "/", "/healthz", @@ -375,8 +375,8 @@ }, "Logging": { "LogLevel": { - "Default": "Debug", - "Microsoft.AspNetCore": "Debug" + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" } } } \ 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..6110ae9b 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/CreateUserHandler.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/CreateUserHandler.cs @@ -52,10 +52,9 @@ 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 role = string.IsNullOrWhiteSpace(command.Role) ? "user" : command.Role.ToLowerInvariant(); + string password = _passwordService.Hash(command.Password); + user = new User(command.UserId, command.Email, command.Name, password, role, 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)); 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..7c8330e5 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/RevokeAccessTokenHandler.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/RevokeAccessTokenHandler.cs @@ -3,14 +3,11 @@ 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); 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..a4b9a6a1 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/UnlockUserHandler.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/UnlockUserHandler.cs @@ -6,25 +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()) { await _userRepository.UpdateAsync(user); 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.WebApi/Program.cs b/src/apps/identity/Genocs.Identities.WebApi/Program.cs index 37e23570..df42df4b 100644 --- a/src/apps/identity/Genocs.Identities.WebApi/Program.cs +++ b/src/apps/identity/Genocs.Identities.WebApi/Program.cs @@ -9,6 +9,7 @@ using Genocs.Secrets.Vault; using Genocs.WebApi; using Genocs.WebApi.CQRS; +using Genocs.Auth; using Serilog; using Genocs.Discovery.Consul; @@ -22,6 +23,7 @@ IGenocsBuilder gnxBuilder = await builder .AddGenocs() + .AddJwt() .AddWebApi() .AddConsul() .AddFabio() diff --git a/src/apps/identity/Genocs.Identities.WebApi/appsettings.json b/src/apps/identity/Genocs.Identities.WebApi/appsettings.json index 0f06a4c6..274a343b 100644 --- a/src/apps/identity/Genocs.Identities.WebApi/appsettings.json +++ b/src/apps/identity/Genocs.Identities.WebApi/appsettings.json @@ -8,18 +8,18 @@ "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 }, "fabio": { - "enabled": true, + "enabled": false, "url": "http://localhost:9999", "service": "identity-service", "requestRetries": 3 @@ -30,7 +30,7 @@ "services": { }, "requestMasking": { - "enabled": true, + "enabled": false, "maskTemplate": "*****" }, "correlationIdHeader": "x-correlation-id" @@ -95,6 +95,7 @@ ] }, "jwt": { + "enabled": true, "certificate": { "location": "certs/localhost.pfx", "password": "test", @@ -103,8 +104,8 @@ "issuer": "genocs-identity-service", "validIssuer": "genocs-identity-service", "validateAudience": false, - "validateIssuer": true, - "validateLifetime": true, + "validateIssuer": false, + "validateLifetime": false, "expiry": "01:00:00" }, "metrics": { 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() From db339c2ef27cd15cbbde0bfe9b1c9dac6e0a0c12 Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Thu, 5 Dec 2024 12:37:00 +0100 Subject: [PATCH 12/24] Refactor for multiple roles and sync methods Updated various classes and interfaces to support multiple roles instead of a single role, including `JwtHandler`, `JsonWebToken`, `User`, and DTO classes. Made methods synchronous in `IAccessTokenService` and related classes. Introduced policy-based authorization in `Program.cs` and added `Policies` and `Roles` classes for better role management. Reorganized `using` directives and improved handling of nullable values. --- .../AccessTokenValidatorMiddleware.cs | 3 +- src/Genocs.Auth/Handlers/JwtHandler.cs | 25 +++--- src/Genocs.Auth/IAccessTokenService.cs | 8 +- src/Genocs.Auth/IJwtHandler.cs | 4 +- src/Genocs.Auth/JsonWebToken.cs | 2 +- src/Genocs.Auth/JsonWebTokenPayload.cs | 2 +- .../Services/InMemoryAccessTokenService.cs | 22 ++--- .../Builders/DispatcherEndpointsBuilder.cs | 80 +++++++++++-------- .../IDispatcherEndpointsBuilder.cs | 24 +++--- src/Genocs.WebApi/EndpointsBuilder.cs | 45 ++++++----- src/Genocs.WebApi/IEndpointsBuilder.cs | 6 +- .../Commands/Handlers/CreateUserHandler.cs | 4 +- .../Handlers/RevokeAccessTokenHandler.cs | 6 +- .../Commands/Handlers/SignInHandler.cs | 17 ++-- .../Handlers/UseRefreshTokenHandler.cs | 2 +- .../DTO/AuthDto.cs | 6 +- .../DTO/UserDetailsDto.cs | 6 +- .../DTO/UserDto.cs | 2 +- .../Domain/Constants/Policies.cs | 7 ++ .../Domain/Constants/Roles.cs | 8 ++ .../Domain/Entities/Role.cs | 9 ++- .../Domain/Entities/User.cs | 14 ++-- .../Events/UserCreated.cs | 15 +--- .../Extensions.cs | 22 +++++ .../Mongo/Documents/UserDocument.cs | 6 +- .../Mongo/Queries/Handlers/GetUserHandler.cs | 2 +- .../Services/IJwtProvider.cs | 8 +- .../Services/JwtProvider.cs | 6 +- .../Genocs.Identities.WebApi/Program.cs | 13 +-- 29 files changed, 218 insertions(+), 156 deletions(-) create mode 100644 src/apps/identity/Genocs.Identities.Application/Domain/Constants/Policies.cs create mode 100644 src/apps/identity/Genocs.Identities.Application/Domain/Constants/Roles.cs diff --git a/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs b/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs index 3902b9ef..9af383c6 100644 --- a/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs +++ b/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs @@ -40,10 +40,9 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) return; } - if (await _accessTokenService.IsCurrentActiveToken()) + if (_accessTokenService.IsCurrentActiveToken()) { await next(context); - return; } diff --git a/src/Genocs.Auth/Handlers/JwtHandler.cs b/src/Genocs.Auth/Handlers/JwtHandler.cs index 8da52ffa..e5c36877 100644 --- a/src/Genocs.Auth/Handlers/JwtHandler.cs +++ b/src/Genocs.Auth/Handlers/JwtHandler.cs @@ -1,7 +1,8 @@ -using Genocs.Auth.Configurations; -using Microsoft.IdentityModel.Tokens; +using System.Data; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; +using Genocs.Auth.Configurations; +using Microsoft.IdentityModel.Tokens; namespace Genocs.Auth.Handlers; @@ -29,6 +30,7 @@ public JwtHandler(JwtOptions options, TokenValidationParameters tokenValidationP { var issuerSigningKey = tokenValidationParameters.IssuerSigningKey ?? throw new InvalidOperationException("Issuer signing key not set."); + if (string.IsNullOrWhiteSpace(options.Algorithm)) { throw new InvalidOperationException("Security algorithm not set."); @@ -43,15 +45,15 @@ public JwtHandler(JwtOptions options, TokenValidationParameters tokenValidationP /// /// Creates a new token. /// - /// - /// + /// The UserId. + /// The User Role. /// /// /// /// It is thrown when mandatory data is empty. public JsonWebToken CreateToken( string userId, - string? role = null, + IEnumerable? roles = null, string? audience = null, IDictionary>? claims = null) { @@ -69,9 +71,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)) @@ -109,7 +114,7 @@ public JsonWebToken CreateToken( RefreshToken = string.Empty, Expires = expires.ToTimestamp(), Id = userId, - Role = role ?? string.Empty, + Roles = roles, Claims = claims ?? EmptyClaims }; } @@ -118,7 +123,7 @@ public JsonWebToken CreateToken( /// Gets the token payload. /// /// - /// + /// The JWT Token payload. public JsonWebTokenPayload? GetTokenPayload(string accessToken) { _jwtSecurityTokenHandler.ValidateToken( @@ -134,7 +139,7 @@ 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) 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..5a6a2f08 100644 --- a/src/Genocs.Auth/IJwtHandler.cs +++ b/src/Genocs.Auth/IJwtHandler.cs @@ -9,13 +9,13 @@ 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, + IEnumerable? roles = null, string? audience = null, IDictionary>? claims = null); diff --git a/src/Genocs.Auth/JsonWebToken.cs b/src/Genocs.Auth/JsonWebToken.cs index bdf2eefc..0aae4c7e 100644 --- a/src/Genocs.Auth/JsonWebToken.cs +++ b/src/Genocs.Auth/JsonWebToken.cs @@ -28,7 +28,7 @@ public class JsonWebToken /// /// 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..59013a65 100644 --- a/src/Genocs.Auth/JsonWebTokenPayload.cs +++ b/src/Genocs.Auth/JsonWebTokenPayload.cs @@ -13,7 +13,7 @@ public class JsonWebTokenPayload /// /// The Identity Role. /// - public string? Role { get; set; } + public IEnumerable? Roles { get; set; } /// /// The expiration ticks. diff --git a/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs b/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs index 48ef5fa8..5ac36282 100644 --- a/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs +++ b/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs @@ -5,6 +5,10 @@ namespace Genocs.Auth.Services; +/// +/// 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 : IAccessTokenService { private readonly IMemoryCache _cache; @@ -21,23 +25,21 @@ public InMemoryAccessTokenService( _expires = jwtOptions.Expiry ?? TimeSpan.FromMinutes(jwtOptions.ExpiryMinutes); } - public Task IsCurrentActiveToken() - => IsActiveAsync(GetCurrent()); + public bool IsCurrentActiveToken() + => IsActive(GetCurrent()); - public Task DeactivateCurrentAsync() - => DeactivateAsync(GetCurrent()); + 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 GetCurrent() @@ -52,7 +54,7 @@ private string GetCurrent() 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.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs index d7d53246..ce436aa6 100644 --- a/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs +++ b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs @@ -6,14 +6,9 @@ namespace Genocs.WebApi.CQRS.Builders; -public class DispatcherEndpointsBuilder : IDispatcherEndpointsBuilder +public class DispatcherEndpointsBuilder(IEndpointsBuilder builder) : IDispatcherEndpointsBuilder { - private readonly IEndpointsBuilder _builder; - - public DispatcherEndpointsBuilder(IEndpointsBuilder builder) - { - _builder = builder; - } + private readonly IEndpointsBuilder _builder = builder; public IDispatcherEndpointsBuilder Get( string path, @@ -79,16 +74,16 @@ public IDispatcherEndpointsBuilder Post( public 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, 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; } @@ -102,44 +97,56 @@ 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) + HttpContext? context, + Func? beforeDispatch = null, + Func? afterDispatch = null) where T : class, ICommand { if (beforeDispatch is not null) @@ -147,11 +154,20 @@ private static async Task BuildCommandContext( await beforeDispatch(command, context); } - var dispatcher = context.RequestServices.GetRequiredService(); - await dispatcher.SendAsync(command); + var dispatcher = context?.RequestServices.GetRequiredService(); + + if (dispatcher != null) + { + await dispatcher.SendAsync(command); + } + if (afterDispatch is null) { - context.Response.StatusCode = 200; + if (context != null) + { + context.Response.StatusCode = 200; + } + return; } 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/EndpointsBuilder.cs b/src/Genocs.WebApi/EndpointsBuilder.cs index efad9aa9..98413c3c 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)); @@ -134,18 +132,19 @@ 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); + bool hasRoles = !string.IsNullOrWhiteSpace(roles); var authorize = new AuthorizeAttribute(); if (hasRoles) { @@ -158,8 +157,9 @@ private static void ApplyAuthRolesAndPolicies(IEndpointConventionBuilder builder } } - 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 +171,9 @@ 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/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/apps/identity/Genocs.Identities.Application/Commands/Handlers/CreateUserHandler.cs b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/CreateUserHandler.cs index 6110ae9b..88dc25bf 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/CreateUserHandler.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/CreateUserHandler.cs @@ -54,9 +54,9 @@ public async Task HandleAsync(CreateUser command, CancellationToken cancellation string role = string.IsNullOrWhiteSpace(command.Role) ? "user" : command.Role.ToLowerInvariant(); string password = _passwordService.Hash(command.Password); - user = new User(command.UserId, command.Email, command.Name, password, role, DateTime.UtcNow, command.Permissions); + user = new User(command.UserId, command.Email, command.Name, password, new List { role }, 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/RevokeAccessTokenHandler.cs b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/RevokeAccessTokenHandler.cs index 7c8330e5..1d896078 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/RevokeAccessTokenHandler.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/RevokeAccessTokenHandler.cs @@ -10,5 +10,9 @@ internal sealed class RevokeAccessTokenHandler(IAccessTokenService accessTokenSe ?? 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/UseRefreshTokenHandler.cs b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/UseRefreshTokenHandler.cs index f661f506..dd2ff5c2 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/UseRefreshTokenHandler.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/UseRefreshTokenHandler.cs @@ -47,7 +47,7 @@ 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/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/UserDetailsDto.cs b/src/apps/identity/Genocs.Identities.Application/DTO/UserDetailsDto.cs index cf9a88f7..65341ac5 100644 --- a/src/apps/identity/Genocs.Identities.Application/DTO/UserDetailsDto.cs +++ b/src/apps/identity/Genocs.Identities.Application/DTO/UserDetailsDto.cs @@ -1,12 +1,12 @@ 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/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/Extensions.cs b/src/apps/identity/Genocs.Identities.Application/Extensions.cs index 660697cc..e92d7601 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; @@ -28,6 +30,7 @@ using Genocs.WebApi.CQRS; using Genocs.WebApi.Swagger; using Genocs.WebApi.Swagger.Docs; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; @@ -82,6 +85,25 @@ public static async Task AddCoreAsync(this IGenocsBuilder builde builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(OutboxCommandHandlerDecorator<>)); builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(OutboxEventHandlerDecorator<>)); + // Authorization: Split on a separate method + //builder.Services.AddAuthorization(options => + //{ + // options.AddPolicy("HasAdminRole", policy => policy.RequireRole("admin")); + // options.AddPolicy("HasUserRole", policy => policy.RequireRole("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))); + return builder; } 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..afe17aa6 100644 --- a/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/UserDocument.cs +++ b/src/apps/identity/Genocs.Identities.Application/Mongo/Documents/UserDocument.cs @@ -9,7 +9,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 +24,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 User(Id, Email, Name, Password, Roles, CreatedAt, Permissions, Locked); public bool IsTransient() { 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..ed755d6f 100644 --- a/src/apps/identity/Genocs.Identities.Application/Services/JwtProvider.cs +++ b/src/apps/identity/Genocs.Identities.Application/Services/JwtProvider.cs @@ -15,18 +15,18 @@ public JwtProvider(IJwtHandler jwtHandler) public AuthDto Create( Guid userId, string username, - string role, + IEnumerable roles, 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.WebApi/Program.cs b/src/apps/identity/Genocs.Identities.WebApi/Program.cs index df42df4b..a3e0cceb 100644 --- a/src/apps/identity/Genocs.Identities.WebApi/Program.cs +++ b/src/apps/identity/Genocs.Identities.WebApi/Program.cs @@ -1,6 +1,9 @@ +using Genocs.Auth; using Genocs.Core.Builders; +using Genocs.Discovery.Consul; 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; @@ -9,9 +12,7 @@ using Genocs.Secrets.Vault; using Genocs.WebApi; using Genocs.WebApi.CQRS; -using Genocs.Auth; using Serilog; -using Genocs.Discovery.Consul; StaticLogger.EnsureInitialized(); @@ -47,17 +48,17 @@ ctx.Response.Headers.Append("user-id", cmd.UserId.ToString()); return Task.CompletedTask; }) - .Post("access-tokens/revoke", auth: true, roles: "admin") + .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(); From dd2dfdfe40cd45cc3ba02d7aaa04f7b8c1368f47 Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Thu, 5 Dec 2024 14:49:52 +0100 Subject: [PATCH 13/24] Add CreateAdmin feature and refactor user creation Refactored `CreateUser` and `CreateUserHandler` for cleaner syntax and constructor injection. Updated `EndpointsBuilder` to allow anonymous access when no authentication or roles are required. Enhanced `Extensions` for better authorization policy formatting and middleware usage. Added `CreateAdmin` command, handler, and endpoint with specific authorization policies. --- src/Genocs.WebApi/EndpointsBuilder.cs | 4 ++ .../Commands/CreateAdmin.cs | 12 +++++ .../Commands/CreateUser.cs | 29 +++------- .../Commands/Handlers/CreateAdminHandler.cs | 54 +++++++++++++++++++ .../Commands/Handlers/CreateUserHandler.cs | 24 +++------ .../Extensions.cs | 25 ++++----- .../Genocs.Identities.WebApi/Program.cs | 5 ++ 7 files changed, 102 insertions(+), 51 deletions(-) create mode 100644 src/apps/identity/Genocs.Identities.Application/Commands/CreateAdmin.cs create mode 100644 src/apps/identity/Genocs.Identities.Application/Commands/Handlers/CreateAdminHandler.cs diff --git a/src/Genocs.WebApi/EndpointsBuilder.cs b/src/Genocs.WebApi/EndpointsBuilder.cs index 98413c3c..754d1505 100644 --- a/src/Genocs.WebApi/EndpointsBuilder.cs +++ b/src/Genocs.WebApi/EndpointsBuilder.cs @@ -154,7 +154,11 @@ private static void ApplyAuthRolesAndPolicies( if (auth || hasRoles) { builder.RequireAuthorization(authorize); + return; } + + // I don't like this, but it is the only way to allow anonymous access + builder.AllowAnonymous(); } private static async Task BuildRequestContext( 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 88dc25bf..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,9 +45,8 @@ public async Task HandleAsync(CreateUser command, CancellationToken cancellation throw new NameInUseException(command.Name); } - string role = string.IsNullOrWhiteSpace(command.Role) ? "user" : command.Role.ToLowerInvariant(); string password = _passwordService.Hash(command.Password); - user = new User(command.UserId, command.Email, command.Name, password, new List { role }, DateTime.UtcNow, command.Permissions); + 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.Roles)); diff --git a/src/apps/identity/Genocs.Identities.Application/Extensions.cs b/src/apps/identity/Genocs.Identities.Application/Extensions.cs index e92d7601..d6d81586 100644 --- a/src/apps/identity/Genocs.Identities.Application/Extensions.cs +++ b/src/apps/identity/Genocs.Identities.Application/Extensions.cs @@ -94,15 +94,15 @@ public static async Task AddCoreAsync(this IGenocsBuilder builde 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))); + .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; } @@ -114,16 +114,17 @@ public static IApplicationBuilder UseCore(this IApplicationBuilder app) { app.UseMiddleware() .UseErrorHandler() - //.UseJaeger() .UseSwaggerDocs() .UseGenocs() .UseAccessTokenValidator() .UseMongo() .UsePublicContracts() .UseAuthentication() + .UseAuthorization() .UseMetrics() .UseRabbitMQ() - .SubscribeCommand(); + .SubscribeCommand() + .SubscribeCommand(); return app; } @@ -143,7 +144,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.WebApi/Program.cs b/src/apps/identity/Genocs.Identities.WebApi/Program.cs index a3e0cceb..2e8b89f2 100644 --- a/src/apps/identity/Genocs.Identities.WebApi/Program.cs +++ b/src/apps/identity/Genocs.Identities.WebApi/Program.cs @@ -48,6 +48,11 @@ ctx.Response.Headers.Append("user-id", cmd.UserId.ToString()); return Task.CompletedTask; }) + .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) => { From 07bee95f1226ab4406078380da2b25744f413d8e Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Thu, 5 Dec 2024 19:40:21 +0100 Subject: [PATCH 14/24] Refactor and improve nullability handling Refactored multiple classes for better readability and nullability handling: - `DispatcherEndpointsBuilder`: Updated `dispatcher` to `ICommandDispatcher?`, improved `afterDispatch` logic, and ensured `context.Response.StatusCode` is set to 200 if `context` is not null. - `EndpointsBuilder`: Reformatted methods to single lines. - `ExceptionResponse`: Included constructor parameters directly in the class definition. - `IExceptionToResponseMapper`: Allowed `Map` method to return nullable `ExceptionResponse`. - `Extensions`: Reformatted methods to single lines, allowed nullable returns and variables. - `IRequest` and `WebApiEndpointDefinitions`: Removed unnecessary braces. --- .../Builders/DispatcherEndpointsBuilder.cs | 16 +++++++--------- src/Genocs.WebApi/EndpointsBuilder.cs | 14 +++----------- .../Exceptions/ExceptionResponse.cs | 12 +++--------- .../Exceptions/IExceptionToResponseMapper.cs | 2 +- src/Genocs.WebApi/Extensions.cs | 12 +++++------- src/Genocs.WebApi/Requests/IRequest.cs | 4 +--- src/Genocs.WebApi/WebApiEndpointDefinition.cs | 4 +--- 7 files changed, 21 insertions(+), 43 deletions(-) diff --git a/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs index ce436aa6..415a4652 100644 --- a/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs +++ b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs @@ -154,23 +154,21 @@ private static async Task BuildCommandContext( await beforeDispatch(command, context); } - var dispatcher = context?.RequestServices.GetRequiredService(); + ICommandDispatcher? dispatcher = context?.RequestServices.GetRequiredService(); if (dispatcher != null) { await dispatcher.SendAsync(command); } - if (afterDispatch is null) + if (afterDispatch is not null) { - if (context != null) - { - context.Response.StatusCode = 200; - } - - return; + await afterDispatch(command, context); } - await afterDispatch(command, context); + if (context != null) + { + context.Response.StatusCode = 200; + } } } \ No newline at end of file diff --git a/src/Genocs.WebApi/EndpointsBuilder.cs b/src/Genocs.WebApi/EndpointsBuilder.cs index 754d1505..9d9fdb36 100644 --- a/src/Genocs.WebApi/EndpointsBuilder.cs +++ b/src/Genocs.WebApi/EndpointsBuilder.cs @@ -132,11 +132,7 @@ 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?.Any() == true) { @@ -161,9 +157,7 @@ private static void ApplyAuthRolesAndPolicies( 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(); @@ -175,9 +169,7 @@ private static async Task BuildRequestContext( 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/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 { From 74926b3edc638b9a7179ae6d3e746eec346cd0ab Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Wed, 11 Dec 2024 19:24:28 +0100 Subject: [PATCH 15/24] Refactor and update codebase for C# 9.0 features Refactored multiple classes to use constructors with parameters and C# 9.0's target-typed new expressions. Updated various methods to return nullable types. Enhanced XML documentation for several interfaces and classes. Modified project files to include a new "logs" folder. Adjusted `appsettings.json` configuration values. Simplified method signatures and object creation across the codebase. Ensured proper middleware order in `Program.cs` and improved static asset handling. --- .../AccessTokenValidatorMiddleware.cs | 22 +++++-------- src/Genocs.Auth/JsonWebToken.cs | 10 +++--- .../Genocs.Core.Demo.HelloWorld.csproj | 4 +++ .../Genocs.Core.Demo.WebApi.csproj | 4 +++ src/Genocs.HTTP/GenocsHttpClient.cs | 30 +++++++---------- src/Genocs.HTTP/GenocsHttpLoggingFilter.cs | 30 +++++++---------- .../GenocsLoggingScopeHttpMessageHandler.cs | 10 +++--- src/Genocs.HTTP/ICorrelationContextFactory.cs | 7 ++++ src/Genocs.HTTP/ICorrelationIdFactory.cs | 2 +- src/Genocs.HTTP/IHttpClient.cs | 32 ++++++++++++------- .../IExceptionToMessageMapper.cs | 2 +- src/Genocs.WebApi/EndpointsBuilder.cs | 4 +-- src/Genocs.WebApi/GenocsFormatterResolver.cs | 9 +++--- .../CustomForwarderHttpClientFactory.cs | 25 ++++----------- .../Framework/ICorrelationContextBuilder.cs | 7 +--- .../Framework/LogContextMiddleware.cs | 10 ++---- .../Framework/MessagingMiddleware.cs | 7 ++-- .../Genocs.APIGateway/appsettings.json | 6 ++-- .../Commands/Handlers/LockUserHandler.cs | 19 +++-------- .../Handlers/RevokeAccessTokenHandler.cs | 6 +--- .../Commands/Handlers/UnlockUserHandler.cs | 1 + .../Handlers/UseRefreshTokenHandler.cs | 17 +++------- .../Commands/LockUser.cs | 9 ++---- .../Commands/RevokeAccessToken.cs | 9 ++---- .../Commands/RevokeRefreshToken.cs | 9 ++---- .../Commands/SignIn.cs | 12 ++----- .../Commands/UnlockUser.cs | 9 ++---- .../Commands/UseRefreshToken.cs | 9 ++---- .../ContractAttribute.cs | 8 ++--- .../CorrelationIdFactory.cs | 5 +-- .../DTO/PagedDto.cs | 7 ++-- .../DTO/UserDetailsDto.cs | 1 - .../Exceptions/AppException.cs | 8 +---- .../Exceptions/ExceptionToMessageMapper.cs | 5 ++- .../Extensions.cs | 3 +- .../Logging/LogContextMiddleware.cs | 2 +- .../Mongo/Documents/RefreshTokenDocument.cs | 2 +- .../Mongo/Documents/UserDocument.cs | 3 +- .../Queries/Handlers/BrowseUsersHandler.cs | 12 +++---- .../Services/MessageBroker.cs | 25 +++------------ .../Services/Rng.cs | 6 ++-- .../Genocs.Identities.WebApi/Program.cs | 4 +-- .../Genocs.Orders.WebApi.csproj | 4 +++ .../Genocs.SignalR.WebApi.csproj | 4 +++ .../signalr/Genocs.SignalR.WebApi/Program.cs | 2 +- 45 files changed, 158 insertions(+), 264 deletions(-) diff --git a/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs b/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs index 9af383c6..e018bb43 100644 --- a/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs +++ b/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs @@ -7,21 +7,15 @@ 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 _allowAnonymousEndpoints; - - /// - /// The AccessTokenValidatorMiddleware constructor. - /// - /// The access token service. - /// The options. - public AccessTokenValidatorMiddleware(IAccessTokenService accessTokenService, JwtOptions options) - { - _accessTokenService = accessTokenService; - _allowAnonymousEndpoints = options.AllowAnonymousEndpoints ?? Enumerable.Empty(); - } + private readonly IAccessTokenService _accessTokenService = accessTokenService; + private readonly IEnumerable _allowAnonymousEndpoints = options.AllowAnonymousEndpoints ?? []; /// /// The InvokeAsync method. diff --git a/src/Genocs.Auth/JsonWebToken.cs b/src/Genocs.Auth/JsonWebToken.cs index 0aae4c7e..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,11 +25,6 @@ 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. /// diff --git a/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.csproj b/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.csproj index 346fc30a..3632b0dd 100644 --- a/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.csproj +++ b/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.csproj @@ -15,4 +15,8 @@ + + + + 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 389dbaf5..043cf442 100644 --- a/src/Genocs.Core.Demo.WebApi/Genocs.Core.Demo.WebApi.csproj +++ b/src/Genocs.Core.Demo.WebApi/Genocs.Core.Demo.WebApi.csproj @@ -42,4 +42,8 @@ + + + + diff --git a/src/Genocs.HTTP/GenocsHttpClient.cs b/src/Genocs.HTTP/GenocsHttpClient.cs index 2c3c2005..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,7 +119,7 @@ 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 () => @@ -130,13 +130,11 @@ public Task SendAsync(HttpRequestMessage request, IHttpClientSerializer? s // Check if the response indicates a successful status code if (!response.IsSuccessStatusCode) { + // If not successful, throw an exception so the retry will come. throw new HttpRequestException($"The HTTP request failed with status code {response.StatusCode}."); - // If not successful, return the default value for the type - return default!; } var stream = await response.Content.ReadAsStreamAsync(); - return await DeserializeJsonFromStream(stream, serializer); }); @@ -178,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) @@ -191,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) 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.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.WebApi/EndpointsBuilder.cs b/src/Genocs.WebApi/EndpointsBuilder.cs index 9d9fdb36..e1a7113e 100644 --- a/src/Genocs.WebApi/EndpointsBuilder.cs +++ b/src/Genocs.WebApi/EndpointsBuilder.cs @@ -119,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)); 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/apps/api-gateway/Genocs.APIGateway/Framework/CustomForwarderHttpClientFactory.cs b/src/apps/api-gateway/Genocs.APIGateway/Framework/CustomForwarderHttpClientFactory.cs index 9e7d538a..d967fbb0 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/Framework/CustomForwarderHttpClientFactory.cs +++ b/src/apps/api-gateway/Genocs.APIGateway/Framework/CustomForwarderHttpClientFactory.cs @@ -3,14 +3,9 @@ namespace Genocs.APIGateway.Framework; -internal class CustomForwarderHttpClientFactory : IForwarderHttpClientFactory +internal class CustomForwarderHttpClientFactory(CorrelationIdFactory correlationIdFactory) : IForwarderHttpClientFactory { - private readonly CorrelationIdFactory _correlationIdFactory; - - public CustomForwarderHttpClientFactory(CorrelationIdFactory correlationIdFactory) - { - _correlationIdFactory = correlationIdFactory; - } + private readonly CorrelationIdFactory _correlationIdFactory = correlationIdFactory; public HttpMessageInvoker CreateClient(ForwarderHttpClientContext context) { @@ -57,20 +52,14 @@ public HttpMessageInvoker CreateClient(ForwarderHttpClientContext context) 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/appsettings.json b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json index 2c8865c6..9184a905 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json +++ b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json @@ -129,12 +129,12 @@ "password": "test", "rawData": "" }, - "expiryMinutes": 30, + "expiryMinutes": 120, "issuer": "genocs-identity-service", "validIssuer": "genocs-identity-service", "validateAudience": false, - "validateIssuer": true, - "validateLifetime": true, + "validateIssuer": false, + "validateLifetime": false, "expiry": "01:00:00" }, "metrics": { 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 1d896078..bfd25250 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/RevokeAccessTokenHandler.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/RevokeAccessTokenHandler.cs @@ -10,9 +10,5 @@ internal sealed class RevokeAccessTokenHandler(IAccessTokenService accessTokenSe ?? throw new ArgumentNullException(nameof(accessTokenService)); public async Task HandleAsync(RevokeAccessToken command, CancellationToken cancellationToken = default) - => await Task.Run(() => - { - _accessTokenService.Deactivate(command.AccessToken); - }, - cancellationToken); + => 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/UnlockUserHandler.cs b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/UnlockUserHandler.cs index a4b9a6a1..aa971c4e 100644 --- a/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/UnlockUserHandler.cs +++ b/src/apps/identity/Genocs.Identities.Application/Commands/Handlers/UnlockUserHandler.cs @@ -16,6 +16,7 @@ public async Task HandleAsync(UnlockUser command, CancellationToken cancellation { var user = await _userRepository.GetAsync(command.UserId) ?? throw new UserNotFoundException(command.UserId); + if (user.Unlock()) { await _userRepository.UpdateAsync(user); 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 dd2ff5c2..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,6 +37,7 @@ public async Task HandleAsync(UseRefreshToken command, CancellationToken cancell ["permissions"] = user.Permissions } : null; + 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/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 65341ac5..75b37af2 100644 --- a/src/apps/identity/Genocs.Identities.Application/DTO/UserDetailsDto.cs +++ b/src/apps/identity/Genocs.Identities.Application/DTO/UserDetailsDto.cs @@ -8,5 +8,4 @@ public class UserDetailsDto : UserDto 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/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 d6d81586..0e292bcd 100644 --- a/src/apps/identity/Genocs.Identities.Application/Extensions.cs +++ b/src/apps/identity/Genocs.Identities.Application/Extensions.cs @@ -116,11 +116,10 @@ public static IApplicationBuilder UseCore(this IApplicationBuilder app) .UseErrorHandler() .UseSwaggerDocs() .UseGenocs() - .UseAccessTokenValidator() + .UseAccessTokenValidator() // Implement the Authorization. .UseMongo() .UsePublicContracts() .UseAuthentication() - .UseAuthorization() .UseMetrics() .UseRabbitMQ() .SubscribeCommand() 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 afe17aa6..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; @@ -31,7 +30,7 @@ public UserDocument(User user) Locked = user.Locked; } - public User ToEntity() => new User(Id, Email, Name, Password, Roles, 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/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/Program.cs b/src/apps/identity/Genocs.Identities.WebApi/Program.cs index 2e8b89f2..743d4ce1 100644 --- a/src/apps/identity/Genocs.Identities.WebApi/Program.cs +++ b/src/apps/identity/Genocs.Identities.WebApi/Program.cs @@ -35,8 +35,6 @@ app.MapDefaultEndpoints(); -app.UseCore(); - app.UseDispatcherEndpoints(endpoints => endpoints .Post("sign-in", afterDispatch: (cmd, ctx) => { @@ -65,6 +63,8 @@ .Put("users/{userId:guid}/lock", auth: true, policies: [Policies.AdminOnly]) .Put("users/{userId:guid}/unlock", auth: true, policies: [Policies.AdminOnly])); +app.UseCore(); + app.Run(); Log.CloseAndFlush(); \ No newline at end of file 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/signalr/Genocs.SignalR.WebApi/Genocs.SignalR.WebApi.csproj b/src/apps/signalr/Genocs.SignalR.WebApi/Genocs.SignalR.WebApi.csproj index 16de3ace..c3206cd4 100644 --- a/src/apps/signalr/Genocs.SignalR.WebApi/Genocs.SignalR.WebApi.csproj +++ b/src/apps/signalr/Genocs.SignalR.WebApi/Genocs.SignalR.WebApi.csproj @@ -51,4 +51,8 @@ + + + + \ No newline at end of file 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(); From b525d35a18add383b0960d4405069b91a97decd5 Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Fri, 13 Dec 2024 17:23:01 +0100 Subject: [PATCH 16/24] Refactor JWT handling and improve code readability - Updated `AddJwt` method for better syntax and formatting. - Allowed anonymous access for health check routes. - Adjusted `afterDispatch` logic in `DispatcherEndpointsBuilder`. - Refactored `JwtProvider` and `GenocsHub` to use inline constructor parameters. - Enhanced `appsettings.json` for JWT configuration flexibility. - Cleaned up commented-out authorization policies in `Extensions`. --- src/Genocs.Auth/Extensions.cs | 5 +--- src/Genocs.Core/Builders/Extensions.cs | 6 ++-- .../Builders/DispatcherEndpointsBuilder.cs | 8 +++--- .../Extensions.cs | 28 +++++++++++-------- .../Services/JwtProvider.cs | 9 ++---- .../Genocs.Identities.WebApi/appsettings.json | 6 +++- .../Genocs.SignalR.WebApi/Hubs/GenocsHub.cs | 7 ++--- 7 files changed, 33 insertions(+), 36 deletions(-) diff --git a/src/Genocs.Auth/Extensions.cs b/src/Genocs.Auth/Extensions.cs index 05e93b9f..5b4776d8 100644 --- a/src/Genocs.Auth/Extensions.cs +++ b/src/Genocs.Auth/Extensions.cs @@ -34,10 +34,7 @@ public static IGenocsBuilder AddJwt( return builder.AddJwt(options, optionsFactory); } - private static IGenocsBuilder AddJwt( - this IGenocsBuilder builder, - JwtOptions options, - Action? optionsFactory = null) + private static IGenocsBuilder AddJwt(this IGenocsBuilder builder, JwtOptions options, Action? optionsFactory = null) { if (!builder.TryRegister(RegistryName)) { diff --git a/src/Genocs.Core/Builders/Extensions.cs b/src/Genocs.Core/Builders/Extensions.cs index e63a36f9..ca6262c1 100644 --- a/src/Genocs.Core/Builders/Extensions.cs +++ b/src/Genocs.Core/Builders/Extensions.cs @@ -145,16 +145,16 @@ 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"); + 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; } diff --git a/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs index 415a4652..b9ce0f29 100644 --- a/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs +++ b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs @@ -161,14 +161,14 @@ private static async Task BuildCommandContext( await dispatcher.SendAsync(command); } - if (afterDispatch is not null) + if (context != null) { - await afterDispatch(command, context); + context.Response.StatusCode = 200; } - if (context != null) + if (afterDispatch is not null) { - context.Response.StatusCode = 200; + await afterDispatch(command, context); } } } \ 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 0e292bcd..4b922eaf 100644 --- a/src/apps/identity/Genocs.Identities.Application/Extensions.cs +++ b/src/apps/identity/Genocs.Identities.Application/Extensions.cs @@ -30,7 +30,6 @@ using Genocs.WebApi.CQRS; using Genocs.WebApi.Swagger; using Genocs.WebApi.Swagger.Docs; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; @@ -63,7 +62,6 @@ public static async Task AddCoreAsync(this IGenocsBuilder builde .AddInMemoryCommandDispatcher() .AddInMemoryEventDispatcher() .AddInMemoryQueryDispatcher() - .AddJwt() .AddHttpClient(); await builder.AddRabbitMQAsync(); @@ -93,16 +91,22 @@ public static async Task AddCoreAsync(this IGenocsBuilder builde //}); 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))); + .AddPolicy(Policies.UserOnly, policy => + policy + .RequireClaim(ClaimTypes.Role, Roles.User) + .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; } diff --git a/src/apps/identity/Genocs.Identities.Application/Services/JwtProvider.cs b/src/apps/identity/Genocs.Identities.Application/Services/JwtProvider.cs index ed755d6f..464e38ec 100644 --- a/src/apps/identity/Genocs.Identities.Application/Services/JwtProvider.cs +++ b/src/apps/identity/Genocs.Identities.Application/Services/JwtProvider.cs @@ -3,14 +3,9 @@ namespace Genocs.Identities.Application.Services; -public class JwtProvider : IJwtProvider +public class JwtProvider(IJwtHandler jwtHandler) : IJwtProvider { - private readonly IJwtHandler _jwtHandler; - - public JwtProvider(IJwtHandler jwtHandler) - { - _jwtHandler = jwtHandler; - } + private readonly IJwtHandler _jwtHandler = jwtHandler; public AuthDto Create( Guid userId, diff --git a/src/apps/identity/Genocs.Identities.WebApi/appsettings.json b/src/apps/identity/Genocs.Identities.WebApi/appsettings.json index 274a343b..065d9cc8 100644 --- a/src/apps/identity/Genocs.Identities.WebApi/appsettings.json +++ b/src/apps/identity/Genocs.Identities.WebApi/appsettings.json @@ -96,16 +96,20 @@ }, "jwt": { "enabled": true, - "certificate": { + "_certificate": { "location": "certs/localhost.pfx", "password": "test", "rawData": "" }, "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": false, "validateLifetime": false, + "requireExpirationTime": false, + "requireSignedTokens": false, + "validateIssuerSigningKey": false, "expiry": "01:00:00" }, "metrics": { 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) { From 5636ddc9479d0a80a9c9c87c9175dc33f38d55bb Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Fri, 13 Dec 2024 21:53:49 +0100 Subject: [PATCH 17/24] Refactor authentication and JWT handling components - Simplified `DisabledAuthenticationPolicyEvaluator` for better clarity. - Enhanced method signatures in `Extensions` for consistency and added authorization settings. - Improved performance in `DateExtensions` by using `in DateTime`. - Significant updates to `JwtHandler` with new token creation methods. - Updated `IJwtHandler` interface for clarity and consistency. - Reordered properties in `JsonWebTokenPayload` for better organization. - Added new package references in `HelloWorld.csproj` for JWT and OpenAPI. - Enhanced `Program.cs` with authentication, authorization, and CORS settings. - Refactored `DispatcherEndpointsBuilder` for improved readability. - Updated `JwtProvider` for consistency with `IJwtHandler`. - Introduced `TokenProvider` for encapsulating JWT token creation logic. --- .../DisabledAuthenticationPolicyEvaluator.cs | 11 +-- src/Genocs.Auth/Extensions.cs | 26 +++--- src/Genocs.Auth/Handlers/JwtHandler.cs | 84 ++++++++++++++----- src/Genocs.Auth/IJwtHandler.cs | 10 +-- src/Genocs.Auth/JsonWebTokenPayload.cs | 8 +- .../Genocs.Core.Demo.HelloWorld.csproj | 3 + src/Genocs.Core.Demo.HelloWorld/Program.cs | 45 ++++++++++ .../Genocs.Core.Demo.Infrastructure.csproj | 5 ++ .../SecurityAuthentication/TokenProvider.cs | 63 ++++++++++++++ .../Builders/DispatcherEndpointsBuilder.cs | 9 +- .../Services/JwtProvider.cs | 7 +- 11 files changed, 205 insertions(+), 66 deletions(-) create mode 100644 src/Genocs.Core.Demo.Infrastructure/SecurityAuthentication/TokenProvider.cs 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 5b4776d8..4f6eb817 100644 --- a/src/Genocs.Auth/Extensions.cs +++ b/src/Genocs.Auth/Extensions.cs @@ -20,10 +20,7 @@ public static class Extensions { private const string RegistryName = "auth"; - public static IGenocsBuilder AddJwt( - this IGenocsBuilder builder, - string sectionName = JwtOptions.Position, - Action? optionsFactory = null) + public static IGenocsBuilder AddJwt(this IGenocsBuilder builder, string sectionName = JwtOptions.Position, Action? optionsFactory = null) { if (string.IsNullOrWhiteSpace(sectionName)) { @@ -138,6 +135,9 @@ private static IGenocsBuilder AddJwt(this IGenocsBuilder builder, JwtOptions opt tokenValidationParameters.RoleClaimType = options.RoleClaimType; } + // Authorization settings + builder.Services.AddAuthorization(); + builder.Services .AddAuthentication(o => { @@ -176,12 +176,14 @@ private static IGenocsBuilder AddJwt(this IGenocsBuilder builder, JwtOptions opt /// 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()); @@ -212,16 +214,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)) { @@ -268,5 +268,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/Handlers/JwtHandler.cs b/src/Genocs.Auth/Handlers/JwtHandler.cs index e5c36877..6f263746 100644 --- a/src/Genocs.Auth/Handlers/JwtHandler.cs +++ b/src/Genocs.Auth/Handlers/JwtHandler.cs @@ -1,6 +1,7 @@ using System.Data; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; +using System.Text; using Genocs.Auth.Configurations; using Microsoft.IdentityModel.Tokens; @@ -8,9 +9,6 @@ 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, @@ -24,7 +22,6 @@ 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) { @@ -33,13 +30,14 @@ public JwtHandler(JwtOptions options, TokenValidationParameters tokenValidationP 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; } /// @@ -47,15 +45,11 @@ public JwtHandler(JwtOptions options, TokenValidationParameters tokenValidationP /// /// The UserId. /// The User Role. - /// - /// + /// The audience. + /// The list of claims. /// /// It is thrown when mandatory data is empty. - public JsonWebToken CreateToken( - string userId, - IEnumerable? roles = 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)) { @@ -100,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, Roles = roles, - Claims = claims ?? EmptyClaims + Claims = claims }; } /// /// Gets the token payload. /// - /// + /// The string describing the access token. /// The JWT Token payload. - public JsonWebTokenPayload? GetTokenPayload(string accessToken) + 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) { @@ -146,4 +137,51 @@ public JsonWebToken CreateToken( .ToDictionary(k => k.Key, v => v.Select(c => c.Value)) }; } + + /// + /// This method creates a token using the JwtSecurityTokenHandler class. + /// + /// + public string CreateToken() + { + var tokenHandler = new JwtSecurityTokenHandler(); + byte[] key = Encoding.ASCII.GetBytes(_options.IssuerSigningKey!); + + var tokenDescriptor = 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), SecurityAlgorithms.HmacSha256) + }; + + var token = tokenHandler.CreateToken(tokenDescriptor); + return tokenHandler.WriteToken(token); + } + + /// + /// This method creates a token using the JsonWebTokenHandler class. + /// + /// + public string CreateTokenWithJsonWebTokenHandler() + { + var tokenHandler = new Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler(); + byte[] key = Encoding.ASCII.GetBytes(_options.IssuerSigningKey!); + var tokenDescriptor = 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), SecurityAlgorithms.HmacSha256) + }; + + string token = tokenHandler.CreateToken(tokenDescriptor); + return token; + } } \ No newline at end of file diff --git a/src/Genocs.Auth/IJwtHandler.cs b/src/Genocs.Auth/IJwtHandler.cs index 5a6a2f08..8cfba038 100644 --- a/src/Genocs.Auth/IJwtHandler.cs +++ b/src/Genocs.Auth/IJwtHandler.cs @@ -13,16 +13,12 @@ public interface IJwtHandler /// The audience. /// The claims. /// The JsonWebToken just created. - JsonWebToken CreateToken( - string userId, - IEnumerable? roles = 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/JsonWebTokenPayload.cs b/src/Genocs.Auth/JsonWebTokenPayload.cs index 59013a65..1f3f86c1 100644 --- a/src/Genocs.Auth/JsonWebTokenPayload.cs +++ b/src/Genocs.Auth/JsonWebTokenPayload.cs @@ -16,12 +16,12 @@ public class JsonWebTokenPayload 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.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.csproj b/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.csproj index 3632b0dd..259f927c 100644 --- a/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.csproj +++ b/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.csproj @@ -4,13 +4,16 @@ net9.0 enable enable + genocs + + diff --git a/src/Genocs.Core.Demo.HelloWorld/Program.cs b/src/Genocs.Core.Demo.HelloWorld/Program.cs index d52a8481..4a7e2422 100644 --- a/src/Genocs.Core.Demo.HelloWorld/Program.cs +++ b/src/Genocs.Core.Demo.HelloWorld/Program.cs @@ -1,6 +1,11 @@ +using System.Text; using Genocs.Core.Builders; +using Genocs.Core.Demo.Infrastructure.SecurityAuthentication; using Genocs.GnxOpenTelemetry; using Genocs.Logging; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; StaticLogger.EnsureInitialized(); @@ -16,9 +21,44 @@ // 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(); + }); +}); + +// Authorization settings +builder.Services.AddAuthorization(); + +// Authentication settings +builder.Services.AddSingleton(); + +builder.Services + .AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => + { + builder.Configuration.Bind("JwtSettings", options); + options.RequireHttpsMetadata = false; + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = false, + ValidateLifetime = false, + ValidateIssuerSigningKey = false, + ValidIssuer = builder.Configuration["JwtSettings:Issuer"], + ValidAudience = builder.Configuration["JwtSettings:Audience"], + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JwtSettings:Key"])) + }; + }); + var app = builder.Build(); // Configure the HTTP request pipeline. @@ -29,8 +69,13 @@ app.UseHttpsRedirection(); +app.UseCors(); +app.UseRouting(); +app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); +app.MapGet("/", () => "ok").RequireAuthorization(); + app.Run(); 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.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs index b9ce0f29..94871dd4 100644 --- a/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs +++ b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs @@ -161,14 +161,15 @@ private static async Task BuildCommandContext( await dispatcher.SendAsync(command); } - if (context != null) + if (afterDispatch is not null) { - context.Response.StatusCode = 200; + await afterDispatch(command, context); } - if (afterDispatch is not null) + if (context != null) { - await afterDispatch(command, context); + context.Response.StatusCode = 200; } + } } \ 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 464e38ec..71a3bdd2 100644 --- a/src/apps/identity/Genocs.Identities.Application/Services/JwtProvider.cs +++ b/src/apps/identity/Genocs.Identities.Application/Services/JwtProvider.cs @@ -7,12 +7,7 @@ public class JwtProvider(IJwtHandler jwtHandler) : IJwtProvider { private readonly IJwtHandler _jwtHandler = jwtHandler; - public AuthDto Create( - Guid userId, - string username, - IEnumerable roles, - string? audience = null, - IDictionary>? claims = null) + public AuthDto Create(Guid userId, string username, IEnumerable roles, string? audience = null, IDictionary>? claims = null) { var jwt = _jwtHandler.CreateToken(userId.ToString("N"), roles, audience, claims); From 5894fd7232171ff202a9c2946b88602a149eec03 Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Sat, 14 Dec 2024 12:37:28 +0100 Subject: [PATCH 18/24] Enhance JWT authentication and code organization - Updated `AccessTokenValidatorMiddleware` documentation. - Modified `JwtOptions` to include default `Algorithm` and added XML documentation. - Improved `Extensions` class with new using directives and clearer service registration. - Refactored `JwtHandler` to centralize `SecurityTokenDescriptor` creation. - Simplified field initialization in `InMemoryAccessTokenService`. - Added project reference to `Genocs.Auth` in `Genocs.Core.Demo`. - Configured JWT authentication in `Program.cs` and added access token validation middleware. - Updated `appsettings.json` with new `simmetric_jwt` configuration section. - Improved formatting in `DispatcherEndpointsBuilder` class. --- .../AccessTokenValidatorMiddleware.cs | 2 +- src/Genocs.Auth/Configurations/JwtOptions.cs | 16 +++++++- src/Genocs.Auth/Extensions.cs | 25 ++++++------ src/Genocs.Auth/Handlers/JwtHandler.cs | 38 +++++++++---------- .../Services/InMemoryAccessTokenService.cs | 21 +++------- .../Genocs.Core.Demo.HelloWorld.csproj | 1 + src/Genocs.Core.Demo.HelloWorld/Program.cs | 13 ++++--- .../appsettings.json | 17 ++++++++- .../Builders/DispatcherEndpointsBuilder.cs | 9 ++--- 9 files changed, 78 insertions(+), 64 deletions(-) diff --git a/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs b/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs index e018bb43..3a867872 100644 --- a/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs +++ b/src/Genocs.Auth/AccessTokenValidatorMiddleware.cs @@ -22,7 +22,7 @@ public class AccessTokenValidatorMiddleware(IAccessTokenService accessTokenServi /// /// 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; diff --git a/src/Genocs.Auth/Configurations/JwtOptions.cs b/src/Genocs.Auth/Configurations/JwtOptions.cs index 1f53d103..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,6 +38,11 @@ public class JwtOptions public bool RequireHttpsMetadata { get; set; } public bool RequireExpirationTime { get; set; } = true; public bool RequireSignedTokens { get; set; } = true; + + /// + /// 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; } @@ -71,7 +83,7 @@ public class JwtOptions /// /// 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"; diff --git a/src/Genocs.Auth/Extensions.cs b/src/Genocs.Auth/Extensions.cs index 4f6eb817..19a7f2f6 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; @@ -38,16 +38,18 @@ private static IGenocsBuilder AddJwt(this IGenocsBuilder builder, JwtOptions opt 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, @@ -115,13 +117,8 @@ private static IGenocsBuilder AddJwt(this IGenocsBuilder builder, JwtOptions opt // 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."); } diff --git a/src/Genocs.Auth/Handlers/JwtHandler.cs b/src/Genocs.Auth/Handlers/JwtHandler.cs index 6f263746..ce517fc0 100644 --- a/src/Genocs.Auth/Handlers/JwtHandler.cs +++ b/src/Genocs.Auth/Handlers/JwtHandler.cs @@ -141,23 +141,12 @@ public JsonWebToken CreateToken(string userId, IEnumerable? roles = null /// /// This method creates a token using the JwtSecurityTokenHandler class. /// - /// + /// The JWT token string. public string CreateToken() { - var tokenHandler = new JwtSecurityTokenHandler(); - byte[] key = Encoding.ASCII.GetBytes(_options.IssuerSigningKey!); - - var tokenDescriptor = 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), SecurityAlgorithms.HmacSha256) - }; + SecurityTokenDescriptor tokenDescriptor = CreateSecurityTokenDescriptor(); + var tokenHandler = new JwtSecurityTokenHandler(); var token = tokenHandler.CreateToken(tokenDescriptor); return tokenHandler.WriteToken(token); } @@ -165,12 +154,24 @@ public string CreateToken() /// /// 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!); - var tokenDescriptor = new SecurityTokenDescriptor + return new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new Claim[] { @@ -178,10 +179,7 @@ public string CreateTokenWithJsonWebTokenHandler() new Claim(ClaimTypes.Role, "role") }), Expires = DateTime.UtcNow.AddMinutes(_options.ExpiryMinutes), - SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256) + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), _options.Algorithm) }; - - string token = tokenHandler.CreateToken(tokenDescriptor); - return token; } } \ No newline at end of file diff --git a/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs b/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs index 5ac36282..f1d51329 100644 --- a/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs +++ b/src/Genocs.Auth/Services/InMemoryAccessTokenService.cs @@ -9,21 +9,11 @@ namespace Genocs.Auth.Services; /// 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 : IAccessTokenService +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 bool IsCurrentActiveToken() => IsActive(GetCurrent()); @@ -44,8 +34,7 @@ public void Deactivate(string token) private string GetCurrent() { - var authorizationHeader = _httpContextAccessor - .HttpContext?.Request.Headers["authorization"]; + var authorizationHeader = _httpContextAccessor.HttpContext?.Request.Headers.Authorization; if (authorizationHeader is null) { diff --git a/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.csproj b/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.csproj index 259f927c..85556587 100644 --- a/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.csproj +++ b/src/Genocs.Core.Demo.HelloWorld/Genocs.Core.Demo.HelloWorld.csproj @@ -13,6 +13,7 @@ + diff --git a/src/Genocs.Core.Demo.HelloWorld/Program.cs b/src/Genocs.Core.Demo.HelloWorld/Program.cs index 4a7e2422..54064f6b 100644 --- a/src/Genocs.Core.Demo.HelloWorld/Program.cs +++ b/src/Genocs.Core.Demo.HelloWorld/Program.cs @@ -1,11 +1,7 @@ -using System.Text; +using Genocs.Auth; using Genocs.Core.Builders; -using Genocs.Core.Demo.Infrastructure.SecurityAuthentication; using Genocs.GnxOpenTelemetry; using Genocs.Logging; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.IdentityModel.Tokens; StaticLogger.EnsureInitialized(); @@ -16,6 +12,7 @@ builder.AddGenocs() .AddOpenTelemetry() + .AddJwt("simmetric_jwt") .Build(); // Add services to the container. @@ -35,6 +32,7 @@ }); }); +/* // Authorization settings builder.Services.AddAuthorization(); @@ -58,6 +56,7 @@ IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JwtSettings:Key"])) }; }); +*/ var app = builder.Build(); @@ -74,6 +73,10 @@ app.UseAuthentication(); app.UseAuthorization(); +// Used to validate the access token +// In RealTime +app.UseAccessTokenValidator(); + app.MapControllers(); app.MapGet("/", () => "ok").RequireAuthorization(); diff --git a/src/Genocs.Core.Demo.HelloWorld/appsettings.json b/src/Genocs.Core.Demo.HelloWorld/appsettings.json index 5792270e..2c245ee9 100644 --- a/src/Genocs.Core.Demo.HelloWorld/appsettings.json +++ b/src/Genocs.Core.Demo.HelloWorld/appsettings.json @@ -71,5 +71,20 @@ "Microsoft.AspNetCore": "Debug" } }, - "AllowedHosts": "*" + "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.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs index 94871dd4..b9ce0f29 100644 --- a/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs +++ b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs @@ -161,15 +161,14 @@ private static async Task BuildCommandContext( await dispatcher.SendAsync(command); } - if (afterDispatch is not null) - { - await afterDispatch(command, context); - } - if (context != null) { context.Response.StatusCode = 200; } + if (afterDispatch is not null) + { + await afterDispatch(command, context); + } } } \ No newline at end of file From 48b01f31ac67b5bb9e60dcaf9d119031707bf23c Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Sat, 14 Dec 2024 14:47:38 +0100 Subject: [PATCH 19/24] Add JWT authentication and enhance authorization policies This commit introduces JWT authentication features in `Extensions.cs` with detailed XML documentation and two new methods for adding JWT authentication. The `Program.cs` file is updated to include new authorization policies and utilize the JWT methods. Additionally, the configuration of authorization has been modified, new policies have been introduced, and some commented-out code has been removed. Middleware for access token validation has been added, along with updates to dispatcher endpoints to enforce authorization requirements. --- src/Genocs.Auth/Extensions.cs | 19 +++++++ src/Genocs.Core.Demo.HelloWorld/Program.cs | 51 ++++++++++--------- .../Builders/DispatcherEndpointsBuilder.cs | 17 +------ src/Genocs.WebApi/EndpointsBuilder.cs | 8 +-- .../Extensions.cs | 14 ++--- .../Genocs.Identities.WebApi/Program.cs | 22 +++++--- 6 files changed, 71 insertions(+), 60 deletions(-) diff --git a/src/Genocs.Auth/Extensions.cs b/src/Genocs.Auth/Extensions.cs index 19a7f2f6..6abd6e6b 100644 --- a/src/Genocs.Auth/Extensions.cs +++ b/src/Genocs.Auth/Extensions.cs @@ -20,6 +20,13 @@ public static class Extensions { private const string RegistryName = "auth"; + /// + /// 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)) @@ -31,6 +38,13 @@ public static IGenocsBuilder AddJwt(this IGenocsBuilder builder, string sectionN return builder.AddJwt(options, optionsFactory); } + /// + /// 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)) @@ -251,6 +265,11 @@ public static IGenocsBuilder AddPrivateKeyJwt(this IGenocsBuilder builder, strin 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(); } diff --git a/src/Genocs.Core.Demo.HelloWorld/Program.cs b/src/Genocs.Core.Demo.HelloWorld/Program.cs index 54064f6b..db0cb88d 100644 --- a/src/Genocs.Core.Demo.HelloWorld/Program.cs +++ b/src/Genocs.Core.Demo.HelloWorld/Program.cs @@ -1,7 +1,10 @@ +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(); @@ -16,7 +19,6 @@ .Build(); // Add services to the container. - builder.Services.AddControllers(); // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi @@ -32,31 +34,27 @@ }); }); -/* -// Authorization settings -builder.Services.AddAuthorization(); +// 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))); -// Authentication settings -builder.Services.AddSingleton(); -builder.Services - .AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => - { - builder.Configuration.Bind("JwtSettings", options); - options.RequireHttpsMetadata = false; - options.TokenValidationParameters = new TokenValidationParameters - { - ValidateIssuer = true, - ValidateAudience = false, - ValidateLifetime = false, - ValidateIssuerSigningKey = false, - ValidIssuer = builder.Configuration["JwtSettings:Issuer"], - ValidAudience = builder.Configuration["JwtSettings:Audience"], - IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JwtSettings:Key"])) - }; - }); -*/ var app = builder.Build(); @@ -79,6 +77,11 @@ 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.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs index b9ce0f29..abefbea4 100644 --- a/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs +++ b/src/Genocs.WebApi.CQRS/Builders/DispatcherEndpointsBuilder.cs @@ -10,27 +10,14 @@ public class DispatcherEndpointsBuilder(IEndpointsBuilder builder) : IDispatcher { private readonly IEndpointsBuilder _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) => diff --git a/src/Genocs.WebApi/EndpointsBuilder.cs b/src/Genocs.WebApi/EndpointsBuilder.cs index e1a7113e..a0d45643 100644 --- a/src/Genocs.WebApi/EndpointsBuilder.cs +++ b/src/Genocs.WebApi/EndpointsBuilder.cs @@ -139,15 +139,17 @@ private static void ApplyAuthRolesAndPolicies(IEndpointConventionBuilder builder } bool hasRoles = !string.IsNullOrWhiteSpace(roles); - var authorize = new AuthorizeAttribute(); if (hasRoles) { + var authorize = new AuthorizeAttribute(); authorize.Roles = roles; + builder.RequireAuthorization(authorize); + return; } - if (auth || hasRoles) + if (auth) { - builder.RequireAuthorization(authorize); + builder.RequireAuthorization(); return; } diff --git a/src/apps/identity/Genocs.Identities.Application/Extensions.cs b/src/apps/identity/Genocs.Identities.Application/Extensions.cs index 4b922eaf..9a6b3b9f 100644 --- a/src/apps/identity/Genocs.Identities.Application/Extensions.cs +++ b/src/apps/identity/Genocs.Identities.Application/Extensions.cs @@ -83,18 +83,10 @@ public static async Task AddCoreAsync(this IGenocsBuilder builde builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(OutboxCommandHandlerDecorator<>)); builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(OutboxEventHandlerDecorator<>)); - // Authorization: Split on a separate method - //builder.Services.AddAuthorization(options => - //{ - // options.AddPolicy("HasAdminRole", policy => policy.RequireRole("admin")); - // options.AddPolicy("HasUserRole", policy => policy.RequireRole("user")); - //}); - builder.Services.AddAuthorizationBuilder() - .AddPolicy(Policies.UserOnly, policy => - policy - .RequireClaim(ClaimTypes.Role, Roles.User) - .Build()); + .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() diff --git a/src/apps/identity/Genocs.Identities.WebApi/Program.cs b/src/apps/identity/Genocs.Identities.WebApi/Program.cs index 743d4ce1..ab77f212 100644 --- a/src/apps/identity/Genocs.Identities.WebApi/Program.cs +++ b/src/apps/identity/Genocs.Identities.WebApi/Program.cs @@ -1,3 +1,4 @@ +using System.Security.Claims; using Genocs.Auth; using Genocs.Core.Builders; using Genocs.Discovery.Consul; @@ -12,6 +13,7 @@ using Genocs.Secrets.Vault; using Genocs.WebApi; using Genocs.WebApi.CQRS; +using Microsoft.AspNetCore.Authorization.Infrastructure; using Serilog; StaticLogger.EnsureInitialized(); @@ -19,21 +21,29 @@ var builder = WebApplication.CreateBuilder(args); builder.Host - .UseLogging() - .UseVault(); + .UseLogging(); IGenocsBuilder gnxBuilder = await builder .AddGenocs() .AddJwt() .AddWebApi() - .AddConsul() - .AddFabio() .AddCoreAsync(); gnxBuilder.Build(); + +//// 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")))) +// ; + var app = builder.Build(); -app.MapDefaultEndpoints(); +// app.MapDefaultEndpoints(); + +app.UseCore(); app.UseDispatcherEndpoints(endpoints => endpoints .Post("sign-in", afterDispatch: (cmd, ctx) => @@ -63,8 +73,6 @@ .Put("users/{userId:guid}/lock", auth: true, policies: [Policies.AdminOnly]) .Put("users/{userId:guid}/unlock", auth: true, policies: [Policies.AdminOnly])); -app.UseCore(); - app.Run(); Log.CloseAndFlush(); \ No newline at end of file From 23aa7c6418365f49be9de771be028dae7758c02a Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Sat, 14 Dec 2024 19:08:38 +0100 Subject: [PATCH 20/24] Enhance GenocsBuilder and update Program.cs structure - Added `WebApplicationBuilder` property to `GenocsBuilder` with XML documentation. - Refactored constructors to initialize `_buildActions` as an empty array. - Introduced a new `Build` method in `GenocsBuilder` for service provider construction. - Cleaned up unused namespaces in `Program.cs`. - Commented out default authorization policies and replaced with `UseCore()` for core services setup. - Enhanced endpoint mapping to support health checks and added `UseDispatcherEndpoints` for sign-in actions. --- src/Genocs.Core/Builders/GenocsBuilder.cs | 19 +++++++++++++++++-- .../Genocs.Identities.WebApi/Program.cs | 19 ++++--------------- 2 files changed, 21 insertions(+), 17 deletions(-) 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/apps/identity/Genocs.Identities.WebApi/Program.cs b/src/apps/identity/Genocs.Identities.WebApi/Program.cs index ab77f212..29f53077 100644 --- a/src/apps/identity/Genocs.Identities.WebApi/Program.cs +++ b/src/apps/identity/Genocs.Identities.WebApi/Program.cs @@ -1,19 +1,14 @@ -using System.Security.Claims; using Genocs.Auth; using Genocs.Core.Builders; -using Genocs.Discovery.Consul; 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 Microsoft.AspNetCore.Authorization.Infrastructure; using Serilog; StaticLogger.EnsureInitialized(); @@ -31,20 +26,14 @@ gnxBuilder.Build(); -//// 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")))) -// ; - var app = builder.Build(); -// app.MapDefaultEndpoints(); - +// 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) => { From 104088196666c31668f93a52fb89d6d2a70b7943 Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Sat, 14 Dec 2024 20:11:08 +0100 Subject: [PATCH 21/24] Update configuration settings and service details - Changed logging level from "warning" to "debug". - Updated Jaeger and OpenTelemetry UDP endpoints. - Modified JWT certificate location and expiry settings. - Changed ELK index formats for API Gateway and Products. - Updated Swagger titles and versions for API Gateway and Orders services. - Added request retries parameter and disabled request masking. - Expanded OpenTelemetry configuration to include console and Azure settings. - Renamed MongoDB database from "orders-service" to "orders". --- .../Genocs.APIGateway/appsettings.json | 22 +++--- .../Genocs.Identities.WebApi/appsettings.json | 75 +++++++++++++++++-- .../Commands/Handlers/CreateOrderHandler.cs | 9 +-- .../Genocs.Orders.WebApi/appsettings.json | 62 ++++++++++++--- .../Genocs.Products.WebApi/appsettings.json | 58 ++++++++++++-- 5 files changed, 185 insertions(+), 41 deletions(-) diff --git a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json index 9184a905..85de1893 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json +++ b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json @@ -37,7 +37,7 @@ "correlationIdHeader": "x-correlation-id" }, "logger": { - "level": "warning", + "level": "debug", "excludePaths": [ "/", "/healthz", @@ -84,7 +84,7 @@ "jaeger": { "enabled": true, "serviceName": "api-gateway", - "udpHost": "localhost", + "udpHost": "http://localhost:4317", "udpPort": 6831, "maxPacketSize": 65000, "sampler": "const", @@ -99,7 +99,7 @@ "enabled": true, "exporter": { "enabled": true, - "otlpEndpoint": "http://localhost:4318", + "otlpEndpoint": "http://localhost:4317", "protocol": "Grpc", "processorType": "Batch", "maxQueueSize": 2048, @@ -124,17 +124,21 @@ "jwt": { "enabled": true, "allowAnonymousEndpoints": [], - "certificate": { - "location": "certs/localhost.cer", + "_certificate": { + "location": "certs/localhost.pfx", "password": "test", "rawData": "" }, - "expiryMinutes": 120, + "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": false, "validateLifetime": false, + "requireExpirationTime": false, + "requireSignedTokens": false, + "validateIssuerSigningKey": false, "expiry": "01:00:00" }, "metrics": { @@ -221,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" @@ -239,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, diff --git a/src/apps/identity/Genocs.Identities.WebApi/appsettings.json b/src/apps/identity/Genocs.Identities.WebApi/appsettings.json index 065d9cc8..a20c6d67 100644 --- a/src/apps/identity/Genocs.Identities.WebApi/appsettings.json +++ b/src/apps/identity/Genocs.Identities.WebApi/appsettings.json @@ -16,7 +16,8 @@ "pingEnabled": false, "pingEndpoint": "healthz", "pingInterval": 3, - "removeAfterInterval": 3 + "removeAfterInterval": 3, + "requestRetries": 3 }, "fabio": { "enabled": false, @@ -25,7 +26,7 @@ "requestRetries": 3 }, "httpClient": { - "type": "fabio", + "type": "", "retries": 3, "services": { }, @@ -83,7 +84,7 @@ "jaeger": { "enabled": true, "serviceName": "identity", - "udpHost": "localhost", + "udpHost": "http://localhost:4317", "udpPort": 6831, "maxPacketSize": 65000, "sampler": "const", @@ -94,13 +95,41 @@ "/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.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", @@ -193,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": { @@ -236,7 +291,7 @@ "mongo": { "type": "database", "roleName": "identity-service", - "enabled": true, + "enabled": false, "autoRenewal": true, "templates": { "connectionString": "mongodb://{{username}}:{{password}}@localhost:27017" @@ -246,5 +301,11 @@ }, "webApi": { "bindRequestFromRoute": true + }, + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + } } } \ No newline at end of file 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/appsettings.json b/src/apps/orders/Genocs.Orders.WebApi/appsettings.json index c60b1f0c..9547b485 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" }, "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/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 From 2654fe6b756e97034960ba73c39b57429678295f Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Sat, 14 Dec 2024 20:38:29 +0100 Subject: [PATCH 22/24] Enable services and enhance configuration settings - Updated `appsettings.json` to enable `consul` and `fabio` services for both `api-gateway` and `orders-service`. - Increased random exception threshold in `GetProductHandler` from 20 to 90. - Set `httpClient` type to `fabio` and disabled request masking. - Added `openTelemetry` section for telemetry features with console and Azure exporter settings. - Changed JWT certificate from `.cer` to `.pfx` and increased `expiryMinutes` from 30 to 60, with several validation properties set to `false`. - Updated `swagger` version from `v01` to `v1`. - Introduced new `webApi` section to bind requests from the route. - Added `Logging` section to specify application log levels. --- .../Genocs.APIGateway/appsettings.json | 4 +- .../Genocs.Orders.WebApi/appsettings.json | 6 +- .../Queries/Handlers/GetProductHandler.cs | 2 +- .../Genocs.SignalR.WebApi/appsettings.json | 62 ++++++++++++++++--- 4 files changed, 58 insertions(+), 16 deletions(-) diff --git a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json index 85de1893..7a83b97f 100644 --- a/src/apps/api-gateway/Genocs.APIGateway/appsettings.json +++ b/src/apps/api-gateway/Genocs.APIGateway/appsettings.json @@ -8,7 +8,7 @@ "displayVersion": true }, "consul": { - "enabled": false, + "enabled": true, "url": "http://localhost:8500", "service": "api-gateway", "address": "docker.for.mac.localhost", @@ -20,7 +20,7 @@ "requestRetries": 3 }, "fabio": { - "enabled": false, + "enabled": true, "url": "http://localhost:9999", "service": "api-gateway", "requestRetries": 3 diff --git a/src/apps/orders/Genocs.Orders.WebApi/appsettings.json b/src/apps/orders/Genocs.Orders.WebApi/appsettings.json index 9547b485..024b63f3 100644 --- a/src/apps/orders/Genocs.Orders.WebApi/appsettings.json +++ b/src/apps/orders/Genocs.Orders.WebApi/appsettings.json @@ -8,7 +8,7 @@ "displayVersion": true }, "consul": { - "enabled": false, + "enabled": true, "url": "http://localhost:8500", "service": "orders-service", "address": "docker.for.mac.localhost", @@ -20,7 +20,7 @@ "requestRetries": 3 }, "fabio": { - "enabled": false, + "enabled": true, "url": "http://localhost:9999", "service": "orders-service", "requestRetries": 3 @@ -29,7 +29,7 @@ "type": "fabio", "retries": 3, "services": { - "products": "https://localhost:5520" + "products": "products-service" }, "requestMasking": { "enabled": false, 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 8b53b04e..b75a3c9c 100644 --- a/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/GetProductHandler.cs +++ b/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/GetProductHandler.cs @@ -19,7 +19,7 @@ public GetProductHandler(IMongoDbBaseRepository repository { int currentValue = _random.Next(0, 100); - if (currentValue < 20) + if (currentValue < 90) { throw new Exception("Random exception"); } 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 From f24c46508d67da55fab3cc96c3ebb295977e9222 Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Sat, 14 Dec 2024 20:49:28 +0100 Subject: [PATCH 23/24] Update package versions and modify exception logic - Updated `MassTransit.RabbitMQ` from `8.3.2` to `8.3.3` in both `Genocs.Core.Demo.WebApi.csproj` and `Genocs.Core.Demo.Worker.csproj`. - Updated `MongoDB.Driver` from `3.0.0` to `3.1.0` in `Genocs.Persistence.MongoDb.csproj`. - Changed exception condition in `GetProductHandler.cs` from `if (currentValue < 90)` to `if (currentValue < 5)`, reducing the likelihood of exceptions. --- src/Genocs.Core.Demo.WebApi/Genocs.Core.Demo.WebApi.csproj | 2 +- src/Genocs.Core.Demo.Worker/Genocs.Core.Demo.Worker.csproj | 2 +- .../Genocs.Persistence.MongoDb.csproj | 2 +- .../Queries/Handlers/GetProductHandler.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) 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 043cf442..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,7 +32,7 @@ - + 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 482854e1..dcc3acc6 100644 --- a/src/Genocs.Core.Demo.Worker/Genocs.Core.Demo.Worker.csproj +++ b/src/Genocs.Core.Demo.Worker/Genocs.Core.Demo.Worker.csproj @@ -21,7 +21,7 @@ - + diff --git a/src/Genocs.Persistence.MongoDb/Genocs.Persistence.MongoDb.csproj b/src/Genocs.Persistence.MongoDb/Genocs.Persistence.MongoDb.csproj index bacc5ac9..80159a21 100644 --- a/src/Genocs.Persistence.MongoDb/Genocs.Persistence.MongoDb.csproj +++ b/src/Genocs.Persistence.MongoDb/Genocs.Persistence.MongoDb.csproj @@ -26,7 +26,7 @@ - + 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 b75a3c9c..4053be04 100644 --- a/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/GetProductHandler.cs +++ b/src/apps/products/Genocs.Products.WebApi/Queries/Handlers/GetProductHandler.cs @@ -19,7 +19,7 @@ public GetProductHandler(IMongoDbBaseRepository repository { int currentValue = _random.Next(0, 100); - if (currentValue < 90) + if (currentValue < 5) { throw new Exception("Random exception"); } From 5badccafa17e033f6a9208fe9818b88575ec0ba4 Mon Sep 17 00:00:00 2001 From: Nocco Giovanni Emanuele Date: Sat, 14 Dec 2024 21:59:42 +0100 Subject: [PATCH 24/24] Update CHANGELOG for v7.2.0 release with recent changes and improvements --- CHANGELOG.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) 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