From 1440aa526447c217ca7022a8dc225760d20ccbf7 Mon Sep 17 00:00:00 2001 From: nikiforovall Date: Thu, 2 May 2024 22:45:31 +0300 Subject: [PATCH 1/6] feat: Add Sdk client vNext --- .../configuration-authentication.md | 2 +- docs/migration.md | 11 + samples/AuthGettingStarted/Program.cs | 2 +- .../Controllers/KeycloakAdminController.cs | 2 +- .../ServiceCollectionExtensions.OpenApi.cs | 9 +- src/Directory.Packages.props | 6 +- ...eycloak.AuthServices.Authentication.csproj | 1 + .../ServiceCollectionExtensions.cs | 10 +- ...akWebApiAuthenticationBuilderExtensions.cs | 6 +- ...ycloakWebApiServiceCollectionExtensions.cs | 5 +- ...akWebAppAuthenticationBuilderExtensions.cs | 2 +- .../ServiceCollectionExtensions.cs | 2 +- .../ConfigurationExtensions.cs | 60 + .../KeycloakInstallationOptions.cs | 20 +- .../Admin/IKeycloakGroupClient.cs | 87 +- .../Admin/IKeycloakProtectedResourceClient.cs | 97 +- .../Admin/IKeycloakRealmClient.cs | 28 +- .../Admin/IKeycloakUserClient.cs | 190 +- .../Admin/KeycloakClient.cs | 33 + .../Admin/Models/Contracts.cs | 8144 +++++++++++++++++ .../Admin/Models/Group/Group.cs | 18 - .../Admin/Models/Resources/Resource.cs | 46 - .../Models/Resources/ResourceResponse.cs | 38 - .../Admin/Models/Users/Credential.cs | 18 - .../Admin/Models/Users/FederatedIdentity.cs | 12 - .../Admin/Models/Users/User.cs | 33 - .../Admin/Models/Users/UserConsent.cs | 13 - .../Groups/GetGroupRequestParameters.cs | 8 - .../Users/GetUsersRequestParameters.cs | 15 - .../Admin/ServiceCollectionExtensions.cs | 87 - .../HttpResponseExtensions.cs | 30 + .../Keycloak.AuthServices.Sdk.csproj | 6 +- .../{Admin => }/KeycloakAdminClientOptions.cs | 4 +- src/Keycloak.AuthServices.Sdk/README.md | 7 + .../ServiceCollectionExtensions.cs | 69 + tests/Directory.Build.props | 1 + .../AddKeycloakWebApiTests.cs | 5 +- .../Admin/KeycloakRealmClientTests.cs | 50 + .../AuthorizationServerPolicyTests.cs | 11 +- .../AddKeycloakWebApiAuthenticationTests.cs | 18 +- .../AddKeycloakWebApiTests.cs | 10 +- ...cloak.AuthServices.IntegrationTests.csproj | 2 + .../Playground.cs | 2 +- .../PolicyTests.cs | 10 +- .../Utils.cs | 46 +- .../appsettings.Master.json | 12 + .../{Admin => }/KeycloakRealmClientTests.cs | 11 +- .../{Admin => }/KeycloakUserClientTests.cs | 135 +- 48 files changed, 8794 insertions(+), 640 deletions(-) create mode 100644 src/Keycloak.AuthServices.Common/ConfigurationExtensions.cs create mode 100644 src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs create mode 100644 src/Keycloak.AuthServices.Sdk/Admin/Models/Contracts.cs delete mode 100644 src/Keycloak.AuthServices.Sdk/Admin/Models/Group/Group.cs delete mode 100644 src/Keycloak.AuthServices.Sdk/Admin/Models/Resources/Resource.cs delete mode 100644 src/Keycloak.AuthServices.Sdk/Admin/Models/Resources/ResourceResponse.cs delete mode 100644 src/Keycloak.AuthServices.Sdk/Admin/Models/Users/Credential.cs delete mode 100644 src/Keycloak.AuthServices.Sdk/Admin/Models/Users/FederatedIdentity.cs delete mode 100644 src/Keycloak.AuthServices.Sdk/Admin/Models/Users/User.cs delete mode 100644 src/Keycloak.AuthServices.Sdk/Admin/Models/Users/UserConsent.cs delete mode 100644 src/Keycloak.AuthServices.Sdk/Admin/ServiceCollectionExtensions.cs create mode 100644 src/Keycloak.AuthServices.Sdk/HttpResponseExtensions.cs rename src/Keycloak.AuthServices.Sdk/{Admin => }/KeycloakAdminClientOptions.cs (68%) create mode 100644 src/Keycloak.AuthServices.Sdk/README.md create mode 100644 src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs create mode 100644 tests/Keycloak.AuthServices.IntegrationTests/Admin/KeycloakRealmClientTests.cs create mode 100644 tests/Keycloak.AuthServices.IntegrationTests/appsettings.Master.json rename tests/Keycloak.AuthServices.Sdk.Tests/{Admin => }/KeycloakRealmClientTests.cs (84%) rename tests/Keycloak.AuthServices.Sdk.Tests/{Admin => }/KeycloakUserClientTests.cs (73%) diff --git a/docs/configuration/configuration-authentication.md b/docs/configuration/configuration-authentication.md index b10ff363..3c775c96 100644 --- a/docs/configuration/configuration-authentication.md +++ b/docs/configuration/configuration-authentication.md @@ -53,7 +53,7 @@ Not everything you want to do can be configured with `KeycloakAuthenticationOpti Here is a trick to bind options from configuration an override directly in the same code: -<<< @/../tests/Keycloak.AuthServices.IntegrationTests/ConfigurationTests/AddKeycloakWebApiAuthenticationTests.cs#AddKeycloakWebApiAuthentication_FromConfigurationWithInlineOverrides2{3-5} +<<< @/../tests/Keycloak.AuthServices.IntegrationTests/ConfigurationTests/AddKeycloakWebApiAuthenticationTests.cs#AddKeycloakWebApiAuthentication_FromConfigurationWithInlineOverrides2{3} Typically, ASP.NET Core expects to find these (default) options under the `Authentication:Schemes:{SchemeName}`. See [Configuring Authentication **Strategies**](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/security?view=aspnetcore-8.0#configuring-authentication-strategy) for more details. Here is how to configure [JwtBearerOptions](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.jwtbeareroptions): diff --git a/docs/migration.md b/docs/migration.md index f8e4bf64..f82ab8e2 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -42,6 +42,17 @@ public void TestKebabCaseNotation() authenticationOptions.Should().BeEquivalentTo(Expected); } +[Fact] +public void TestKebabCaseNotationWithExtensionMethod() +{ + var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); + + var authenticationOptions = configuration + .GetKeycloakOptions("Keycloak1"); + + authenticationOptions.Should().BeEquivalentTo(Expected); +} + [Fact] public void TestPascalCaseNotation() { diff --git a/samples/AuthGettingStarted/Program.cs b/samples/AuthGettingStarted/Program.cs index b60930a5..ec23dd7a 100644 --- a/samples/AuthGettingStarted/Program.cs +++ b/samples/AuthGettingStarted/Program.cs @@ -2,7 +2,7 @@ using Api; using Keycloak.AuthServices.Authentication; using Keycloak.AuthServices.Authorization; -using Keycloak.AuthServices.Sdk.Admin; +using Keycloak.AuthServices.Sdk; var builder = WebApplication.CreateBuilder(args); diff --git a/samples/AuthorizationAndCleanArchitecture/Controllers/KeycloakAdminController.cs b/samples/AuthorizationAndCleanArchitecture/Controllers/KeycloakAdminController.cs index 50483b64..ed76d51b 100644 --- a/samples/AuthorizationAndCleanArchitecture/Controllers/KeycloakAdminController.cs +++ b/samples/AuthorizationAndCleanArchitecture/Controllers/KeycloakAdminController.cs @@ -22,7 +22,7 @@ public KeycloakAdminController( [HttpGet("realms")] public async Task GetRealms() { - return this.Ok(await this.keycloakRealmClient.GetRealm(DefaultRealm)); + return this.Ok(await this.keycloakRealmClient.GetRealmAsync(DefaultRealm)); } [HttpGet("resources")] diff --git a/samples/AuthorizationGettingStarted/ServiceCollectionExtensions.OpenApi.cs b/samples/AuthorizationGettingStarted/ServiceCollectionExtensions.OpenApi.cs index 6ef2de96..925253aa 100644 --- a/samples/AuthorizationGettingStarted/ServiceCollectionExtensions.OpenApi.cs +++ b/samples/AuthorizationGettingStarted/ServiceCollectionExtensions.OpenApi.cs @@ -3,7 +3,6 @@ namespace Microsoft.Extensions.DependencyInjection; using Keycloak.AuthServices.Authentication; using Keycloak.AuthServices.Common; using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; public static partial class ServiceCollectionExtensions @@ -13,9 +12,7 @@ public static IServiceCollection AddApplicationSwagger( IConfiguration configuration ) { - var options = configuration - .GetSection(KeycloakAuthenticationOptions.Section) - .Get(KeycloakFormatBinder.Instance); + var options = configuration.GetKeycloakOptions(); services.AddEndpointsApiExplorer(); services.AddSwaggerGen(c => @@ -58,9 +55,7 @@ IConfiguration configuration { KeycloakAuthenticationOptions options = new(); - configuration - .GetSection(KeycloakAuthenticationOptions.Section) - .Bind(options, KeycloakFormatBinder.Instance); + configuration.BindKeycloakOptions(options); app.UseSwagger(); app.UseSwaggerUI(s => s.OAuthClientId(options.Resource)); diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 76b6f17c..8a502411 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -5,15 +5,11 @@ + - - - - - diff --git a/src/Keycloak.AuthServices.Authentication/Keycloak.AuthServices.Authentication.csproj b/src/Keycloak.AuthServices.Authentication/Keycloak.AuthServices.Authentication.csproj index 24a55d8b..3c4e3e89 100644 --- a/src/Keycloak.AuthServices.Authentication/Keycloak.AuthServices.Authentication.csproj +++ b/src/Keycloak.AuthServices.Authentication/Keycloak.AuthServices.Authentication.csproj @@ -16,6 +16,7 @@ + diff --git a/src/Keycloak.AuthServices.Authentication/ServiceCollectionExtensions.cs b/src/Keycloak.AuthServices.Authentication/ServiceCollectionExtensions.cs index 470a4c70..4f1d5eca 100644 --- a/src/Keycloak.AuthServices.Authentication/ServiceCollectionExtensions.cs +++ b/src/Keycloak.AuthServices.Authentication/ServiceCollectionExtensions.cs @@ -68,9 +68,8 @@ public static AuthenticationBuilder AddKeycloakAuthentication( Action? configureOptions = default ) { - var authenticationOptions = configuration - .GetSection(KeycloakAuthenticationOptions.Section) - .Get(KeycloakFormatBinder.Instance)!; + var authenticationOptions = + configuration.GetKeycloakOptions()!; return services.AddKeycloakAuthentication(authenticationOptions, configureOptions); } @@ -91,9 +90,8 @@ public static AuthenticationBuilder AddKeycloakAuthentication( Action? configureOptions = default ) { - var authenticationOptions = configuration - .GetSection(keycloakClientSectionName) - .Get(KeycloakFormatBinder.Instance)!; + var authenticationOptions = + configuration.GetKeycloakOptions()!; return services.AddKeycloakAuthentication(authenticationOptions, configureOptions); } diff --git a/src/Keycloak.AuthServices.Authentication/WebApiExtensions/KeycloakWebApiAuthenticationBuilderExtensions.cs b/src/Keycloak.AuthServices.Authentication/WebApiExtensions/KeycloakWebApiAuthenticationBuilderExtensions.cs index 490fb14a..adf34259 100644 --- a/src/Keycloak.AuthServices.Authentication/WebApiExtensions/KeycloakWebApiAuthenticationBuilderExtensions.cs +++ b/src/Keycloak.AuthServices.Authentication/WebApiExtensions/KeycloakWebApiAuthenticationBuilderExtensions.cs @@ -97,8 +97,7 @@ public static KeycloakWebApiAuthenticationBuilder AddKeycloakWebApi( services: builder.Services, jwtBearerAuthenticationScheme: jwtBearerScheme, configureJwtBearerOptions: configureJwtBearerOptions, - configureKeycloakOptions: options => - configurationSection.Bind(options, KeycloakFormatBinder.Instance), + configureKeycloakOptions: options => configurationSection.BindKeycloakOptions(options), configurationSection: configurationSection ); } @@ -127,8 +126,7 @@ public static KeycloakWebApiAuthenticationBuilder AddKeycloakWebApi( services: builder.Services, jwtBearerAuthenticationScheme: jwtBearerScheme, configureJwtBearerOptions: configureJwtBearerOptions, - configureKeycloakOptions: options => - configurationSection.Bind(options, KeycloakFormatBinder.Instance), + configureKeycloakOptions: options => configurationSection.BindKeycloakOptions(options), configurationSection: configurationSection ); } diff --git a/src/Keycloak.AuthServices.Authentication/WebApiExtensions/KeycloakWebApiServiceCollectionExtensions.cs b/src/Keycloak.AuthServices.Authentication/WebApiExtensions/KeycloakWebApiServiceCollectionExtensions.cs index 4739aac8..13cc92d5 100644 --- a/src/Keycloak.AuthServices.Authentication/WebApiExtensions/KeycloakWebApiServiceCollectionExtensions.cs +++ b/src/Keycloak.AuthServices.Authentication/WebApiExtensions/KeycloakWebApiServiceCollectionExtensions.cs @@ -71,10 +71,7 @@ public static KeycloakWebApiAuthenticationBuilder AddKeycloakWebApiAuthenticatio var builder = services.AddAuthentication(jwtBearerScheme); return builder.AddKeycloakWebApi( - options => - configuration - .GetSection(configSectionName) - .Bind(options, KeycloakFormatBinder.Instance), + options => configuration.BindKeycloakOptions(options, configSectionName), configureJwtBearerOptions, jwtBearerScheme ); diff --git a/src/Keycloak.AuthServices.Authentication/WebAppExtensions/KeycloakWebAppAuthenticationBuilderExtensions.cs b/src/Keycloak.AuthServices.Authentication/WebAppExtensions/KeycloakWebAppAuthenticationBuilderExtensions.cs index 11fb8188..dee1aad6 100644 --- a/src/Keycloak.AuthServices.Authentication/WebAppExtensions/KeycloakWebAppAuthenticationBuilderExtensions.cs +++ b/src/Keycloak.AuthServices.Authentication/WebAppExtensions/KeycloakWebAppAuthenticationBuilderExtensions.cs @@ -81,7 +81,7 @@ public static KeycloakWebAppAuthenticationBuilder AddKeycloakWebApp( return builder.AddKeycloakWebAppWithConfiguration( configureKeycloakOptions: options => - configurationSection.Bind(options, KeycloakFormatBinder.Instance), + configurationSection.BindKeycloakOptions(options), configureCookieAuthenticationOptions: configureCookieAuthenticationOptions, configureOpenIdConnectOptions: configureOpenIdConnectOptions, openIdConnectScheme: openIdConnectScheme, diff --git a/src/Keycloak.AuthServices.Authorization/ServiceCollectionExtensions.cs b/src/Keycloak.AuthServices.Authorization/ServiceCollectionExtensions.cs index de9dbf83..72c43206 100644 --- a/src/Keycloak.AuthServices.Authorization/ServiceCollectionExtensions.cs +++ b/src/Keycloak.AuthServices.Authorization/ServiceCollectionExtensions.cs @@ -106,7 +106,7 @@ public static IHttpClientBuilder AddAuthorizationServer( Action? configureClient = default ) => services.AddAuthorizationServer( - options => configurationSection.Bind(options, KeycloakFormatBinder.Instance), + options => configurationSection.BindKeycloakOptions(options), configureClient ); diff --git a/src/Keycloak.AuthServices.Common/ConfigurationExtensions.cs b/src/Keycloak.AuthServices.Common/ConfigurationExtensions.cs new file mode 100644 index 00000000..d193cbb8 --- /dev/null +++ b/src/Keycloak.AuthServices.Common/ConfigurationExtensions.cs @@ -0,0 +1,60 @@ +namespace Keycloak.AuthServices.Common; + +using Microsoft.Extensions.Configuration; + +/// +/// +/// +public static class ConfigurationExtensions +{ + /// + /// Retrieves the Keycloak options from the configuration section with the specified name. + /// + /// The type of the Keycloak options. + /// The configuration instance. + /// The name of the configuration section. Default is ConfigurationConstants.ConfigurationPrefix. + /// The Keycloak options instance. + public static T? GetKeycloakOptions( + this IConfiguration configuration, + string configSectionName = ConfigurationConstants.ConfigurationPrefix + ) + where T : KeycloakInstallationOptions => + configuration.GetSection(configSectionName).Get(KeycloakFormatBinder.Instance); + + /// + /// Retrieves the Keycloak options from the configuration section + /// + /// The type of the Keycloak options. + /// The Keycloak options instance. + public static T? GetKeycloakOptions(this IConfigurationSection configurationSection) + where T : KeycloakInstallationOptions => + configurationSection.Get(KeycloakFormatBinder.Instance); + + /// + /// Binds the Keycloak options from the configuration section with the specified name to the provided options instance. + /// + /// The type of the Keycloak options. + /// The configuration instance. + /// The options instance to bind the configuration to. + /// The name of the configuration section. Default is ConfigurationConstants.ConfigurationPrefix. + public static void BindKeycloakOptions( + this IConfiguration configuration, + T options, + string configSectionName = ConfigurationConstants.ConfigurationPrefix + ) + where T : KeycloakInstallationOptions => + configuration.GetSection(configSectionName).Bind(options, KeycloakFormatBinder.Instance); + + /// + /// Binds the Keycloak options from the configuration section to the provided options instance. + /// + /// The type of the Keycloak options. + /// + /// The options instance to bind the configuration to. + public static void BindKeycloakOptions( + this IConfigurationSection configurationSection, + T options + ) + where T : KeycloakInstallationOptions => + configurationSection.Bind(options, KeycloakFormatBinder.Instance); +} diff --git a/src/Keycloak.AuthServices.Common/KeycloakInstallationOptions.cs b/src/Keycloak.AuthServices.Common/KeycloakInstallationOptions.cs index 9697d9dc..6b8725a7 100644 --- a/src/Keycloak.AuthServices.Common/KeycloakInstallationOptions.cs +++ b/src/Keycloak.AuthServices.Common/KeycloakInstallationOptions.cs @@ -103,7 +103,25 @@ public string KeycloakUrlRealm return default!; } - return $"{NormalizeUrl(this.AuthServerUrl)}/realms/{this.Realm}/"; + return $"{NormalizeUrl(this.AuthServerUrl?.TrimEnd('/'))}/realms/{this.Realm}/"; + } + } + + /// + /// Token endpoint URL including Realm + /// + public string KeycloakTokenEndpoint + { + get + { + if (string.IsNullOrWhiteSpace(this.KeycloakUrlRealm)) + { + return default!; + } + + return NormalizeUrl( + $"{this.KeycloakUrlRealm?.TrimEnd('/')}/{KeycloakConstants.TokenEndpointPath}" + )!; } } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs index 0b2cd0c0..8bbb16f4 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs @@ -1,63 +1,46 @@ namespace Keycloak.AuthServices.Sdk.Admin; -using System.Threading.Tasks; -using Constants; -using Models; -using Refit; -using Requests.Groups; - /// /// Group management /// -[Headers("Accept: application/json")] public interface IKeycloakGroupClient { - /// - /// Get a stream of groups on the realm. - /// - /// Realm name. - /// Optional query parameters. - /// A stream of groups, filtered according to query parameters. - [Get(KeycloakClientApiConstants.GetGroups)] - Task> GetGroups( - string realm, - [Query] GetGroupRequestParameters? parameters = default - ); + // /// + // /// Get a stream of groups on the realm. + // /// + // /// Realm name. + // /// Optional query parameters. + // /// A stream of groups, filtered according to query parameters. + // Task> GetGroups( + // string realm, + // GetGroupRequestParameters? parameters = default + // ); - /// - /// Get representation of a group. - /// - /// Realm name. - /// group ID. - /// The group representation. - [Get(KeycloakClientApiConstants.GetGroup)] - Task GetGroup(string realm, [AliasAs("id")] string groupId); + // /// + // /// Get representation of a group. + // /// + // /// Realm name. + // /// group ID. + // /// The group representation. + // Task GetGroup(string realm, string groupId); - /// - /// Create a new group. - /// - /// - /// Group name must be unique. - /// - /// Realm name (not ID). - /// Group representation. - /// - [Post(KeycloakClientApiConstants.CreateGroup)] - [Headers("Content-Type: application/json")] - Task CreateGroup(string realm, [Body] Group group); + // /// + // /// Create a new group. + // /// + // /// + // /// Group name must be unique. + // /// + // /// Realm name (not ID). + // /// Group representation. + // /// + // Task CreateGroup(string realm, Group group); - /// - /// Update the group. - /// - /// Realm name (not ID). - /// group ID. - /// Group representation. - /// - [Put(KeycloakClientApiConstants.UpdateGroup)] - [Headers("Content-Type: application/json")] - Task UpdateGroup( - string realm, - [AliasAs("id")] string groupId, - [Body] Group group - ); + // /// + // /// Update the group. + // /// + // /// Realm name (not ID). + // /// group ID. + // /// Group representation. + // /// + // Task UpdateGroup(string realm, string groupId, Group group); } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakProtectedResourceClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakProtectedResourceClient.cs index 5b81010a..098569e7 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakProtectedResourceClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakProtectedResourceClient.cs @@ -1,68 +1,53 @@ namespace Keycloak.AuthServices.Sdk.Admin; -using Constants; -using Models; -using Refit; - /// /// Access to protected resource API /// public interface IKeycloakProtectedResourceClient { - /// - /// Searches for resource - /// - /// - /// - [Get(KeycloakClientApiConstants.GetResources)] - [Headers("Accept: application/json")] - Task> GetResources(string realm); + // /// + // /// Searches for resource + // /// + // /// + // /// + // Task> GetResources(string realm); - /// - /// Gets resource by Id - /// - /// - /// - /// - [Get(KeycloakClientApiConstants.GetResource)] - [Headers("Accept: application/json")] - Task GetResource(string realm, string id); + // /// + // /// Gets resource by Id + // /// + // /// + // /// + // /// + // Task GetResource(string realm, string id); - /// - /// Gets resource by Name - /// - /// - /// https://github.com/keycloak/keycloak-documentation/blob/main/authorization_services/topics/service-protection-resources-api-papi.adoc#querying-resources - /// - /// - /// - /// - [Get(KeycloakClientApiConstants.GetResourceByExactName)] - [Headers("Accept: application/json")] - Task SearchResourcesByName(string realm, [Query] string name); + // /// + // /// Gets resource by Name + // /// + // /// + // /// https://github.com/keycloak/keycloak-documentation/blob/main/authorization_services/topics/service-protection-resources-api-papi.adoc#querying-resources + // /// + // /// + // /// + // /// + // Task SearchResourcesByName(string realm, string name); - /// - /// Creates resource - /// - /// - /// - /// - [Post(KeycloakClientApiConstants.CreateResource)] - [Headers("Accept: application/json", "Content-Type: application/json")] - Task CreateResource(string realm, [Body] Resource resource); + // /// + // /// Creates resource + // /// + // /// + // /// + // /// + // Task CreateResource(string realm, Resource resource); - /// - /// Updates resource - /// - /// - /// Docs: https://github.com/keycloak/keycloak-documentation/blob/main/authorization_services/topics/service-protection-resources-api-papi.adoc#updating-resources - /// - /// - /// - /// - /// - [Put(KeycloakClientApiConstants.PutResource)] - [Headers("Accept: application/json", "Content-Type: application/json")] - Task UpdateResource( - string realm, string id, [Body] Resource resource); + // /// + // /// Updates resource + // /// + // /// + // /// Docs: https://github.com/keycloak/keycloak-documentation/blob/main/authorization_services/topics/service-protection-resources-api-papi.adoc#updating-resources + // /// + // /// + // /// + // /// + // /// + // Task UpdateResource(string realm, string id, Resource resource); } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakRealmClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakRealmClient.cs index 1d2466c7..46c98e75 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakRealmClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakRealmClient.cs @@ -1,7 +1,7 @@ namespace Keycloak.AuthServices.Sdk.Admin; -using Constants; -using Refit; +using Keycloak.AuthServices.Sdk; +using Keycloak.AuthServices.Sdk.Admin.Models; /// /// Realm management @@ -12,8 +12,26 @@ public interface IKeycloakRealmClient /// Get realm /// /// + /// /// - [Get(KeycloakClientApiConstants.GetRealm)] - [Headers("Accept: application/json")] - Task GetRealm(string realm); + async Task GetRealmAsync( + string realm, + CancellationToken cancellationToken = default + ) + { + var response = await this.GetRealmWithResponseAsync(realm, cancellationToken); + + return (await response.GetAsync(cancellationToken))!; + } + + /// + /// Get realm + /// + /// + /// + /// + Task GetRealmWithResponseAsync( + string realm, + CancellationToken cancellationToken = default + ); } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs index d8c9efbb..87c8ba5a 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs @@ -1,121 +1,99 @@ namespace Keycloak.AuthServices.Sdk.Admin; -using Constants; -using Models; -using Refit; -using Requests.Groups; -using Requests.Users; - /// /// User management /// -[Headers("Accept: application/json")] public interface IKeycloakUserClient { - /// - /// Get a stream of users on the realm. - /// - /// Realm name (not ID). - /// Optional query parameters. - /// A stream of users, filtered according to query parameters. - [Get(KeycloakClientApiConstants.GetUsers)] - Task> GetUsers( - string realm, - [Query] GetUsersRequestParameters? parameters = default - ); + // /// + // /// Get a stream of users on the realm. + // /// + // /// Realm name (not ID). + // /// Optional query parameters. + // /// A stream of users, filtered according to query parameters. + // Task> GetUsers(string realm, GetUsersRequestParameters? parameters = default); - /// - /// Get representation of a user. - /// - /// Realm name (not ID). - /// User ID. - /// The user representation. - [Get(KeycloakClientApiConstants.GetUser)] - Task GetUser(string realm, [AliasAs("id")] string userId); + // /// + // /// Get representation of a user. + // /// + // /// Realm name (not ID). + // /// User ID. + // /// The user representation. + // Task GetUser(string realm, string userId); - /// - /// Create a new user. - /// - /// - /// Username must be unique. - /// - /// Realm name (not ID). - /// User representation. - /// - [Post(KeycloakClientApiConstants.CreateUser)] - [Headers("Content-Type: application/json")] - Task CreateUser(string realm, [Body] User user); + // /// + // /// Create a new user. + // /// + // /// + // /// Username must be unique. + // /// + // /// Realm name (not ID). + // /// User representation. + // /// + // Task CreateUser(string realm, User user); - /// - /// Update the user. - /// - /// Realm name (not ID). - /// User ID. - /// User representation. - /// - [Put(KeycloakClientApiConstants.UpdateUser)] - [Headers("Content-Type: application/json")] - Task UpdateUser(string realm, [AliasAs("id")] string userId, [Body] User user); + // /// + // /// Update the user. + // /// + // /// Realm name (not ID). + // /// User ID. + // /// User representation. + // /// + // Task UpdateUser(string realm, string userId, User user); - /// - /// Delete the given user. - /// - /// Realm name (not ID). - /// User ID. - /// - [Delete(KeycloakClientApiConstants.DeleteUser)] - [Headers("Content-Type: application/json")] - Task DeleteUser(string realm, [AliasAs("id")] string userId); + // /// + // /// Delete the given user. + // /// + // /// Realm name (not ID). + // /// User ID. + // /// + // Task DeleteUser(string realm, string userId); - /// - /// Send an email-verification email to the user. - /// - /// - /// An email contains a link the user can click to verify their email address. - /// - /// Realm name (not ID). - /// User ID. - /// Client ID. - /// Redirect URI. The default for the redirect is the account client. - [Put(KeycloakClientApiConstants.SendVerifyEmail)] - Task SendVerifyEmail( - string realm, - [AliasAs("id")] string userId, - [Query] [AliasAs("client_id")] string? clientId = default, - [Query] [AliasAs("redirect_uri")] string? redirectUri = default - ); + // /// + // /// Send an email-verification email to the user. + // /// + // /// + // /// An email contains a link the user can click to verify their email address. + // /// + // /// Realm name (not ID). + // /// User ID. + // /// Client ID. + // /// Redirect URI. The default for the redirect is the account client. + // Task SendVerifyEmail( + // string realm, + // string userId, + // string? clientId = default, + // string? redirectUri = default + // ); - /// - /// Execute actions email for the user. - /// - /// Realm name (not ID). - /// User ID. - /// Client ID. - /// Number of seconds after which the generated token expires - /// Redirect URI. The default for the redirect is the account client. - /// Actions - [Put(KeycloakClientApiConstants.ExecuteActionsEmail)] - [Headers("Content-Type: application/json")] - Task ExecuteActionsEmail( - string realm, - [AliasAs("id")] string userId, - [Query] [AliasAs("client_id")] string? clientId = default, - [Query] int? lifespan = default, - [Query] [AliasAs("redirect_uri")] string? redirectUri = default, - [Body] List? actions = default - ); + // /// + // /// Execute actions email for the user. + // /// + // /// Realm name (not ID). + // /// User ID. + // /// Client ID. + // /// Number of seconds after which the generated token expires + // /// Redirect URI. The default for the redirect is the account client. + // /// Actions + // Task ExecuteActionsEmail( + // string realm, + // string userId, + // string? clientId = default, + // int? lifespan = default, + // string? redirectUri = default, + // List? actions = default + // ); - /// - /// Get a users's groups. - /// - /// Realm name (not ID). - /// User ID. - /// Optional query parameters. - /// A stream of users, filtered according to query parameters. - [Get(KeycloakClientApiConstants.GetUserGroups)] - Task> GetUserGroups( - string realm, - [AliasAs("id")] string userId, - [Query] GetGroupRequestParameters? parameters = default - ); + // /// + // /// Get a users's groups. + // /// + // /// Realm name (not ID). + // /// User ID. + // /// Optional query parameters. + // /// A stream of users, filtered according to query parameters. + // Task> GetUserGroups( + // string realm, + // string userId, + // GetGroupRequestParameters? parameters = default + // ); } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs new file mode 100644 index 00000000..9cae4f70 --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs @@ -0,0 +1,33 @@ +namespace Keycloak.AuthServices.Sdk.Admin; + +using Keycloak.AuthServices.Sdk.Admin.Constants; + +/// +/// TBD: +/// +public partial class KeycloakClient : IKeycloakClient +{ + private readonly HttpClient httpClient; + + /// + /// TBD: + /// + /// + public KeycloakClient(HttpClient httpClient) => this.httpClient = httpClient; + + /// + public async Task GetRealmWithResponseAsync( + string realm, + CancellationToken cancellationToken = default + ) + { + var path = KeycloakClientApiConstants.GetRealm.Replace("{realm}", realm); + + var responseMessage = await this.httpClient.GetAsync( + path, + cancellationToken: cancellationToken + ); + + return responseMessage!; + } +} diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Models/Contracts.cs b/src/Keycloak.AuthServices.Sdk/Admin/Models/Contracts.cs new file mode 100644 index 00000000..5ee15e0d --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/Admin/Models/Contracts.cs @@ -0,0 +1,8144 @@ +#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended." +#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword." +#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?' +#pragma warning disable 612 // Disable "CS0612 '...' is obsolete" +#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ... +#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..." +#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'" +#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant" +#pragma warning disable 8603 // Disable "CS8603 Possible null reference return" +#pragma warning disable 8604 // Disable "CS8604 Possible null reference argument for parameter" + +namespace Keycloak.AuthServices.Sdk.Admin.Models; + +using System = global::System; + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class AbstractPolicyRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("description")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Description { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("type")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Type { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("policies")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Policies { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("resources")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Resources { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("scopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Scopes { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("logic")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Text.Json.Serialization.JsonConverter( + typeof(System.Text.Json.Serialization.JsonStringEnumConverter) + )] + public Logic? Logic { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("decisionStrategy")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Text.Json.Serialization.JsonConverter( + typeof(System.Text.Json.Serialization.JsonStringEnumConverter) + )] + public DecisionStrategy? DecisionStrategy { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("owner")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Owner { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("resourcesData")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? ResourcesData { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("scopesData")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? ScopesData { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class Access +{ + [System.Text.Json.Serialization.JsonPropertyName("roles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Roles { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("verify_caller")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Verify_caller { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class AccessToken +{ + [System.Text.Json.Serialization.JsonPropertyName("jti")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Jti { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("exp")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? Exp { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("nbf")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? Nbf { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("iat")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? Iat { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("iss")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Iss { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("sub")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Sub { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("typ")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Typ { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("azp")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Azp { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("otherClaims")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? OtherClaims { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("nonce")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Nonce { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("auth_time")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? Auth_time { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("session_state")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Session_state { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("at_hash")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? At_hash { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("c_hash")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? C_hash { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("given_name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Given_name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("family_name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Family_name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("middle_name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Middle_name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("nickname")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Nickname { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("preferred_username")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Preferred_username { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("profile")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Profile { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("picture")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Picture { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("website")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Website { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("email")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Email { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("email_verified")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Email_verified { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("gender")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Gender { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("birthdate")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Birthdate { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("zoneinfo")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Zoneinfo { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("locale")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Locale { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("phone_number")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Phone_number { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("phone_number_verified")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Phone_number_verified { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("address")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public AddressClaimSet? Address { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("updated_at")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? Updated_at { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("claims_locales")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Claims_locales { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("acr")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Acr { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("s_hash")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? S_hash { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("authTime")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public int? AuthTime { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("sid")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Sid { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("trusted-certs")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? TrustedCerts { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("allowed-origins")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? AllowedOrigins { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("realm_access")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public Access? Realm_access { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("resource_access")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Resource_access { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("authorization")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public Authorization? Authorization { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("cnf")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public Confirmation? Cnf { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("scope")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Scope { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class AddressClaimSet +{ + [System.Text.Json.Serialization.JsonPropertyName("formatted")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Formatted { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("street_address")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Street_address { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("locality")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Locality { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("region")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Region { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("postal_code")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Postal_code { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("country")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Country { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class AdminEventRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("time")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? Time { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("realmId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? RealmId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("authDetails")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public AuthDetailsRepresentation? AuthDetails { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("operationType")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? OperationType { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("resourceType")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ResourceType { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("resourcePath")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ResourcePath { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("representation")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Representation { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("error")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Error { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +[System.Obsolete] +public partial class ApplicationRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ClientId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("description")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Description { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("rootUrl")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? RootUrl { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("adminUrl")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? AdminUrl { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("baseUrl")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? BaseUrl { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("surrogateAuthRequired")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? SurrogateAuthRequired { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("enabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Enabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("alwaysDisplayInConsole")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? AlwaysDisplayInConsole { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientAuthenticatorType")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ClientAuthenticatorType { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("secret")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Secret { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("registrationAccessToken")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? RegistrationAccessToken { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("defaultRoles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public System.Collections.Generic.ICollection? DefaultRoles { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("redirectUris")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? RedirectUris { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("webOrigins")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? WebOrigins { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("notBefore")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? NotBefore { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("bearerOnly")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? BearerOnly { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("consentRequired")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? ConsentRequired { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("standardFlowEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? StandardFlowEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("implicitFlowEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? ImplicitFlowEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("directAccessGrantsEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? DirectAccessGrantsEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("serviceAccountsEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? ServiceAccountsEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("authorizationServicesEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? AuthorizationServicesEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("directGrantsOnly")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? DirectGrantsOnly { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("publicClient")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? PublicClient { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("frontchannelLogout")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? FrontchannelLogout { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("protocol")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Protocol { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("attributes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Attributes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("authenticationFlowBindingOverrides")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + string + >? AuthenticationFlowBindingOverrides { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("fullScopeAllowed")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? FullScopeAllowed { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("nodeReRegistrationTimeout")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? NodeReRegistrationTimeout { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("registeredNodes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? RegisteredNodes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("protocolMappers")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? ProtocolMappers { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientTemplate")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public string? ClientTemplate { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("useTemplateConfig")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? UseTemplateConfig { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("useTemplateScope")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? UseTemplateScope { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("useTemplateMappers")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? UseTemplateMappers { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("defaultClientScopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? DefaultClientScopes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("optionalClientScopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? OptionalClientScopes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("authorizationSettings")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public ResourceServerRepresentation? AuthorizationSettings { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("access")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Access { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("origin")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Origin { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("claims")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public Claims? Claims { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class AuthDetailsRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("realmId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? RealmId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ClientId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("userId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? UserId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("ipAddress")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? IpAddress { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class AuthenticationExecutionExportRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("authenticatorConfig")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? AuthenticatorConfig { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("authenticator")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Authenticator { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("authenticatorFlow")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? AuthenticatorFlow { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("requirement")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Requirement { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("priority")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? Priority { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("autheticatorFlow")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? AutheticatorFlow { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("flowAlias")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? FlowAlias { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("userSetupAllowed")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? UserSetupAllowed { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class AuthenticationExecutionInfoRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("requirement")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Requirement { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("displayName")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? DisplayName { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("alias")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Alias { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("description")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Description { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("requirementChoices")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? RequirementChoices { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("configurable")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Configurable { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("authenticationFlow")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? AuthenticationFlow { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("providerId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ProviderId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("authenticationConfig")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? AuthenticationConfig { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("flowId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? FlowId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("level")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? Level { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("index")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? Index { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class AuthenticationExecutionRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("authenticatorConfig")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? AuthenticatorConfig { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("authenticator")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Authenticator { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("authenticatorFlow")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? AuthenticatorFlow { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("requirement")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Requirement { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("priority")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? Priority { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("autheticatorFlow")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? AutheticatorFlow { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("flowId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? FlowId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("parentFlow")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ParentFlow { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class AuthenticationFlowRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("alias")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Alias { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("description")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Description { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("providerId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ProviderId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("topLevel")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? TopLevel { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("builtIn")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? BuiltIn { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("authenticationExecutions")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? AuthenticationExecutions { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class AuthenticatorConfigInfoRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("providerId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ProviderId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("helpText")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? HelpText { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("properties")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Properties { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class AuthenticatorConfigRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("alias")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Alias { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("config")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Config { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class Authorization +{ + [System.Text.Json.Serialization.JsonPropertyName("permissions")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Permissions { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class CertificateRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("privateKey")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? PrivateKey { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("publicKey")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? PublicKey { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("certificate")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Certificate { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("kid")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Kid { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ClaimRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("username")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Username { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("profile")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Profile { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("picture")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Picture { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("website")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Website { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("email")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Email { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("gender")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Gender { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("locale")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Locale { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("address")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Address { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("phone")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Phone { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ClientInitialAccessCreatePresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("expiration")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? Expiration { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("count")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? Count { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ClientInitialAccessPresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("token")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Token { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("timestamp")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? Timestamp { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("expiration")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? Expiration { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("count")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? Count { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("remainingCount")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? RemainingCount { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ClientMappingsRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("client")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Client { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("mappings")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Mappings { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ClientPoliciesRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("policies")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Policies { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ClientPolicyConditionRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("condition")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Condition { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("configuration")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Configuration { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ClientPolicyExecutorRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("executor")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Executor { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("configuration")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Configuration { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ClientPolicyRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("description")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Description { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("enabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Enabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("conditions")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Conditions { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("profiles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Profiles { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ClientProfileRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("description")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Description { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("executors")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Executors { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ClientProfilesRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("profiles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Profiles { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("globalProfiles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? GlobalProfiles { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ClientRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ClientId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("description")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Description { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("rootUrl")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? RootUrl { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("adminUrl")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? AdminUrl { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("baseUrl")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? BaseUrl { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("surrogateAuthRequired")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? SurrogateAuthRequired { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("enabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Enabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("alwaysDisplayInConsole")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? AlwaysDisplayInConsole { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientAuthenticatorType")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ClientAuthenticatorType { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("secret")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Secret { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("registrationAccessToken")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? RegistrationAccessToken { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("defaultRoles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public System.Collections.Generic.ICollection? DefaultRoles { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("redirectUris")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? RedirectUris { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("webOrigins")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? WebOrigins { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("notBefore")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? NotBefore { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("bearerOnly")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? BearerOnly { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("consentRequired")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? ConsentRequired { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("standardFlowEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? StandardFlowEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("implicitFlowEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? ImplicitFlowEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("directAccessGrantsEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? DirectAccessGrantsEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("serviceAccountsEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? ServiceAccountsEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("authorizationServicesEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? AuthorizationServicesEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("directGrantsOnly")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? DirectGrantsOnly { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("publicClient")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? PublicClient { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("frontchannelLogout")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? FrontchannelLogout { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("protocol")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Protocol { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("attributes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Attributes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("authenticationFlowBindingOverrides")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + string + >? AuthenticationFlowBindingOverrides { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("fullScopeAllowed")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? FullScopeAllowed { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("nodeReRegistrationTimeout")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? NodeReRegistrationTimeout { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("registeredNodes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? RegisteredNodes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("protocolMappers")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? ProtocolMappers { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientTemplate")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public string? ClientTemplate { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("useTemplateConfig")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? UseTemplateConfig { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("useTemplateScope")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? UseTemplateScope { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("useTemplateMappers")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? UseTemplateMappers { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("defaultClientScopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? DefaultClientScopes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("optionalClientScopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? OptionalClientScopes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("authorizationSettings")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public ResourceServerRepresentation? AuthorizationSettings { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("access")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Access { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("origin")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Origin { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ClientScopeRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("description")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Description { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("protocol")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Protocol { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("attributes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Attributes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("protocolMappers")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? ProtocolMappers { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +[System.Obsolete] +public partial class ClientTemplateRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("description")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Description { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("protocol")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Protocol { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("fullScopeAllowed")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? FullScopeAllowed { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("bearerOnly")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? BearerOnly { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("consentRequired")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? ConsentRequired { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("standardFlowEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? StandardFlowEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("implicitFlowEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? ImplicitFlowEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("directAccessGrantsEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? DirectAccessGrantsEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("serviceAccountsEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? ServiceAccountsEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("publicClient")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? PublicClient { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("frontchannelLogout")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? FrontchannelLogout { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("attributes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Attributes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("protocolMappers")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? ProtocolMappers { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ComponentExportRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("providerId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ProviderId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("subType")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? SubType { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("subComponents")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public MultivaluedHashMapStringComponentExportRepresentation? SubComponents { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("config")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public MultivaluedHashMapStringString? Config { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ComponentRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("providerId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ProviderId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("providerType")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ProviderType { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("parentId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ParentId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("subType")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? SubType { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("config")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public MultivaluedHashMapStringString? Config { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ComponentTypeRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("helpText")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? HelpText { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("properties")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Properties { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("metadata")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Metadata { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class Composites +{ + [System.Text.Json.Serialization.JsonPropertyName("realm")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Realm { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("client")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.ICollection + >? Client { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("application")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.ICollection + >? Application { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ConfigPropertyRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("label")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Label { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("helpText")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? HelpText { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("type")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Type { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("defaultValue")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public object? DefaultValue { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("options")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Options { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("secret")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Secret { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("required")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Required { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("readOnly")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? ReadOnly { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class Confirmation +{ + [System.Text.Json.Serialization.JsonPropertyName("x5t#S256")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? X5t_S256 { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("jkt")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Jkt { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class CredentialRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("type")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Type { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("userLabel")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? UserLabel { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("createdDate")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? CreatedDate { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("secretData")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? SecretData { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("credentialData")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? CredentialData { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("priority")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? Priority { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("value")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Value { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("temporary")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Temporary { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("device")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public string? Device { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("hashedSaltedValue")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public string? HashedSaltedValue { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("salt")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public string? Salt { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("hashIterations")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public int? HashIterations { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("counter")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public int? Counter { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("algorithm")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public string? Algorithm { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("digits")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public int? Digits { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("period")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public int? Period { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("config")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public Config? Config { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public enum DecisionEffect +{ + [System.Runtime.Serialization.EnumMember(Value = @"PERMIT")] + PERMIT = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"DENY")] + DENY = 1, +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public enum DecisionStrategy +{ + [System.Runtime.Serialization.EnumMember(Value = @"AFFIRMATIVE")] + AFFIRMATIVE = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"UNANIMOUS")] + UNANIMOUS = 1, + + [System.Runtime.Serialization.EnumMember(Value = @"CONSENSUS")] + CONSENSUS = 2, +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public enum EnforcementMode +{ + [System.Runtime.Serialization.EnumMember(Value = @"PERMISSIVE")] + PERMISSIVE = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"ENFORCING")] + ENFORCING = 1, + + [System.Runtime.Serialization.EnumMember(Value = @"DISABLED")] + DISABLED = 2, +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class EvaluationResultRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("resource")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public ResourceRepresentation? Resource { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("scopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Scopes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("policies")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Policies { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("status")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Text.Json.Serialization.JsonConverter( + typeof(System.Text.Json.Serialization.JsonStringEnumConverter) + )] + public DecisionEffect? Status { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("allowedScopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? AllowedScopes { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class EventRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("time")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? Time { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("type")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Type { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("realmId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? RealmId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ClientId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("userId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? UserId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("sessionId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? SessionId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("ipAddress")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? IpAddress { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("error")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Error { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("details")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Details { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class FederatedIdentityRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("identityProvider")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? IdentityProvider { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("userId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? UserId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("userName")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? UserName { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class GlobalRequestResult +{ + [System.Text.Json.Serialization.JsonPropertyName("successRequests")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? SuccessRequests { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("failedRequests")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? FailedRequests { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class GroupRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("path")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Path { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("parentId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ParentId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("subGroupCount")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? SubGroupCount { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("subGroups")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? SubGroups { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("attributes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.ICollection + >? Attributes { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("realmRoles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? RealmRoles { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientRoles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.ICollection + >? ClientRoles { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("access")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Access { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class IDToken +{ + [System.Text.Json.Serialization.JsonPropertyName("jti")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Jti { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("exp")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? Exp { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("nbf")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? Nbf { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("iat")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? Iat { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("iss")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Iss { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("sub")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Sub { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("typ")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Typ { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("azp")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Azp { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("otherClaims")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? OtherClaims { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("nonce")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Nonce { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("auth_time")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? Auth_time { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("session_state")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Session_state { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("at_hash")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? At_hash { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("c_hash")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? C_hash { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("given_name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Given_name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("family_name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Family_name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("middle_name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Middle_name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("nickname")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Nickname { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("preferred_username")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Preferred_username { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("profile")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Profile { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("picture")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Picture { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("website")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Website { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("email")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Email { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("email_verified")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Email_verified { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("gender")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Gender { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("birthdate")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Birthdate { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("zoneinfo")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Zoneinfo { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("locale")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Locale { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("phone_number")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Phone_number { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("phone_number_verified")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Phone_number_verified { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("address")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public AddressClaimSet? Address { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("updated_at")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? Updated_at { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("claims_locales")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Claims_locales { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("acr")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Acr { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("s_hash")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? S_hash { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("authTime")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public int? AuthTime { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("sid")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Sid { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class IdentityProviderMapperRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("identityProviderAlias")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? IdentityProviderAlias { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("identityProviderMapper")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? IdentityProviderMapper { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("config")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Config { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class IdentityProviderMapperTypeRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("category")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Category { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("helpText")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? HelpText { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("properties")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Properties { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class IdentityProviderRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("alias")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Alias { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("displayName")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? DisplayName { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("internalId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? InternalId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("providerId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ProviderId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("enabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Enabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("updateProfileFirstLoginMode")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public string? UpdateProfileFirstLoginMode { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("trustEmail")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? TrustEmail { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("storeToken")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? StoreToken { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("addReadTokenRoleOnCreate")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? AddReadTokenRoleOnCreate { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("authenticateByDefault")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? AuthenticateByDefault { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("linkOnly")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? LinkOnly { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("firstBrokerLoginFlowAlias")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? FirstBrokerLoginFlowAlias { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("postBrokerLoginFlowAlias")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? PostBrokerLoginFlowAlias { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("config")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Config { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("updateProfileFirstLogin")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? UpdateProfileFirstLogin { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class InstallationAdapterConfig +{ + [System.Text.Json.Serialization.JsonPropertyName("realm")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Realm { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("realm-public-key")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? RealmPublicKey { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("auth-server-url")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? AuthServerUrl { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("ssl-required")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? SslRequired { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("bearer-only")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? BearerOnly { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("resource")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Resource { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("public-client")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? PublicClient { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("verify-token-audience")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? VerifyTokenAudience { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("credentials")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Credentials { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("use-resource-role-mappings")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? UseResourceRoleMappings { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("confidential-port")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? ConfidentialPort { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("policy-enforcer")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public PolicyEnforcerConfig? PolicyEnforcer { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class KeyMetadataRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("providerId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ProviderId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("providerPriority")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? ProviderPriority { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("kid")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Kid { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("status")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Status { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("type")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Type { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("algorithm")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Algorithm { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("publicKey")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? PublicKey { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("certificate")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Certificate { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("use")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Text.Json.Serialization.JsonConverter( + typeof(System.Text.Json.Serialization.JsonStringEnumConverter) + )] + public KeyUse? Use { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("validTo")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? ValidTo { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class KeyStoreConfig +{ + [System.Text.Json.Serialization.JsonPropertyName("realmCertificate")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? RealmCertificate { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("storePassword")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? StorePassword { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("keyPassword")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? KeyPassword { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("keyAlias")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? KeyAlias { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("realmAlias")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? RealmAlias { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("format")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Format { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public enum KeyUse +{ + [System.Runtime.Serialization.EnumMember(Value = @"SIG")] + SIG = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"ENC")] + ENC = 1, +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class KeysMetadataRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("active")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Active { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("keys")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Keys { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public enum Logic +{ + [System.Runtime.Serialization.EnumMember(Value = @"POSITIVE")] + POSITIVE = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"NEGATIVE")] + NEGATIVE = 1, +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ManagementPermissionReference +{ + [System.Text.Json.Serialization.JsonPropertyName("enabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Enabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("resource")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Resource { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("scopePermissions")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? ScopePermissions { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class MappingsRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("realmMappings")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? RealmMappings { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientMappings")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + ClientMappingsRepresentation + >? ClientMappings { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class MethodConfig +{ + [System.Text.Json.Serialization.JsonPropertyName("method")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Method { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("scopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Scopes { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("scopes-enforcement-mode")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Text.Json.Serialization.JsonConverter( + typeof(System.Text.Json.Serialization.JsonStringEnumConverter) + )] + public ScopeEnforcementMode? ScopesEnforcementMode { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class MultivaluedHashMapStringComponentExportRepresentation + : System.Collections.Generic.Dictionary< + string, + System.Collections.ObjectModel.Collection + > { } + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class MultivaluedHashMapStringString + : System.Collections.Generic.Dictionary< + string, + System.Collections.ObjectModel.Collection + > { } + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class MultivaluedMapStringString + : System.Collections.Generic.Dictionary< + string, + System.Collections.ObjectModel.Collection + > { } + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +[System.Obsolete] +public partial class OAuthClientRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ClientId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("description")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Description { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("rootUrl")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? RootUrl { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("adminUrl")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? AdminUrl { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("baseUrl")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? BaseUrl { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("surrogateAuthRequired")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? SurrogateAuthRequired { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("enabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Enabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("alwaysDisplayInConsole")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? AlwaysDisplayInConsole { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientAuthenticatorType")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ClientAuthenticatorType { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("secret")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Secret { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("registrationAccessToken")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? RegistrationAccessToken { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("defaultRoles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public System.Collections.Generic.ICollection? DefaultRoles { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("redirectUris")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? RedirectUris { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("webOrigins")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? WebOrigins { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("notBefore")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? NotBefore { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("bearerOnly")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? BearerOnly { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("consentRequired")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? ConsentRequired { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("standardFlowEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? StandardFlowEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("implicitFlowEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? ImplicitFlowEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("directAccessGrantsEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? DirectAccessGrantsEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("serviceAccountsEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? ServiceAccountsEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("authorizationServicesEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? AuthorizationServicesEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("directGrantsOnly")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? DirectGrantsOnly { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("publicClient")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? PublicClient { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("frontchannelLogout")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? FrontchannelLogout { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("protocol")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Protocol { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("attributes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Attributes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("authenticationFlowBindingOverrides")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + string + >? AuthenticationFlowBindingOverrides { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("fullScopeAllowed")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? FullScopeAllowed { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("nodeReRegistrationTimeout")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? NodeReRegistrationTimeout { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("registeredNodes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? RegisteredNodes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("protocolMappers")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? ProtocolMappers { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientTemplate")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public string? ClientTemplate { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("useTemplateConfig")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? UseTemplateConfig { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("useTemplateScope")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? UseTemplateScope { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("useTemplateMappers")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? UseTemplateMappers { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("defaultClientScopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? DefaultClientScopes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("optionalClientScopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? OptionalClientScopes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("authorizationSettings")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public ResourceServerRepresentation? AuthorizationSettings { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("access")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Access { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("origin")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Origin { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("claims")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public Claims2? Claims { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class PathCacheConfig +{ + [System.Text.Json.Serialization.JsonPropertyName("max-entries")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? MaxEntries { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("lifespan")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? Lifespan { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class PathConfig +{ + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("type")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Type { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("path")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Path { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("methods")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Methods { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("scopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Scopes { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("enforcement-mode")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Text.Json.Serialization.JsonConverter( + typeof(System.Text.Json.Serialization.JsonStringEnumConverter) + )] + public EnforcementMode? EnforcementMode { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("claim-information-point")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.IDictionary + >? ClaimInformationPoint { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("invalidated")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Invalidated { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("staticPath")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? StaticPath { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("static")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Static { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class PathSegment +{ + [System.Text.Json.Serialization.JsonPropertyName("path")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Path { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("matrixParameters")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public MultivaluedMapStringString? MatrixParameters { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class Permission +{ + [System.Text.Json.Serialization.JsonPropertyName("rsid")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Rsid { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("rsname")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Rsname { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("scopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Scopes { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("claims")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.ICollection + >? Claims { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public enum PolicyEnforcementMode +{ + [System.Runtime.Serialization.EnumMember(Value = @"ENFORCING")] + ENFORCING = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"PERMISSIVE")] + PERMISSIVE = 1, + + [System.Runtime.Serialization.EnumMember(Value = @"DISABLED")] + DISABLED = 2, +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class PolicyEnforcerConfig +{ + [System.Text.Json.Serialization.JsonPropertyName("enforcement-mode")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Text.Json.Serialization.JsonConverter( + typeof(System.Text.Json.Serialization.JsonStringEnumConverter) + )] + public EnforcementMode? EnforcementMode { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("paths")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Paths { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("path-cache")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public PathCacheConfig? PathCache { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("lazy-load-paths")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? LazyLoadPaths { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("on-deny-redirect-to")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? OnDenyRedirectTo { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("user-managed-access")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public UserManagedAccessConfig? UserManagedAccess { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("claim-information-point")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.IDictionary + >? ClaimInformationPoint { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("http-method-as-scope")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? HttpMethodAsScope { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("realm")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Realm { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("auth-server-url")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? AuthServerUrl { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("credentials")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Credentials { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("resource")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Resource { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class PolicyEvaluationRequest +{ + [System.Text.Json.Serialization.JsonPropertyName("context")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.IDictionary + >? Context { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("resources")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Resources { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ClientId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("userId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? UserId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("roleIds")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? RoleIds { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("entitlements")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Entitlements { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class PolicyEvaluationResponse +{ + [System.Text.Json.Serialization.JsonPropertyName("results")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Results { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("entitlements")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Entitlements { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("status")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Text.Json.Serialization.JsonConverter( + typeof(System.Text.Json.Serialization.JsonStringEnumConverter) + )] + public DecisionEffect? Status { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("rpt")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public AccessToken? Rpt { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class PolicyProviderRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("type")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Type { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("group")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Group { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class PolicyRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("description")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Description { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("type")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Type { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("policies")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Policies { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("resources")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Resources { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("scopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Scopes { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("logic")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Text.Json.Serialization.JsonConverter( + typeof(System.Text.Json.Serialization.JsonStringEnumConverter) + )] + public Logic? Logic { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("decisionStrategy")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Text.Json.Serialization.JsonConverter( + typeof(System.Text.Json.Serialization.JsonStringEnumConverter) + )] + public DecisionStrategy? DecisionStrategy { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("owner")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Owner { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("resourcesData")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? ResourcesData { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("scopesData")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? ScopesData { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("config")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Config { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class PolicyResultRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("policy")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public PolicyRepresentation? Policy { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("status")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Text.Json.Serialization.JsonConverter( + typeof(System.Text.Json.Serialization.JsonStringEnumConverter) + )] + public DecisionEffect? Status { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("associatedPolicies")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? AssociatedPolicies { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("scopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Scopes { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ProtocolMapperEvaluationRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("mapperId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? MapperId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("mapperName")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? MapperName { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("containerId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ContainerId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("containerName")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ContainerName { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("containerType")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ContainerType { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("protocolMapper")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ProtocolMapper { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ProtocolMapperRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("protocol")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Protocol { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("protocolMapper")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ProtocolMapper { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("consentRequired")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? ConsentRequired { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("consentText")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public string? ConsentText { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("config")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Config { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class PublishedRealmRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("realm")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Realm { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("public_key")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Public_key { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("token-service")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? TokenService { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("account-service")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? AccountService { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("tokens-not-before")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? TokensNotBefore { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class RealmEventsConfigRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("eventsEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? EventsEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("eventsExpiration")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? EventsExpiration { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("eventsListeners")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? EventsListeners { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("enabledEventTypes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? EnabledEventTypes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("adminEventsEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? AdminEventsEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("adminEventsDetailsEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? AdminEventsDetailsEnabled { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class RealmRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("realm")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Realm { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("displayName")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? DisplayName { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("displayNameHtml")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? DisplayNameHtml { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("notBefore")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? NotBefore { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("defaultSignatureAlgorithm")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? DefaultSignatureAlgorithm { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("revokeRefreshToken")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? RevokeRefreshToken { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("refreshTokenMaxReuse")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? RefreshTokenMaxReuse { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("accessTokenLifespan")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? AccessTokenLifespan { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("accessTokenLifespanForImplicitFlow")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? AccessTokenLifespanForImplicitFlow { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("ssoSessionIdleTimeout")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? SsoSessionIdleTimeout { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("ssoSessionMaxLifespan")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? SsoSessionMaxLifespan { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("ssoSessionIdleTimeoutRememberMe")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? SsoSessionIdleTimeoutRememberMe { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("ssoSessionMaxLifespanRememberMe")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? SsoSessionMaxLifespanRememberMe { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("offlineSessionIdleTimeout")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? OfflineSessionIdleTimeout { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("offlineSessionMaxLifespanEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? OfflineSessionMaxLifespanEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("offlineSessionMaxLifespan")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? OfflineSessionMaxLifespan { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientSessionIdleTimeout")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? ClientSessionIdleTimeout { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientSessionMaxLifespan")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? ClientSessionMaxLifespan { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientOfflineSessionIdleTimeout")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? ClientOfflineSessionIdleTimeout { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientOfflineSessionMaxLifespan")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? ClientOfflineSessionMaxLifespan { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("accessCodeLifespan")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? AccessCodeLifespan { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("accessCodeLifespanUserAction")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? AccessCodeLifespanUserAction { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("accessCodeLifespanLogin")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? AccessCodeLifespanLogin { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("actionTokenGeneratedByAdminLifespan")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? ActionTokenGeneratedByAdminLifespan { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("actionTokenGeneratedByUserLifespan")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? ActionTokenGeneratedByUserLifespan { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("oauth2DeviceCodeLifespan")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? Oauth2DeviceCodeLifespan { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("enabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Enabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("sslRequired")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? SslRequired { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("passwordCredentialGrantAllowed")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? PasswordCredentialGrantAllowed { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("registrationAllowed")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? RegistrationAllowed { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("registrationEmailAsUsername")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? RegistrationEmailAsUsername { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("rememberMe")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? RememberMe { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("verifyEmail")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? VerifyEmail { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("loginWithEmailAllowed")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? LoginWithEmailAllowed { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("duplicateEmailsAllowed")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? DuplicateEmailsAllowed { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("resetPasswordAllowed")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? ResetPasswordAllowed { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("editUsernameAllowed")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? EditUsernameAllowed { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("userCacheEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? UserCacheEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("realmCacheEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? RealmCacheEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("bruteForceProtected")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? BruteForceProtected { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("permanentLockout")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? PermanentLockout { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("maxTemporaryLockouts")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? MaxTemporaryLockouts { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("maxFailureWaitSeconds")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? MaxFailureWaitSeconds { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("minimumQuickLoginWaitSeconds")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? MinimumQuickLoginWaitSeconds { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("waitIncrementSeconds")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? WaitIncrementSeconds { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("quickLoginCheckMilliSeconds")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? QuickLoginCheckMilliSeconds { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("maxDeltaTimeSeconds")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? MaxDeltaTimeSeconds { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("failureFactor")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? FailureFactor { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("privateKey")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public string? PrivateKey { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("publicKey")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public string? PublicKey { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("certificate")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public string? Certificate { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("codeSecret")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public string? CodeSecret { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("roles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public RolesRepresentation? Roles { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("groups")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Groups { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("defaultRoles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public System.Collections.Generic.ICollection? DefaultRoles { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("defaultRole")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public RoleRepresentation? DefaultRole { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("defaultGroups")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? DefaultGroups { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("requiredCredentials")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public System.Collections.Generic.ICollection? RequiredCredentials { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("passwordPolicy")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? PasswordPolicy { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("otpPolicyType")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? OtpPolicyType { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("otpPolicyAlgorithm")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? OtpPolicyAlgorithm { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("otpPolicyInitialCounter")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? OtpPolicyInitialCounter { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("otpPolicyDigits")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? OtpPolicyDigits { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("otpPolicyLookAheadWindow")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? OtpPolicyLookAheadWindow { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("otpPolicyPeriod")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? OtpPolicyPeriod { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("otpPolicyCodeReusable")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? OtpPolicyCodeReusable { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("otpSupportedApplications")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? OtpSupportedApplications { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("localizationTexts")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.IDictionary + >? LocalizationTexts { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("webAuthnPolicyRpEntityName")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? WebAuthnPolicyRpEntityName { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("webAuthnPolicySignatureAlgorithms")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? WebAuthnPolicySignatureAlgorithms { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("webAuthnPolicyRpId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? WebAuthnPolicyRpId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName( + "webAuthnPolicyAttestationConveyancePreference" + )] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? WebAuthnPolicyAttestationConveyancePreference { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("webAuthnPolicyAuthenticatorAttachment")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? WebAuthnPolicyAuthenticatorAttachment { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("webAuthnPolicyRequireResidentKey")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? WebAuthnPolicyRequireResidentKey { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("webAuthnPolicyUserVerificationRequirement")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? WebAuthnPolicyUserVerificationRequirement { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("webAuthnPolicyCreateTimeout")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? WebAuthnPolicyCreateTimeout { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName( + "webAuthnPolicyAvoidSameAuthenticatorRegister" + )] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? WebAuthnPolicyAvoidSameAuthenticatorRegister { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("webAuthnPolicyAcceptableAaguids")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? WebAuthnPolicyAcceptableAaguids { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("webAuthnPolicyExtraOrigins")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? WebAuthnPolicyExtraOrigins { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("webAuthnPolicyPasswordlessRpEntityName")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? WebAuthnPolicyPasswordlessRpEntityName { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName( + "webAuthnPolicyPasswordlessSignatureAlgorithms" + )] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? WebAuthnPolicyPasswordlessSignatureAlgorithms { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("webAuthnPolicyPasswordlessRpId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? WebAuthnPolicyPasswordlessRpId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName( + "webAuthnPolicyPasswordlessAttestationConveyancePreference" + )] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? WebAuthnPolicyPasswordlessAttestationConveyancePreference { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName( + "webAuthnPolicyPasswordlessAuthenticatorAttachment" + )] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? WebAuthnPolicyPasswordlessAuthenticatorAttachment { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName( + "webAuthnPolicyPasswordlessRequireResidentKey" + )] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? WebAuthnPolicyPasswordlessRequireResidentKey { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName( + "webAuthnPolicyPasswordlessUserVerificationRequirement" + )] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? WebAuthnPolicyPasswordlessUserVerificationRequirement { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("webAuthnPolicyPasswordlessCreateTimeout")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? WebAuthnPolicyPasswordlessCreateTimeout { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName( + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" + )] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? WebAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("webAuthnPolicyPasswordlessAcceptableAaguids")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? WebAuthnPolicyPasswordlessAcceptableAaguids { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("webAuthnPolicyPasswordlessExtraOrigins")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? WebAuthnPolicyPasswordlessExtraOrigins { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientProfiles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public ClientProfilesRepresentation? ClientProfiles { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientPolicies")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public ClientPoliciesRepresentation? ClientPolicies { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("users")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Users { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("federatedUsers")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? FederatedUsers { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("scopeMappings")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? ScopeMappings { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientScopeMappings")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.ICollection + >? ClientScopeMappings { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clients")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Clients { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientScopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? ClientScopes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("defaultDefaultClientScopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? DefaultDefaultClientScopes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("defaultOptionalClientScopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? DefaultOptionalClientScopes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("browserSecurityHeaders")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + string + >? BrowserSecurityHeaders { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("smtpServer")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? SmtpServer { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("userFederationProviders")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? UserFederationProviders { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("userFederationMappers")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? UserFederationMappers { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("loginTheme")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? LoginTheme { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("accountTheme")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? AccountTheme { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("adminTheme")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? AdminTheme { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("emailTheme")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? EmailTheme { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("eventsEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? EventsEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("eventsExpiration")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? EventsExpiration { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("eventsListeners")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? EventsListeners { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("enabledEventTypes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? EnabledEventTypes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("adminEventsEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? AdminEventsEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("adminEventsDetailsEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? AdminEventsDetailsEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("identityProviders")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? IdentityProviders { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("identityProviderMappers")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? IdentityProviderMappers { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("protocolMappers")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? ProtocolMappers { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("components")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public MultivaluedHashMapStringComponentExportRepresentation? Components { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("internationalizationEnabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? InternationalizationEnabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("supportedLocales")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? SupportedLocales { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("defaultLocale")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? DefaultLocale { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("authenticationFlows")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? AuthenticationFlows { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("authenticatorConfig")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? AuthenticatorConfig { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("requiredActions")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? RequiredActions { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("browserFlow")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? BrowserFlow { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("registrationFlow")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? RegistrationFlow { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("directGrantFlow")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? DirectGrantFlow { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("resetCredentialsFlow")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ResetCredentialsFlow { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientAuthenticationFlow")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ClientAuthenticationFlow { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("dockerAuthenticationFlow")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? DockerAuthenticationFlow { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("firstBrokerLoginFlow")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? FirstBrokerLoginFlow { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("attributes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Attributes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("keycloakVersion")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? KeycloakVersion { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("userManagedAccessAllowed")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? UserManagedAccessAllowed { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("social")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? Social { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("updateProfileOnInitialSocialLogin")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? UpdateProfileOnInitialSocialLogin { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("socialProviders")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public System.Collections.Generic.IDictionary? SocialProviders { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("applicationScopeMappings")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.ICollection + >? ApplicationScopeMappings { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("applications")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public System.Collections.Generic.ICollection? Applications { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("oauthClients")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public System.Collections.Generic.ICollection? OauthClients { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientTemplates")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public System.Collections.Generic.ICollection? ClientTemplates { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("oAuth2DevicePollingInterval")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? OAuth2DevicePollingInterval { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class RequiredActionProviderRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("alias")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Alias { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("providerId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ProviderId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("enabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Enabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("defaultAction")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? DefaultAction { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("priority")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? Priority { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("config")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Config { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ResourceOwnerRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ResourceRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("_id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? _id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("uris")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Uris { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("type")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Type { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("scopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Scopes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("icon_uri")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Icon_uri { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("owner")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public Owner? Owner { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("ownerManagedAccess")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? OwnerManagedAccess { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("displayName")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? DisplayName { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("attributes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.ICollection + >? Attributes { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("uri")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public string? Uri { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("scopesUma")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? ScopesUma { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ResourceServerRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ClientId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("allowRemoteResourceManagement")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? AllowRemoteResourceManagement { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("policyEnforcementMode")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Text.Json.Serialization.JsonConverter( + typeof(System.Text.Json.Serialization.JsonStringEnumConverter) + )] + public PolicyEnforcementMode? PolicyEnforcementMode { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("resources")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Resources { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("policies")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Policies { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("scopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Scopes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("decisionStrategy")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Text.Json.Serialization.JsonConverter( + typeof(System.Text.Json.Serialization.JsonStringEnumConverter) + )] + public DecisionStrategy? DecisionStrategy { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class RoleRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("description")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Description { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("scopeParamRequired")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public bool? ScopeParamRequired { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("composite")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Composite { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("composites")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public Composites? Composites { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientRole")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? ClientRole { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("containerId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ContainerId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("attributes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.ICollection + >? Attributes { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class RolesRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("realm")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Realm { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("client")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.ICollection + >? Client { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("application")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.ICollection + >? Application { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public enum ScopeEnforcementMode +{ + [System.Runtime.Serialization.EnumMember(Value = @"ALL")] + ALL = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"ANY")] + ANY = 1, + + [System.Runtime.Serialization.EnumMember(Value = @"DISABLED")] + DISABLED = 2, +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ScopeMappingRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("self")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Self { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("client")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Client { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientTemplate")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public string? ClientTemplate { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientScope")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ClientScope { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("roles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Roles { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class ScopeRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("iconUri")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? IconUri { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("policies")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Policies { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("resources")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Resources { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("displayName")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? DisplayName { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class SocialLinkRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("socialProvider")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? SocialProvider { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("socialUserId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? SocialUserId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("socialUsername")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? SocialUsername { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class UPAttribute +{ + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("displayName")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? DisplayName { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("validations")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.IDictionary + >? Validations { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("annotations")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Annotations { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("required")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public UPAttributeRequired? Required { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("permissions")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public UPAttributePermissions? Permissions { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("selector")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public UPAttributeSelector? Selector { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("group")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Group { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("multivalued")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Multivalued { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class UPAttributePermissions +{ + [System.Text.Json.Serialization.JsonPropertyName("view")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? View { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("edit")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Edit { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class UPAttributeRequired +{ + [System.Text.Json.Serialization.JsonPropertyName("roles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Roles { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("scopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Scopes { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class UPAttributeSelector +{ + [System.Text.Json.Serialization.JsonPropertyName("scopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Scopes { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class UPConfig +{ + [System.Text.Json.Serialization.JsonPropertyName("attributes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Attributes { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("groups")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Groups { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("unmanagedAttributePolicy")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Text.Json.Serialization.JsonConverter( + typeof(System.Text.Json.Serialization.JsonStringEnumConverter) + )] + public UnmanagedAttributePolicy? UnmanagedAttributePolicy { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class UPGroup +{ + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("displayHeader")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? DisplayHeader { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("displayDescription")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? DisplayDescription { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("annotations")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Annotations { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public enum UnmanagedAttributePolicy +{ + [System.Runtime.Serialization.EnumMember(Value = @"ENABLED")] + ENABLED = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"ADMIN_VIEW")] + ADMIN_VIEW = 1, + + [System.Runtime.Serialization.EnumMember(Value = @"ADMIN_EDIT")] + ADMIN_EDIT = 2, +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class UserConsentRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("clientId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ClientId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("grantedClientScopes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? GrantedClientScopes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("createdDate")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? CreatedDate { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("lastUpdatedDate")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? LastUpdatedDate { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("grantedRealmRoles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public System.Collections.Generic.ICollection? GrantedRealmRoles { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class UserFederationMapperRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("federationProviderDisplayName")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? FederationProviderDisplayName { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("federationMapperType")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? FederationMapperType { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("config")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Config { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class UserFederationProviderRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("displayName")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? DisplayName { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("providerName")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ProviderName { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("config")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Config { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("priority")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? Priority { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("fullSyncPeriod")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? FullSyncPeriod { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("changedSyncPeriod")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? ChangedSyncPeriod { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("lastSync")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? LastSync { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class UserManagedAccessConfig +{ + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class UserProfileAttributeGroupMetadata +{ + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("displayHeader")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? DisplayHeader { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("displayDescription")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? DisplayDescription { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("annotations")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Annotations { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class UserProfileAttributeMetadata +{ + [System.Text.Json.Serialization.JsonPropertyName("name")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Name { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("displayName")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? DisplayName { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("required")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Required { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("readOnly")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? ReadOnly { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("annotations")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Annotations { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("validators")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.IDictionary + >? Validators { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("group")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Group { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("multivalued")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Multivalued { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class UserProfileMetadata +{ + [System.Text.Json.Serialization.JsonPropertyName("attributes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Attributes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("groups")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Groups { get; set; } = + default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class UserRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("username")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Username { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("firstName")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? FirstName { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("lastName")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? LastName { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("email")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Email { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("emailVerified")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? EmailVerified { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("attributes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.ICollection + >? Attributes { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("userProfileMetadata")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public UserProfileMetadata? UserProfileMetadata { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("self")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Self { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("origin")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Origin { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("createdTimestamp")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? CreatedTimestamp { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("enabled")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Enabled { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("totp")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? Totp { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("federationLink")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? FederationLink { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("serviceAccountClientId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? ServiceAccountClientId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("credentials")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Credentials { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("disableableCredentialTypes")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? DisableableCredentialTypes { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("requiredActions")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? RequiredActions { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("federatedIdentities")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? FederatedIdentities { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("realmRoles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? RealmRoles { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientRoles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.ICollection + >? ClientRoles { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clientConsents")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? ClientConsents { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("notBefore")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public int? NotBefore { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("applicationRoles")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public System.Collections.Generic.IDictionary< + string, + System.Collections.Generic.ICollection + >? ApplicationRoles { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("socialLinks")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + [System.Obsolete] + public System.Collections.Generic.ICollection? SocialLinks { get; set; } = + default!; + + [System.Text.Json.Serialization.JsonPropertyName("groups")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.ICollection? Groups { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("access")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Access { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class UserSessionRepresentation +{ + [System.Text.Json.Serialization.JsonPropertyName("id")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Id { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("username")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? Username { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("userId")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? UserId { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("ipAddress")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public string? IpAddress { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("start")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? Start { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("lastAccess")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public long? LastAccess { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("rememberMe")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public bool? RememberMe { get; set; } = default!; + + [System.Text.Json.Serialization.JsonPropertyName("clients")] + [System.Text.Json.Serialization.JsonIgnore( + Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault + )] + public System.Collections.Generic.IDictionary? Clients { get; set; } = default!; + + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +[System.Obsolete] +public partial class Claims : ClaimRepresentation { } + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +[System.Obsolete] +public partial class Config + : System.Collections.Generic.Dictionary< + string, + System.Collections.ObjectModel.Collection + > +{ + private System.Collections.Generic.IDictionary? _additionalProperties; + + [System.Text.Json.Serialization.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get + { + return _additionalProperties + ?? ( + _additionalProperties = new System.Collections.Generic.Dictionary< + string, + object + >() + ); + } + set { _additionalProperties = value; } + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +[System.Obsolete] +public partial class Claims2 : ClaimRepresentation { } + +[System.CodeDom.Compiler.GeneratedCode( + "NJsonSchema", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class Owner : ResourceOwnerRepresentation { } + +[System.CodeDom.Compiler.GeneratedCode( + "NSwag", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class FileParameter +{ + public FileParameter(System.IO.Stream data) + : this(data, null, null) { } + + public FileParameter(System.IO.Stream data, string? fileName) + : this(data, fileName, null) { } + + public FileParameter(System.IO.Stream data, string? fileName, string? contentType) + { + Data = data; + FileName = fileName; + ContentType = contentType; + } + + public System.IO.Stream Data { get; private set; } + + public string? FileName { get; private set; } + + public string? ContentType { get; private set; } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NSwag", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class FileResponse : System.IDisposable +{ + private System.IDisposable? _client; + private System.IDisposable? _response; + + public int StatusCode { get; private set; } + + public System.Collections.Generic.IReadOnlyDictionary< + string, + System.Collections.Generic.IEnumerable + > Headers { get; private set; } + + public System.IO.Stream Stream { get; private set; } + + public bool IsPartial + { + get { return StatusCode == 206; } + } + + public FileResponse( + int statusCode, + System.Collections.Generic.IReadOnlyDictionary< + string, + System.Collections.Generic.IEnumerable + > headers, + System.IO.Stream stream, + System.IDisposable? client, + System.IDisposable? response + ) + { + StatusCode = statusCode; + Headers = headers; + Stream = stream; + _client = client; + _response = response; + } + + public void Dispose() + { + Stream.Dispose(); + if (_response != null) + _response.Dispose(); + if (_client != null) + _client.Dispose(); + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NSwag", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class KeycloakException : System.Exception +{ + public int StatusCode { get; private set; } + + public string? Response { get; private set; } + + public System.Collections.Generic.IReadOnlyDictionary< + string, + System.Collections.Generic.IEnumerable + > Headers { get; private set; } + + public KeycloakException( + string message, + int statusCode, + string? response, + System.Collections.Generic.IReadOnlyDictionary< + string, + System.Collections.Generic.IEnumerable + > headers, + System.Exception? innerException + ) + : base( + message + + "\n\nStatus: " + + statusCode + + "\nResponse: \n" + + ( + (response == null) + ? "(null)" + : response.Substring(0, response.Length >= 512 ? 512 : response.Length) + ), + innerException + ) + { + StatusCode = statusCode; + Response = response; + Headers = headers; + } + + public override string ToString() + { + return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString()); + } +} + +[System.CodeDom.Compiler.GeneratedCode( + "NSwag", + "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" +)] +public partial class KeycloakException : KeycloakException +{ + public TResult Result { get; private set; } + + public KeycloakException( + string message, + int statusCode, + string? response, + System.Collections.Generic.IReadOnlyDictionary< + string, + System.Collections.Generic.IEnumerable + > headers, + TResult result, + System.Exception? innerException + ) + : base(message, statusCode, response, headers, innerException) + { + Result = result; + } +} + +#pragma warning restore 108 +#pragma warning restore 114 +#pragma warning restore 472 +#pragma warning restore 612 +#pragma warning restore 1573 +#pragma warning restore 1591 +#pragma warning restore 8073 +#pragma warning restore 3016 +#pragma warning restore 8603 +#pragma warning restore 8604 diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Models/Group/Group.cs b/src/Keycloak.AuthServices.Sdk/Admin/Models/Group/Group.cs deleted file mode 100644 index 987de774..00000000 --- a/src/Keycloak.AuthServices.Sdk/Admin/Models/Group/Group.cs +++ /dev/null @@ -1,18 +0,0 @@ -#pragma warning disable CS1591, CS8618 -namespace Keycloak.AuthServices.Sdk.Admin.Models; - -using System.Collections.Generic; - -/// -/// Group representation. -/// -public class Group -{ - public string Id { get; init; } = default!; - public string? Name { get; init; } - public string? Path { get; init; } - public Dictionary? ClientRoles { get; init; } - public string[]? RealmRoles { get; init; } - public Group[]? SubGroups { get; init; } - public Dictionary? Attributes { get; init; } -} diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Models/Resources/Resource.cs b/src/Keycloak.AuthServices.Sdk/Admin/Models/Resources/Resource.cs deleted file mode 100644 index 93794538..00000000 --- a/src/Keycloak.AuthServices.Sdk/Admin/Models/Resources/Resource.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace Keycloak.AuthServices.Sdk.Admin.Models; - -using System.Text.Json.Serialization; - -/// -/// -public class Resource -{ - /// - /// Constructs resource - /// - /// Name of the resource - /// Scope of the resource. Usually, it is in a form of verb - public Resource(string name, string[] scopes) - { - this.Name = name; - this.Scopes = scopes; - } - - /// - /// Resource name - /// - public string Name { get; } - - /// - /// Display name - /// - public string? DisplayName { get; init; } - - /// - /// Resource type - /// - /// urn:workspace-authz:resource:workspaces - public string? Type { get; init; } - - /// - /// Resource scopes - /// - [JsonPropertyName("resource_scopes")] - public string[] Scopes { get; } - - /// - /// Resource attributes - /// - public Dictionary Attributes { get; init; } = new Dictionary(); -} diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Models/Resources/ResourceResponse.cs b/src/Keycloak.AuthServices.Sdk/Admin/Models/Resources/ResourceResponse.cs deleted file mode 100644 index 2ec13c73..00000000 --- a/src/Keycloak.AuthServices.Sdk/Admin/Models/Resources/ResourceResponse.cs +++ /dev/null @@ -1,38 +0,0 @@ -#pragma warning disable CS1591, CS8618 -namespace Keycloak.AuthServices.Sdk.Admin.Models; - -using System.Text.Json.Serialization; - -/// -/// -public class ResourceResponse -{ - [JsonPropertyName("_id")] - public string Id { get; set; } - public string Name { get; set; } - public string Type { get; set; } - public Owner Owner { get; set; } - public bool OwnerManagedAccess { get; set; } - public string DisplayName { get; set; } - public Dictionary> Attributes { get; set; } - public List Uris { get; set; } - - [JsonPropertyName("resource_scopes")] - public List ResourceScopes { get; set; } - public List Scopes { get; set; } -} - -public class Owner -{ - public string Id { get; set; } -} - -public class ResourceScope -{ - public string Name { get; set; } -} - -public class Scope -{ - public string Name { get; set; } -} diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Models/Users/Credential.cs b/src/Keycloak.AuthServices.Sdk/Admin/Models/Users/Credential.cs deleted file mode 100644 index 716801f7..00000000 --- a/src/Keycloak.AuthServices.Sdk/Admin/Models/Users/Credential.cs +++ /dev/null @@ -1,18 +0,0 @@ -#pragma warning disable CS1591, CS8618 -namespace Keycloak.AuthServices.Sdk.Admin.Models; - -/// -/// Credential representation. -/// -public class Credential -{ - public long? CreatedDate { get; init; } - public string? CredentialData { get; init; } - public string? Id { get; init; } - public int? Priority { get; init; } - public string? SecretData { get; init; } - public bool? Temporary { get; init; } - public string? Type { get; init; } - public string? UserLabel { get; init; } - public string? Value { get; init; } -} diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Models/Users/FederatedIdentity.cs b/src/Keycloak.AuthServices.Sdk/Admin/Models/Users/FederatedIdentity.cs deleted file mode 100644 index 3d31b8ae..00000000 --- a/src/Keycloak.AuthServices.Sdk/Admin/Models/Users/FederatedIdentity.cs +++ /dev/null @@ -1,12 +0,0 @@ -#pragma warning disable CS1591, CS8618 -namespace Keycloak.AuthServices.Sdk.Admin.Models; - -/// -/// Federated identity representation. -/// -public class FederatedIdentity -{ - public string? IdentityProvider { get; init; } - public string? UserId { get; init; } - public string? UserName { get; init; } -} diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Models/Users/User.cs b/src/Keycloak.AuthServices.Sdk/Admin/Models/Users/User.cs deleted file mode 100644 index 61df48ac..00000000 --- a/src/Keycloak.AuthServices.Sdk/Admin/Models/Users/User.cs +++ /dev/null @@ -1,33 +0,0 @@ -#pragma warning disable CS1591, CS8618 -namespace Keycloak.AuthServices.Sdk.Admin.Models; - -/// -/// User representation. -/// -public class User -{ - public Dictionary? Access { get; init; } - public Dictionary? Attributes { get; init; } - public UserConsent[]? ClientConsents { get; init; } - public Dictionary? ClientRoles { get; init; } - public long? CreatedTimestamp { get; init; } - public Credential[]? Credentials { get; init; } - public string[]? DisableableCredentialTypes { get; init; } - public string? Email { get; init; } - public bool? EmailVerified { get; init; } - public bool? Enabled { get; init; } - public FederatedIdentity[]? FederatedIdentities { get; init; } - public string? FederationLink { get; init; } - public string? FirstName { get; init; } - public string[]? Groups { get; init; } - public string? Id { get; init; } - public string? LastName { get; init; } - public int? NotBefore { get; init; } - public string? Origin { get; init; } - public string[]? RealmRoles { get; init; } - public string[]? RequiredActions { get; init; } - public string? Self { get; init; } - public string? ServiceAccountClientId { get; init; } - public string? Username { get; init; } - public bool? Totp { get; init; } -} diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Models/Users/UserConsent.cs b/src/Keycloak.AuthServices.Sdk/Admin/Models/Users/UserConsent.cs deleted file mode 100644 index 93e83759..00000000 --- a/src/Keycloak.AuthServices.Sdk/Admin/Models/Users/UserConsent.cs +++ /dev/null @@ -1,13 +0,0 @@ -#pragma warning disable CS1591, CS8618 -namespace Keycloak.AuthServices.Sdk.Admin.Models; - -/// -/// User consent representation. -/// -public class UserConsent -{ - public string? ClientId { get; init; } - public long? CreatedDate { get; init; } - public long? LastUpdatedDate { get; init; } - public string[]? GrantedClientScopes { get; init; } -} diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupRequestParameters.cs b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupRequestParameters.cs index 487ae6ab..3986f2c5 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupRequestParameters.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupRequestParameters.cs @@ -1,7 +1,5 @@ namespace Keycloak.AuthServices.Sdk.Admin.Requests.Groups; -using Refit; - /// /// Optional request parameters for the GetGroups endpoint. /// It can be called in three different ways. @@ -33,38 +31,32 @@ public class GetGroupRequestParameters /// /// Defines whether brief representations are returned. Default is false. /// - [AliasAs("briefRepresentation")] public bool? BriefRepresentation { get; init; } /// /// Defines whether the params LastName, FirstName, /// Email and Username must match exactly /// - [AliasAs("exact")] public bool? Exact { get; init; } /// /// Pagination offset. /// - [AliasAs("first")] public int? First { get; init; } /// /// Maximum results size. Default is 100. /// - [AliasAs("max")] public int? Max { get; init; } /// /// A query to search for custom attributes, in the format "key1:value2 key2:value2". /// - [AliasAs("q")] public string? Query { get; init; } /// /// Search for a string contained in Username, FirstName, /// LastName or Email. /// - [AliasAs("search")] public string? Search { get; init; } } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/GetUsersRequestParameters.cs b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/GetUsersRequestParameters.cs index b0f7f350..063fc805 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/GetUsersRequestParameters.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/GetUsersRequestParameters.cs @@ -1,7 +1,6 @@ namespace Keycloak.AuthServices.Sdk.Admin.Requests.Users; using Models; -using Refit; /// /// Optional request parameters for the endpoint. @@ -34,90 +33,76 @@ public class GetUsersRequestParameters /// /// Defines whether brief representations are returned. Default is false. /// - [AliasAs("briefRepresentation")] public bool? BriefRepresentation { get; init; } /// /// Search for a string contained in , /// or the complete if is true. /// - [AliasAs("email")] public string? Email { get; init; } /// /// Search for whether the email has been verified. /// - [AliasAs("emailVerified")] public bool? EmailVerified { get; init; } /// /// Search for whether the is enabled or not. /// - [AliasAs("enabled")] public bool? Enabled { get; init; } /// /// Defines whether the params , , /// and must match exactly /// - [AliasAs("exact")] public bool? Exact { get; init; } /// /// Pagination offset. /// - [AliasAs("first")] public int? First { get; init; } /// /// Search for a string contained in , /// or the complete if is true. /// - [AliasAs("firstName")] public string? FirstName { get; init; } /// /// Search for the alias of an Identity Provider linked to the . /// - [AliasAs("idpAlias")] public string? IdpAlias { get; init; } /// /// Search for a at an Identity Provider linked to the . /// - [AliasAs("idpUserId")] public string? IdpUserId { get; init; } /// /// Search for a string contained in , /// or the complete if is true. /// - [AliasAs("lastName")] public string? LastName { get; init; } /// /// Maximum results size. Default is 100. /// - [AliasAs("max")] public int? Max { get; init; } /// /// A query to search for custom attributes, in the format "key1:value2 key2:value2". /// - [AliasAs("q")] public string? Query { get; init; } /// /// Search for a string contained in , , /// or . /// - [AliasAs("search")] public string? Search { get; init; } /// /// Search for a string contained in , /// or the complete if is true. /// - [AliasAs("username")] public string? Username { get; init; } } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/ServiceCollectionExtensions.cs b/src/Keycloak.AuthServices.Sdk/Admin/ServiceCollectionExtensions.cs deleted file mode 100644 index d1a3abf3..00000000 --- a/src/Keycloak.AuthServices.Sdk/Admin/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,87 +0,0 @@ -namespace Keycloak.AuthServices.Sdk.Admin; - -using System.Text.Json; -using System.Text.Json.Serialization; -using Common; -using IdentityModel.Client; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Refit; - -/// -/// Adds HTTP Client SDK -/// -public static class ServiceCollectionExtensions -{ - /// - /// Adds keycloak confidential client and underlying token management - /// Configuration is based on conventional configuration registration. - /// - /// - /// - /// - /// v - public static IHttpClientBuilder AddKeycloakAdminHttpClient( - this IServiceCollection services, - KeycloakAdminClientOptions keycloakOptions, - Action? configureClient = default) - { - services.AddAccessTokenManagement(o => - o.Client.Clients.Add("keycloak_admin_api", - new ClientCredentialsTokenRequest - { - RequestUri = new Uri( - $"{keycloakOptions.KeycloakUrlRealm}/{KeycloakConstants.TokenEndpointPath}"), - ClientId = keycloakOptions.Resource, - ClientSecret = keycloakOptions.Credentials.Secret, - })); - - services.AddTransient( - sp => sp.GetRequiredService()); - services.AddTransient( - sp => sp.GetRequiredService()); - services.AddTransient( - sp => sp.GetRequiredService()); - - return services.AddRefitClient(GetKeycloakClientRefitSettings()) - .ConfigureHttpClient(client => - { - var baseUrl = new Uri(keycloakOptions.AuthServerUrl.TrimEnd('/')); - client.BaseAddress = baseUrl; - configureClient?.Invoke(client); - }) - .AddClientAccessTokenHandler("keycloak_admin_api"); - } - - /// - /// Adds keycloak confidential client and underlying token management - /// - /// - /// - /// - /// - /// - public static IHttpClientBuilder AddKeycloakAdminHttpClient( - this IServiceCollection services, - IConfiguration configuration, - Action? configureClient = default, - string? keycloakClientSectionName = default) - { - KeycloakAdminClientOptions options = new(); - - configuration - .GetSection(keycloakClientSectionName ?? KeycloakAdminClientOptions.Section) - .Bind(options, KeycloakFormatBinder.Instance); - - return services.AddKeycloakAdminHttpClient(options, configureClient); - } - - internal static RefitSettings GetKeycloakClientRefitSettings() => - new RefitSettings - { - ContentSerializer = new SystemTextJsonContentSerializer(new JsonSerializerOptions(JsonSerializerDefaults.Web) - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }) - }; -} diff --git a/src/Keycloak.AuthServices.Sdk/HttpResponseExtensions.cs b/src/Keycloak.AuthServices.Sdk/HttpResponseExtensions.cs new file mode 100644 index 00000000..1e97e2a5 --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/HttpResponseExtensions.cs @@ -0,0 +1,30 @@ +namespace Keycloak.AuthServices.Sdk; + +using System.Net.Http.Json; + +/// +/// TBD: +/// +public static class HttpResponseExtensions +{ + /// + /// TBD: + /// + /// + /// + /// + /// + public static async Task GetAsync( + this HttpResponseMessage response, + CancellationToken cancellationToken = default + ) + { + response.EnsureSuccessStatusCode(); + + var result = await response.Content.ReadFromJsonAsync( + cancellationToken: cancellationToken + ); + + return result; + } +} diff --git a/src/Keycloak.AuthServices.Sdk/Keycloak.AuthServices.Sdk.csproj b/src/Keycloak.AuthServices.Sdk/Keycloak.AuthServices.Sdk.csproj index 418e979e..dc5971f1 100644 --- a/src/Keycloak.AuthServices.Sdk/Keycloak.AuthServices.Sdk.csproj +++ b/src/Keycloak.AuthServices.Sdk/Keycloak.AuthServices.Sdk.csproj @@ -15,12 +15,8 @@ - - - + - - diff --git a/src/Keycloak.AuthServices.Sdk/Admin/KeycloakAdminClientOptions.cs b/src/Keycloak.AuthServices.Sdk/KeycloakAdminClientOptions.cs similarity index 68% rename from src/Keycloak.AuthServices.Sdk/Admin/KeycloakAdminClientOptions.cs rename to src/Keycloak.AuthServices.Sdk/KeycloakAdminClientOptions.cs index c945d9d1..817b76f9 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/KeycloakAdminClientOptions.cs +++ b/src/Keycloak.AuthServices.Sdk/KeycloakAdminClientOptions.cs @@ -1,11 +1,11 @@ -namespace Keycloak.AuthServices.Sdk.Admin; +namespace Keycloak.AuthServices.Sdk; using Common; /// /// Defines a set of options used to perform Admin HTTP Client calls /// -public sealed class KeycloakAdminClientOptions: KeycloakInstallationOptions +public sealed class KeycloakAdminClientOptions : KeycloakInstallationOptions { /// /// Default section name diff --git a/src/Keycloak.AuthServices.Sdk/README.md b/src/Keycloak.AuthServices.Sdk/README.md new file mode 100644 index 00000000..5c23616a --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/README.md @@ -0,0 +1,7 @@ +# Keycloak.AuthServices.Sdk + +See: ; + +## Authorization + +Since 2.0.0 the retrieval of access_token is not a concern of this library and should be done based on 3rd parties. E.g: diff --git a/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs b/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..16ee2d5b --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs @@ -0,0 +1,69 @@ +namespace Keycloak.AuthServices.Sdk; + +using Keycloak.AuthServices.Common; +using Keycloak.AuthServices.Sdk.Admin; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +/// +/// Adds HTTP Client SDK +/// +public static class ServiceCollectionExtensions +{ + /// + /// TBD: + /// + /// + /// + /// + /// v + public static IHttpClientBuilder AddKeycloakAdminHttpClient( + this IServiceCollection services, + Action configureKeycloakOptions, + Action? configureClient = default + ) + { + services.Configure(configureKeycloakOptions); + + services.AddTransient(sp => sp.GetRequiredService()); + services.AddTransient(sp => + sp.GetRequiredService() + ); + services.AddTransient(sp => sp.GetRequiredService()); + + return services + .AddHttpClient( + "keycloak_admin_api", + (sp, http) => + { + var keycloakOptions = sp.GetRequiredService< + IOptions + >(); + + http.BaseAddress = new Uri(keycloakOptions.Value.KeycloakUrlRealm); + configureClient?.Invoke(http); + } + ) + .AddTypedClient(); + } + + /// + /// Adds keycloak confidential client and underlying token management + /// + /// + /// + /// + /// + /// + public static IHttpClientBuilder AddKeycloakAdminHttpClient( + this IServiceCollection services, + IConfiguration configuration, + Action? configureClient = default, + string keycloakClientSectionName = KeycloakAdminClientOptions.Section + ) => + services.AddKeycloakAdminHttpClient( + options => configuration.BindKeycloakOptions(options, keycloakClientSectionName), + configureClient + ); +} diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 55fb64ad..345c8c88 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -24,6 +24,7 @@ + diff --git a/tests/Keycloak.AuthServices.IntegrationTests/AddKeycloakWebApiTests.cs b/tests/Keycloak.AuthServices.IntegrationTests/AddKeycloakWebApiTests.cs index 4d56ff30..d0177f87 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/AddKeycloakWebApiTests.cs +++ b/tests/Keycloak.AuthServices.IntegrationTests/AddKeycloakWebApiTests.cs @@ -6,7 +6,6 @@ namespace Keycloak.AuthServices.IntegrationTests; using Keycloak.AuthServices.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.DependencyInjection; -using Xunit.Abstractions; using static Keycloak.AuthServices.IntegrationTests.Utils; public class AddKeycloakWebApiTests(KeycloakFixture fixture, ITestOutputHelper testOutputHelper) @@ -22,7 +21,7 @@ public async Task AddKeycloakWebApi_FromConfiguration_Ok() x => { x.WithLogging(testOutputHelper); - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => @@ -52,7 +51,7 @@ public async Task AddKeycloakWebApi_FromConfigurationSection_Ok() x => { x.WithLogging(testOutputHelper); - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => diff --git a/tests/Keycloak.AuthServices.IntegrationTests/Admin/KeycloakRealmClientTests.cs b/tests/Keycloak.AuthServices.IntegrationTests/Admin/KeycloakRealmClientTests.cs new file mode 100644 index 00000000..2a9b2f6d --- /dev/null +++ b/tests/Keycloak.AuthServices.IntegrationTests/Admin/KeycloakRealmClientTests.cs @@ -0,0 +1,50 @@ +namespace Keycloak.AuthServices.IntegrationTests; + +using Keycloak.AuthServices.Common; +using Keycloak.AuthServices.Sdk; +using Keycloak.AuthServices.Sdk.Admin; +using Microsoft.Extensions.DependencyInjection; +using static Keycloak.AuthServices.IntegrationTests.Utils; + +public class KeycloakRealmClientTests(ITestOutputHelper testOutputHelper) + : AuthenticationScenarioNoKeycloak() +{ + private static readonly string AppSettings = "appsettings.Master.json"; + + [Fact] + public async Task GetRealmAsync_RealmExists_Success() + { + var (services, configuration) = KeycloakSetup(AppSettings, testOutputHelper); + + #region GetRealmAsync_RealmExists_Success + var tokenClientName = "keycloak_admin_api_token"; + + services.AddDistributedMemoryCache(); + services + .AddClientCredentialsTokenManagement() + .AddClient( + tokenClientName, + client => + { + var options = configuration.GetKeycloakOptions()!; + + client.ClientId = options.Resource; + client.ClientSecret = options.Credentials.Secret; + client.TokenEndpoint = options.KeycloakTokenEndpoint; + } + ); + + services + .AddKeycloakAdminHttpClient(configuration) + .AddClientCredentialsTokenHandler(tokenClientName); + + var sp = services.BuildServiceProvider(); + + var client = sp.GetRequiredService(); + + var realm = await client.GetRealmAsync("Test"); + + realm.Should().NotBeNull(); + #endregion GetRealmAsync_RealmExists_Success + } +} diff --git a/tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs b/tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs index 776131c1..19b69934 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs +++ b/tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs @@ -8,7 +8,6 @@ namespace Keycloak.AuthServices.IntegrationTests; using Keycloak.AuthServices.Authorization.AuthorizationServer; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.DependencyInjection; -using Xunit.Abstractions; using static Keycloak.AuthServices.IntegrationTests.Utils; public class AuthorizationServerPolicyTests( @@ -26,7 +25,7 @@ public async Task RequireProtectedResource_DefaultResource_Verified() x => { x.WithLogging(testOutputHelper); - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => @@ -86,7 +85,7 @@ public async Task RequireProtectedResource_Scopes_Verified() x => { x.WithLogging(testOutputHelper); - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => @@ -147,7 +146,7 @@ public async Task RequireProtectedResource_MultipleScopesAllOf_Verified() x => { x.WithLogging(testOutputHelper); - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => @@ -206,7 +205,7 @@ public async Task RequireProtectedResource_MultipleScopesAnyOf_Verified() x => { x.WithLogging(testOutputHelper); - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => @@ -265,7 +264,7 @@ public async Task RequireProtectedResource_MultipleScopesMissingScope_Verified() x => { x.WithLogging(testOutputHelper); - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => diff --git a/tests/Keycloak.AuthServices.IntegrationTests/ConfigurationTests/AddKeycloakWebApiAuthenticationTests.cs b/tests/Keycloak.AuthServices.IntegrationTests/ConfigurationTests/AddKeycloakWebApiAuthenticationTests.cs index 29d32871..fba34b6e 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/ConfigurationTests/AddKeycloakWebApiAuthenticationTests.cs +++ b/tests/Keycloak.AuthServices.IntegrationTests/ConfigurationTests/AddKeycloakWebApiAuthenticationTests.cs @@ -18,7 +18,7 @@ public class AddKeycloakWebApiAuthenticationTests : AuthenticationScenarioNoKeyc private static readonly JwtBearerOptions ExpectedAppSettingsJwtBearerOptions = new() { - Authority = "http://localhost:8080/realms/Test", + Authority = "http://localhost:8080/realms/Test/", Audience = "test-client", RequireHttpsMetadata = false, TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false }, @@ -31,7 +31,7 @@ public async Task AddKeycloakWebApiAuthentication_FromConfiguration_Unauthorized { await using var host = await AlbaHost.For(x => { - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => AddKeycloakWebApiAuthentication_FromConfiguration_Setup( @@ -65,7 +65,7 @@ public async Task AddKeycloakWebApiAuthentication_FromConfiguration2_Unauthorize { await using var host = await AlbaHost.For(x => { - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => AddKeycloakWebApiAuthentication_FromConfiguration2_Setup( @@ -102,7 +102,7 @@ public async Task AddKeycloakWebApiAuthentication_FromConfigurationWithOverrides { await using var host = await AlbaHost.For(x => { - x.UseConfiguration(AppSettingsWithOverrides); + x.WithConfiguration(AppSettingsWithOverrides); x.ConfigureServices( (context, services) => AddKeycloakWebApiAuthentication_FromConfigurationWithOverrides_Setup( @@ -147,7 +147,7 @@ public async Task AddKeycloakWebApiAuthentication_FromConfigurationWithInlineOve { await using var host = await AlbaHost.For(x => { - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => AddKeycloakWebApiAuthentication_FromConfigurationWithInlineOverrides_Setup( @@ -188,7 +188,7 @@ public async Task AddKeycloakWebApiAuthentication_FromConfigurationWithInlineOve { await using var host = await AlbaHost.For(x => { - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => AddKeycloakWebApiAuthentication_FromConfigurationWithInlineOverrides2_Setup( @@ -215,9 +215,7 @@ IConfiguration configuration // #region AddKeycloakWebApiAuthentication_FromConfigurationWithInlineOverrides2 services.AddKeycloakWebApiAuthentication(options => { - configuration - .GetSection(KeycloakAuthenticationOptions.Section) - .Bind(options, KeycloakFormatBinder.Instance); + configuration.BindKeycloakOptions(options); options.SslRequired = "none"; options.Audience = "test-client"; @@ -230,7 +228,7 @@ public async Task AddKeycloakWebApiAuthentication_FromConfigurationSection_Unaut { await using var host = await AlbaHost.For(x => { - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => AddKeycloakWebApiAuthentication_FromConfigurationSection_Setup( diff --git a/tests/Keycloak.AuthServices.IntegrationTests/ConfigurationTests/AddKeycloakWebApiTests.cs b/tests/Keycloak.AuthServices.IntegrationTests/ConfigurationTests/AddKeycloakWebApiTests.cs index 3a7882c8..0486bfa3 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/ConfigurationTests/AddKeycloakWebApiTests.cs +++ b/tests/Keycloak.AuthServices.IntegrationTests/ConfigurationTests/AddKeycloakWebApiTests.cs @@ -15,7 +15,7 @@ public class AddKeycloakWebApiTests : AuthenticationScenarioNoKeycloak private static readonly JwtBearerOptions ExpectedAppSettingsJwtBearerOptions = new() { - Authority = "http://localhost:8080/realms/Test", + Authority = "http://localhost:8080/realms/Test/", Audience = "test-client", RequireHttpsMetadata = false, TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false }, @@ -26,7 +26,7 @@ public async Task AddKeycloakWebApi_FromConfiguration_Unauthorized() { await using var host = await AlbaHost.For(x => { - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => AddKeycloakWebApi_FromConfiguration_Setup(services, context.Configuration) @@ -59,7 +59,7 @@ public async Task AddKeycloakWebApi_FromConfigurationSection_Unauthorized() { await using var host = await AlbaHost.For(x => { - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => AddKeycloakWebApi_FromConfigurationSection_Setup( @@ -95,7 +95,7 @@ public async Task AddKeycloakWebApi_FromInline_Unauthorized() { await using var host = await AlbaHost.For(x => { - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices(AddKeycloakWebApi_FromInline_Setup); }); @@ -129,7 +129,7 @@ public async Task AddKeycloakWebApi_FromInline2_Unauthorized() { await using var host = await AlbaHost.For(x => { - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices(AddKeycloakWebApi_FromInline2_Setup); }); diff --git a/tests/Keycloak.AuthServices.IntegrationTests/Keycloak.AuthServices.IntegrationTests.csproj b/tests/Keycloak.AuthServices.IntegrationTests/Keycloak.AuthServices.IntegrationTests.csproj index eb657ddf..00e68a07 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/Keycloak.AuthServices.IntegrationTests.csproj +++ b/tests/Keycloak.AuthServices.IntegrationTests/Keycloak.AuthServices.IntegrationTests.csproj @@ -7,6 +7,7 @@ + @@ -34,6 +35,7 @@ + diff --git a/tests/Keycloak.AuthServices.IntegrationTests/Playground.cs b/tests/Keycloak.AuthServices.IntegrationTests/Playground.cs index 50e3b16d..ee3e4c94 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/Playground.cs +++ b/tests/Keycloak.AuthServices.IntegrationTests/Playground.cs @@ -23,7 +23,7 @@ public async Task PlaygroundRequireProtectedResource_Scopes_Verified() x => { x.WithLogging(testOutputHelper); - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => diff --git a/tests/Keycloak.AuthServices.IntegrationTests/PolicyTests.cs b/tests/Keycloak.AuthServices.IntegrationTests/PolicyTests.cs index 9c19c7d9..d757e9c0 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/PolicyTests.cs +++ b/tests/Keycloak.AuthServices.IntegrationTests/PolicyTests.cs @@ -24,7 +24,7 @@ public async Task RequireRealmRoles_AdminRole_Verified() x => { x.WithLogging(testOutputHelper); - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => @@ -80,7 +80,7 @@ public async Task RequireClientRoles_TestClientRole_Verified() x => { x.WithLogging(testOutputHelper); - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => @@ -140,7 +140,7 @@ public async Task RequireClientRoles_TestClientRoleWithConfiguration_Verified() x => { x.WithLogging(testOutputHelper); - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => @@ -198,7 +198,7 @@ public async Task RequireRealmRoles_AdminRoleWithMapping_Verified() x => { x.WithLogging(testOutputHelper); - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => @@ -259,7 +259,7 @@ public async Task RequireClientRoles_TestClientRoleWithMapping_Verified() x => { x.WithLogging(testOutputHelper); - x.UseConfiguration(AppSettings); + x.WithConfiguration(AppSettings); x.ConfigureServices( (context, services) => diff --git a/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs b/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs index b9c67544..1a6e9c17 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs +++ b/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs @@ -14,7 +14,7 @@ public static class Utils { - public static IWebHostBuilder UseConfiguration( + public static IWebHostBuilder WithConfiguration( this IWebHostBuilder hostBuilder, string fileName ) => @@ -28,7 +28,7 @@ ITestOutputHelper testOutputHelper ) => hostBuilder.ConfigureLogging(builder => { - builder.SetMinimumLevel(LogLevel.Debug); + builder.SetMinimumLevel(LogLevel.Trace); builder.Services.AddSingleton( new XUnitLoggerProvider( @@ -52,12 +52,45 @@ KeycloakContainer keycloakContainer options.RequireHttpsMetadata = false; } - public static void WithLocalKeycloakInstallation(this JwtBearerOptions options) + public static void WithLocalKeycloakInstallation( + this JwtBearerOptions options, + string realm = "Test" + ) { - options.Authority = $"localhost:8080/realms/Test"; + options.Authority = $"localhost:8080/realms/{realm}"; options.RequireHttpsMetadata = false; } + public static (IServiceCollection services, IConfiguration configuration1) KeycloakSetup( + string fileName, + ITestOutputHelper testOutputHelper + ) + { + var services = new ServiceCollection(); + + var configuration = new ConfigurationBuilder() + .AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), fileName), optional: false) + .Build(); + + services.AddSingleton(configuration); + + services.AddLogging(builder => builder.SetMinimumLevel(LogLevel.Debug)); + + services.AddSingleton( + new XUnitLoggerProvider( + testOutputHelper, + new XUnitLoggerOptions + { + IncludeScopes = true, + IncludeCategory = true, + IncludeLogLevel = true, + } + ) + ); + + return (services, configuration); + } + public static KeycloakAuthenticationOptions ReadKeycloakAuthenticationOptions(string fileName) { var configuration = new ConfigurationBuilder() @@ -66,9 +99,8 @@ public static KeycloakAuthenticationOptions ReadKeycloakAuthenticationOptions(st .AddEnvironmentVariables() .Build(); - var keycloakAuthenticationOptions = configuration - .GetSection(KeycloakAuthenticationOptions.Section) - .Get(KeycloakFormatBinder.Instance)!; + var keycloakAuthenticationOptions = + configuration.GetKeycloakOptions()!; return keycloakAuthenticationOptions; } diff --git a/tests/Keycloak.AuthServices.IntegrationTests/appsettings.Master.json b/tests/Keycloak.AuthServices.IntegrationTests/appsettings.Master.json new file mode 100644 index 00000000..d5afff0d --- /dev/null +++ b/tests/Keycloak.AuthServices.IntegrationTests/appsettings.Master.json @@ -0,0 +1,12 @@ +{ + "Keycloak": { + "realm": "master", + "auth-server-url": "http://localhost:8080/", + "ssl-required": "none", + "resource": "admin-api", + "credentials": { + "secret": "k9LYTWKfbNOyfzFt2ZZsFl3Z4x4aAecf" + }, + "confidential-port": 0 + } +} diff --git a/tests/Keycloak.AuthServices.Sdk.Tests/Admin/KeycloakRealmClientTests.cs b/tests/Keycloak.AuthServices.Sdk.Tests/KeycloakRealmClientTests.cs similarity index 84% rename from tests/Keycloak.AuthServices.Sdk.Tests/Admin/KeycloakRealmClientTests.cs rename to tests/Keycloak.AuthServices.Sdk.Tests/KeycloakRealmClientTests.cs index 4a284a69..d96b4878 100644 --- a/tests/Keycloak.AuthServices.Sdk.Tests/Admin/KeycloakRealmClientTests.cs +++ b/tests/Keycloak.AuthServices.Sdk.Tests/KeycloakRealmClientTests.cs @@ -1,7 +1,6 @@ -namespace Keycloak.AuthServices.Sdk.Tests.Admin; +namespace Keycloak.AuthServices.Sdk.Tests; using System.Net; -using Refit; using RichardSzalay.MockHttp; using Sdk.Admin; @@ -25,9 +24,10 @@ public KeycloakRealmClientTests() [Fact] public async Task GetRealmShouldCallRealmEndpoint() { - this.handler.Expect(HttpMethod.Get, $"{BaseAddress}/admin/realms/master").Respond(HttpStatusCode.OK); + this.handler.Expect(HttpMethod.Get, $"{BaseAddress}/admin/realms/master") + .Respond(HttpStatusCode.OK); - await this.keycloakRealmClient.GetRealm("master"); + await this.keycloakRealmClient.GetRealmAsync("master"); this.handler.VerifyNoOutstandingExpectation(); } @@ -39,7 +39,8 @@ public async Task GetRealmShouldThrowNotFoundApiExceptionWhenRealmDoesNotExist() .Respond(HttpStatusCode.NotFound); var exception = await Assert.ThrowsAsync( - () => this.keycloakRealmClient.GetRealm("nonexistent")); + () => this.keycloakRealmClient.GetRealmAsync("nonexistent") + ); Assert.Equal(HttpStatusCode.NotFound, exception.StatusCode); this.handler.VerifyNoOutstandingExpectation(); diff --git a/tests/Keycloak.AuthServices.Sdk.Tests/Admin/KeycloakUserClientTests.cs b/tests/Keycloak.AuthServices.Sdk.Tests/KeycloakUserClientTests.cs similarity index 73% rename from tests/Keycloak.AuthServices.Sdk.Tests/Admin/KeycloakUserClientTests.cs rename to tests/Keycloak.AuthServices.Sdk.Tests/KeycloakUserClientTests.cs index 1e97ced3..c230da76 100644 --- a/tests/Keycloak.AuthServices.Sdk.Tests/Admin/KeycloakUserClientTests.cs +++ b/tests/Keycloak.AuthServices.Sdk.Tests/KeycloakUserClientTests.cs @@ -1,10 +1,9 @@ -namespace Keycloak.AuthServices.Sdk.Tests.Admin; +namespace Keycloak.AuthServices.Sdk.Tests; using System.Net; using Extensions; using FluentAssertions; -using Microsoft.AspNetCore.Http.Extensions; -using Refit; +using Keycloak.AuthServices.Sdk; using RichardSzalay.MockHttp; using Sdk.Admin; using Sdk.Admin.Models; @@ -24,8 +23,10 @@ public KeycloakUserClientTests() var httpClient = this.handler.ToHttpClient(); httpClient.BaseAddress = new Uri(BaseAddress); - this.keycloakUserClient = RestService.For(httpClient, - ServiceCollectionExtensions.GetKeycloakClientRefitSettings()); + this.keycloakUserClient = RestService.For( + httpClient, + ServiceCollectionExtensions.GetKeycloakClientRefitSettings() + ); } [Fact] @@ -48,14 +49,16 @@ public async Task GetUserShouldCallCorrectEndpoint() public async Task GetUserShouldShouldThrowNotFoundApiExceptionWhenUserDoesNotExist() { var userId = Guid.NewGuid(); - const string errorMessage = /*lang=json,strict*/ "{\"error\":\"User not found\"}"; + const string errorMessage = /*lang=json,strict*/ + "{\"error\":\"User not found\"}"; this.handler.Expect(HttpMethod.Get, $"{BaseAddress}/admin/realms/master/users/{userId}") .WithAcceptHeader() .Respond(HttpStatusCode.NotFound, "application/json", errorMessage); var exception = await Assert.ThrowsAsync( - () => this.keycloakUserClient.GetUser("master", userId.ToString())); + () => this.keycloakUserClient.GetUser("master", userId.ToString()) + ); exception.StatusCode.Should().Be(HttpStatusCode.NotFound); exception.Content.Should().Be(errorMessage); @@ -65,11 +68,14 @@ public async Task GetUserShouldShouldThrowNotFoundApiExceptionWhenUserDoesNotExi [Fact] public async Task GetUsersShouldCallCorrectEndpoint() { - var users = Enumerable.Range(0, 3).Select(_ => - { - var id = Guid.NewGuid(); - return (Id: id.ToString(), Representation: GetUserRepresentation(id)); - }).ToArray(); + var users = Enumerable + .Range(0, 3) + .Select(_ => + { + var id = Guid.NewGuid(); + return (Id: id.ToString(), Representation: GetUserRepresentation(id)); + }) + .ToArray(); var response = $"[{string.Join(",", users.Select(u => u.Representation))}]"; @@ -107,20 +113,20 @@ public async Task GetUsersShouldCallCorrectEndpointWithOptionalQueryParameters() var url = $"{BaseAddress}/admin/realms/master/users"; var queryBuilder = new QueryBuilder { - {"briefRepresentation", "False"}, - {"email", "email"}, - {"emailVerified", "False"}, - {"enabled", "False"}, - {"exact", "False"}, - {"first", "0"}, - {"firstName", "firstName"}, - {"idpAlias", "idpAlias"}, - {"idpUserId", "idpUserId"}, - {"lastName", "lastName"}, - {"max", "1"}, - {"q", "key1:value2 key2:value2"}, - {"search", "search"}, - {"username", "username"} + { "briefRepresentation", "False" }, + { "email", "email" }, + { "emailVerified", "False" }, + { "enabled", "False" }, + { "exact", "False" }, + { "first", "0" }, + { "firstName", "firstName" }, + { "idpAlias", "idpAlias" }, + { "idpUserId", "idpUserId" }, + { "lastName", "lastName" }, + { "max", "1" }, + { "q", "key1:value2 key2:value2" }, + { "search", "search" }, + { "username", "username" } }; var response = $"[{GetUserRepresentation(Guid.NewGuid())}]"; @@ -141,10 +147,10 @@ public async Task CreateUserShouldCallCorrectEndpoint() .WithAcceptAndContentTypeHeaders() .Respond(HttpStatusCode.Created); - await this.keycloakUserClient.CreateUser("master", new() - { - Username = "email@example.com" - }); + await this.keycloakUserClient.CreateUser( + "master", + new() { Username = "email@example.com" } + ); this.handler.VerifyNoOutstandingExpectation(); } @@ -152,7 +158,8 @@ public async Task CreateUserShouldCallCorrectEndpoint() [Fact] public async Task CreateUserShouldReturnBadRequestWhenRequestIsInvalid() { - const string errorMessage = /*lang=json,strict*/ "{\"errorMessage\":\"User name is missing\"}"; + const string errorMessage = /*lang=json,strict*/ + "{\"errorMessage\":\"User name is missing\"}"; this.handler.Expect(HttpMethod.Post, $"{BaseAddress}/admin/realms/master/users") .WithAcceptAndContentTypeHeaders() @@ -173,13 +180,16 @@ public async Task UpdateUserShouldCallCorrectEndpoint() this.handler.Expect(HttpMethod.Put, $"{BaseAddress}/admin/realms/master/users/{userId}") .WithAcceptAndContentTypeHeaders() - .WithContent(/*lang=json,strict*/ "{\"firstName\":\"FirstName\"}") + .WithContent( /*lang=json,strict*/ + "{\"firstName\":\"FirstName\"}" + ) .Respond(HttpStatusCode.NoContent); - await this.keycloakUserClient.UpdateUser("master", userId.ToString(), new() - { - FirstName = "FirstName" - }); + await this.keycloakUserClient.UpdateUser( + "master", + userId.ToString(), + new() { FirstName = "FirstName" } + ); this.handler.VerifyNoOutstandingExpectation(); } @@ -188,14 +198,16 @@ public async Task UpdateUserShouldCallCorrectEndpoint() public async Task UpdateUserShouldThrowNotFoundApiExceptionWhenUserDoesNotExist() { var userId = Guid.NewGuid(); - const string errorMessage = /*lang=json,strict*/ "{\"errorMessage\":\"User name is missing\"}"; + const string errorMessage = /*lang=json,strict*/ + "{\"errorMessage\":\"User name is missing\"}"; this.handler.Expect(HttpMethod.Put, $"{BaseAddress}/admin/realms/master/users/{userId}") .WithAcceptAndContentTypeHeaders() .Respond(HttpStatusCode.NotFound, "application/json", errorMessage); - var exception = await Assert.ThrowsAsync(() => - this.keycloakUserClient.UpdateUser("master", userId.ToString(), new User())); + var exception = await Assert.ThrowsAsync( + () => this.keycloakUserClient.UpdateUser("master", userId.ToString(), new User()) + ); exception.StatusCode.Should().Be(HttpStatusCode.NotFound); exception.Content.Should().Be(errorMessage); @@ -221,14 +233,19 @@ public async Task DeleteUserShouldCallCorrectEndpoint() public async Task DeleteUserShouldThrowNotFoundApiExceptionWhenUserDoesNotExist() { var userId = Guid.NewGuid(); - const string errorMessage = /*lang=json,strict*/ "{\"errorMessage\":\"User name is missing\"}"; + const string errorMessage = /*lang=json,strict*/ + "{\"errorMessage\":\"User name is missing\"}"; - this.handler.Expect(HttpMethod.Delete, $"{BaseAddress}/admin/realms/master/users/{userId.ToString()}") + this.handler.Expect( + HttpMethod.Delete, + $"{BaseAddress}/admin/realms/master/users/{userId.ToString()}" + ) .WithAcceptAndContentTypeHeaders() .Respond(HttpStatusCode.BadRequest, "application/json", errorMessage); - var exception = await Assert.ThrowsAsync(() => - this.keycloakUserClient.DeleteUser("master", userId.ToString())); + var exception = await Assert.ThrowsAsync( + () => this.keycloakUserClient.DeleteUser("master", userId.ToString()) + ); exception.StatusCode.Should().Be(HttpStatusCode.BadRequest); exception.Content.Should().Be(errorMessage); @@ -240,7 +257,10 @@ public async Task SendVerifyEmailShouldCallCorrectEndpoint() { var userId = Guid.NewGuid(); - this.handler.Expect(HttpMethod.Put, $"{BaseAddress}/admin/realms/master/users/{userId}/send-verify-email") + this.handler.Expect( + HttpMethod.Put, + $"{BaseAddress}/admin/realms/master/users/{userId}/send-verify-email" + ) .WithAcceptHeader() .Respond(HttpStatusCode.NoContent); @@ -256,15 +276,21 @@ public async Task SendVerifyEmailShouldCallCorrectEndpointWithOptionalQueryParam const string clientId = "client-id"; const string redirectUri = "https://localhost:5001"; - var url = $"{BaseAddress}/admin/realms/master/users/{userId}/send-verify-email" - + $"?client_id={clientId}" - + $"&redirect_uri={redirectUri}"; + var url = + $"{BaseAddress}/admin/realms/master/users/{userId}/send-verify-email" + + $"?client_id={clientId}" + + $"&redirect_uri={redirectUri}"; this.handler.Expect(HttpMethod.Put, url) .WithAcceptHeader() .Respond(HttpStatusCode.NoContent); - await this.keycloakUserClient.SendVerifyEmail("master", userId.ToString(), clientId, redirectUri); + await this.keycloakUserClient.SendVerifyEmail( + "master", + userId.ToString(), + clientId, + redirectUri + ); this.handler.VerifyNoOutstandingExpectation(); } @@ -273,14 +299,19 @@ public async Task SendVerifyEmailShouldCallCorrectEndpointWithOptionalQueryParam public async Task SendVerifyEmailShouldThrowNotFoundApiExceptionWhenUserDoesNotExist() { var userId = Guid.NewGuid(); - const string errorMessage = /*lang=json,strict*/ "{\"error\":\"User not found\"}"; + const string errorMessage = /*lang=json,strict*/ + "{\"error\":\"User not found\"}"; - this.handler.Expect(HttpMethod.Put, $"{BaseAddress}/admin/realms/master/users/{userId}/send-verify-email") + this.handler.Expect( + HttpMethod.Put, + $"{BaseAddress}/admin/realms/master/users/{userId}/send-verify-email" + ) .WithAcceptHeader() .Respond(HttpStatusCode.NotFound, "application/json", errorMessage); var exception = await Assert.ThrowsAsync( - () => this.keycloakUserClient.SendVerifyEmail("master", userId.ToString())); + () => this.keycloakUserClient.SendVerifyEmail("master", userId.ToString()) + ); exception.StatusCode.Should().Be(HttpStatusCode.NotFound); exception.Content.Should().Be(errorMessage); @@ -288,7 +319,7 @@ public async Task SendVerifyEmailShouldThrowNotFoundApiExceptionWhenUserDoesNotE } private static string GetUserRepresentation(Guid userId) => - /*lang=json,strict*/ $@"{{ + /*lang=json,strict*/$@"{{ ""id"": ""{userId}"", ""createdTimestamp"": 1670000000000, ""username"": ""email@domain.com"", From 3025662a81352d2085e6c8ae2cee7b4b7e608aca Mon Sep 17 00:00:00 2001 From: nikiforovall Date: Fri, 3 May 2024 15:50:43 +0300 Subject: [PATCH 2/6] Add access token management docs; Add realm client --- README.md | 2 + docs/.vitepress/config.mts | 23 + docs/admin-rest-api.md | 3 - docs/admin-rest-api/access-token.md | 46 ++ docs/admin-rest-api/admin-api-openapi.md | 7 + docs/admin-rest-api/admin-api-reference.md | 14 + docs/admin-rest-api/admin-api.spec.md | 120 ++++ docs/admin-rest-api/admin-rest-api.md | 81 +++ docs/admin-rest-api/group-client.md | 3 + .../protected-resource-client.md | 3 + docs/admin-rest-api/realm-client.md | 3 + docs/admin-rest-api/user-client.md | 3 + .../configuration-authentication.md | 2 +- .../configuration-authorization.md | 2 +- docs/index.md | 10 +- docs/migration.md | 56 +- docs/public/apple-touch-icon.png | 3 + docs/public/favicon-16x16.png | 3 + docs/public/favicon-32x32.png | 3 + docs/public/favicon.ico | 3 + docs/public/icon.png | 3 - docs/public/logo.png | 3 - docs/public/site.webmanifest | 14 + .../PoliciesBuilderExtensions.cs | 8 +- ...ycloakClientApiConstants.cs => ApiUrls.cs} | 7 +- .../Admin/IKeycloakClient.cs | 3 +- .../Admin/IKeycloakRealmClient.cs | 2 +- .../Admin/IKeycloakUserClient.cs | 134 +++- .../Admin/KeycloakClient.cs | 87 ++- .../Admin/Models/Contracts.cs | 60 +- .../Users/GetUsersRequestParameters.cs | 33 +- .../ErrorResponse.cs | 21 + .../HttpResponseExtensions.cs | 37 +- .../ServiceCollectionExtensions.cs | 28 + .../Utils/QueryBuilder.cs | 677 ++++++++++++++++++ tests/.editorconfig | 6 + .../Keycloak.AuthServices.Common.Tests.csproj | 6 +- .../Admin/KeycloakRealmClientTests.cs | 96 ++- .../PolicyTests.cs | 4 +- .../Utils.cs | 2 +- .../Extensions/MockedRequestExtensions.cs | 12 +- .../Keycloak.AuthServices.Sdk.Tests.csproj | 8 + .../KeycloakRealmClientTests.cs | 29 +- .../KeycloakUserClientTests.cs | 397 +++++----- 44 files changed, 1668 insertions(+), 399 deletions(-) delete mode 100644 docs/admin-rest-api.md create mode 100644 docs/admin-rest-api/access-token.md create mode 100644 docs/admin-rest-api/admin-api-openapi.md create mode 100644 docs/admin-rest-api/admin-api-reference.md create mode 100644 docs/admin-rest-api/admin-api.spec.md create mode 100644 docs/admin-rest-api/admin-rest-api.md create mode 100644 docs/admin-rest-api/group-client.md create mode 100644 docs/admin-rest-api/protected-resource-client.md create mode 100644 docs/admin-rest-api/realm-client.md create mode 100644 docs/admin-rest-api/user-client.md create mode 100644 docs/public/apple-touch-icon.png create mode 100644 docs/public/favicon-16x16.png create mode 100644 docs/public/favicon-32x32.png create mode 100644 docs/public/favicon.ico delete mode 100644 docs/public/icon.png delete mode 100644 docs/public/logo.png create mode 100644 docs/public/site.webmanifest rename src/Keycloak.AuthServices.Sdk/Admin/{Constants/KeycloakClientApiConstants.cs => ApiUrls.cs} (93%) create mode 100644 src/Keycloak.AuthServices.Sdk/ErrorResponse.cs create mode 100644 src/Keycloak.AuthServices.Sdk/Utils/QueryBuilder.cs diff --git a/README.md b/README.md index ca502d92..c776dc12 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,8 @@ app.Run(); `dotnet cake --target build` +`dotnet cake --target test` + `dotnet pack -o ./Artefacts` ## Blog Posts diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 45b2c5a8..cf482218 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -6,6 +6,11 @@ export default withMermaid({ title: "Keycloak.AuthServices", description: "", base: '/keycloak-authorization-services-dotnet/', + head: [ + ["link", { rel: "icon", type: "image/png", sizes: "16x16", href: "/favicon-16x16.png" }], + ["link", { rel: "icon", type: "image/png", sizes: "32x32", href: "/favicon-32x32.png" }], + ["link", { rel: "manifest", href: "/site.webmanifest" }] + ], themeConfig: { logo: '/logo.svg', // https://vitepress.dev/reference/default-theme-config @@ -42,6 +47,24 @@ export default withMermaid({ { text: 'Protected Resources ✨', link: '/authorization/resources' }, ] }, + { + text: 'Admin REST API ⚙️', + collapsed: true, + items: [ + { text: 'Introduction', link: '/admin-rest-api/admin-rest-api' }, + { text: 'Access Token Management', link: '/admin-rest-api/access-token' }, + { + text: 'Admin API Reference', link: '/admin-rest-api/admin-api-reference', + items: [ + { text: 'Realm Client', link: '/admin-rest-api/realm-client' }, + { text: 'User Client', link: '/admin-rest-api/user-client' }, + { text: 'Group Client', link: '/admin-rest-api/group-client' }, + { text: 'Protected Resource Client', link: '/admin-rest-api/protected-resource-client' }, + ] + }, + { text: 'OpenAPI Support', link: '/admin-rest-api/admin-api-openapi' }, + ] + }, { text: 'Examples', collapsed: false, diff --git a/docs/admin-rest-api.md b/docs/admin-rest-api.md deleted file mode 100644 index 9e1055c5..00000000 --- a/docs/admin-rest-api.md +++ /dev/null @@ -1,3 +0,0 @@ -# Admin REST HTTP API - -🚧👋 Come back later diff --git a/docs/admin-rest-api/access-token.md b/docs/admin-rest-api/access-token.md new file mode 100644 index 00000000..9b1ea5e0 --- /dev/null +++ b/docs/admin-rest-api/access-token.md @@ -0,0 +1,46 @@ +# Access Token Management + +Keycloak comes with a fully functional Admin REST API with all features provided by the Admin Console. To invoke the API you need to obtain an access token with the appropriate permissions. + +Please refer to [official docs](https://www.keycloak.org/docs/latest/server_development/#authenticating-with-a-service-account) for more details on how to setup Service Account. + +## Configure Service Account + +We need to create a *Service Account* in **master** realm, configure a special *Audience Mapper* that adds **security-admin-console** audience to the token, and assign *Service Account Role* - **"admin"**. + +Create a service account client called **"admin-api"** and enable *Client Authentication* and *Service Account Roles*. + +Then, [download adapter config](/configuration/configuration-keycloak#download-adapter-config) from Keycloak and added it to "appsettings.json" to "Keycloak section. Here is how it looks like: + +```json +{ + "Keycloak": { + "realm": "master", + "auth-server-url": "http://localhost:8080/", + "ssl-required": "none", + "resource": "admin-api", + "credentials": { + "secret": "k9LYTWKfbNOyfzFt2ZZsFl3Z4x4aAecf" + }, + "confidential-port": 0 + } +} +``` + +💡 See [admin-api export file](/admin-rest-api/admin-api.spec) if you want to import it and see how it looks in Keycloak. + +## Add Token Management + +Luckily, there is a production-ready library called [DuendeSoftware/Duende.AccessTokenManagement](https://github.com/DuendeSoftware/Duende.AccessTokenManagement) that retrieves and caches tokens. + +To install it run: + +```bash +dotnet add package Duende.AccessTokenManagement +``` + +See [the docs](https://github.com/DuendeSoftware/Duende.AccessTokenManagement/wiki/worker-applications) on how to configure and use this library to use Service Accounts. + +### Example + +<<< @/../tests/Keycloak.AuthServices.IntegrationTests/Admin/KeycloakRealmClientTests.cs#GetRealmAsync_RealmExists_Success {3-14,18 cs:line-numbers} diff --git a/docs/admin-rest-api/admin-api-openapi.md b/docs/admin-rest-api/admin-api-openapi.md new file mode 100644 index 00000000..93e025ee --- /dev/null +++ b/docs/admin-rest-api/admin-api-openapi.md @@ -0,0 +1,7 @@ +# OpenAPI Support + +From Keycloak documentation: + +> The OpenAPI definitions are a feature that is currently in preview. Please provide your feedback by [joining this discussion](https://github.com/keycloak/keycloak/discussions/8898) while we’re continuing to work on this. If you find something is outdated or wrong, create a GitHub issue and provide a pull request. + +It means we can use OpenAPI definitions to generate full-fledged API. diff --git a/docs/admin-rest-api/admin-api-reference.md b/docs/admin-rest-api/admin-api-reference.md new file mode 100644 index 00000000..13bddcb9 --- /dev/null +++ b/docs/admin-rest-api/admin-api-reference.md @@ -0,0 +1,14 @@ +# Admin API Reference + +[Keycloak.AuthServices.Sdk](https://www.nuget.org/packages/Keycloak.AuthServices.Sdk/) provides a basic support for common and most popular API endpoints. + +The full API documentation see . + +>[!IMPORTANT] +> **Keycloak.AuthServices** is an open source project with limited developer capacity. Many API endpoints might be missing. But **contributions are encouraged**! +> +> [![contributionswelcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/nikiforovall/keycloak-authorization-services-dotnet) + +💡**Alternatively**, you may want to use OpenAPI definition for Keycloak to generate a client based on your generator of choice - see [OpenAPI Support](/admin-rest-api/admin-api-openapi) for more details. + +<<< @/../src/Keycloak.AuthServices.Sdk\Admin\IKeycloakClient.cs diff --git a/docs/admin-rest-api/admin-api.spec.md b/docs/admin-rest-api/admin-api.spec.md new file mode 100644 index 00000000..778fedec --- /dev/null +++ b/docs/admin-rest-api/admin-api.spec.md @@ -0,0 +1,120 @@ +# admin-api (Service Client) + +Here is an configuration file for admin-api client. Note, you might need to assign master realm roles after export separately. + +```json +{ + "clientId": "admin-api", // [!code highlight] + "name": "", + "description": "", + "rootUrl": "", + "adminUrl": "", + "baseUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "k9LYTWKfbNOyfzFt2ZZsFl3Z4x4aAecf", // [!code highlight] + "redirectUris": [ + "/*" + ], + "webOrigins": [ + "/*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "oidc.ciba.grant.enabled": "false", + "oauth2.device.authorization.grant.enabled": "false", + "client.secret.creation.time": "1714665890", + "backchannel.logout.session.required": "true", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "introspection.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + }, + { + "name": "Audience", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "security-admin-console", // [!code highlight] + "id.token.claim": "false", + "lightweight.claim": "false", + "introspection.token.claim": "true", + "access.token.claim": "true" + } + }, + { + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "client_id", + "introspection.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "client_id", + "jsonType.label": "String" + } + }, + { + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "introspection.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ], + "access": { + "view": true, + "configure": true, + "manage": true + } +} +``` diff --git a/docs/admin-rest-api/admin-rest-api.md b/docs/admin-rest-api/admin-rest-api.md new file mode 100644 index 00000000..4e7016c4 --- /dev/null +++ b/docs/admin-rest-api/admin-rest-api.md @@ -0,0 +1,81 @@ +# HTTP Admin REST API + +[Keycloak.AuthServices.Sdk](https://www.nuget.org/packages/Keycloak.AuthServices.Sdk) provides a typed HTTP Client to work with Keycloak Admin HTTP REST API. + + +The Admin REST API in Keycloak provides a programmatic way to manage and administer Keycloak instances. It allows you to perform various administrative tasks such as creating and managing realms, users, roles, clients, and more. To interact with the Admin REST API, you can use HTTP requests to send commands and retrieve data. The API follows the REST architectural style and is designed to be simple and intuitive to use. + +> [!NOTE] +> See full list of API endpoints - [Admin REST API](https://www.keycloak.org/docs-api/24.0.1/rest-api/#_overview) + +Keycloak provides a comprehensive set of endpoints that cover a wide range of administrative operations. These endpoints are organized into different resource types, such as realms, users, roles, and clients, making it easy to navigate and manipulate the Keycloak configuration. + +❗ To get started with the Admin REST API, you need to authenticate and obtain an access token. Once you have the token, you can include it in the Authorization header of your HTTP requests to authenticate and authorize your API calls. + +> [!NOTE] +> See [Admin REST API - Server Development](https://www.keycloak.org/docs/latest/server_development/#admin-rest-api) documentation for more details. + +## Add to your code + +Install [Keycloak.AuthServices.Sdk](https://www.nuget.org/packages/Keycloak.AuthServices.Sdk): + +```bash +dotnet add package Keycloak.AuthServices.Sdk +``` + +> [!IMPORTANT] +> Admin API is protected so you need to acquire access token somehow. See [Access Token Management](/admin-rest-api/access-token) + +You can use `IKeycloakClient` from Web APIs, Worker, Console apps, etc. It is fully integrated with [IHttpClientFactory](https://learn.microsoft.com/en-us/dotnet/core/extensions/httpclient-factory) and therefore you don't need to worry about `HttpClient` lifetime and the way you work with it. + +To add it to DI, you can use convenience extensions method `AddKeycloakAdminHttpClient`: + +```csharp +public static IHttpClientBuilder AddKeycloakAdminHttpClient( + this IServiceCollection services, + KeycloakAdminClientOptions keycloakAdminClientOptions, + Action? configureClient = default +) +``` + +It registers typed client with umbrella interface `IKeycloakClient` and adds `KeycloakAdminClientOptions` to DI so you can use it as `IOptions` in your code. + +> [!NOTE] +> 💡 `AddKeycloakAdminHttpClient` returns `IHttpClientBuilder` so you can proceed and configure underlying `HttpClient`. +> +> For example, here is how to add Polly and some custom delegating handlers: +>```csharp +> services +> .AddKeycloakAdminHttpClient(configuration) +> .AddStandardResilienceHandler() +> .AddHttpMessageHandler() +> .AddHttpMessageHandler(); +>``` + +## Console App + +Here is how to use it from a Console App: + +```csharp +var services = new ServiceCollection(); + +var keycloakOptions = new KeycloakAdminClientOptions +{ + AuthServerUrl = "http://localhost:8080/", + Realm = "master", + Resource = "admin-api", +}; +services.AddKeycloakAdminHttpClient(keycloakOptions); + +var sp = services.BuildServiceProvider(); +var client = sp.GetRequiredService(); + +var realm = await client.GetRealmAsync("Test"); +``` + +> [!WARNING] +> In the code above the **key part** is missing - Authentication and Authorization. Because of that, you will receive **401 (Unauthorized)**. In the [next section](/admin-rest-api/access-token) I will show you how to obtain access token and successfully invoke Admin API endpoints. + +Here is `IKeycloakClient`: + +<<< @/../src/Keycloak.AuthServices.Sdk\Admin\IKeycloakClient.cs diff --git a/docs/admin-rest-api/group-client.md b/docs/admin-rest-api/group-client.md new file mode 100644 index 00000000..4d8ecfdc --- /dev/null +++ b/docs/admin-rest-api/group-client.md @@ -0,0 +1,3 @@ +# IKeycloakGroupClient + +<<< @/../src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs diff --git a/docs/admin-rest-api/protected-resource-client.md b/docs/admin-rest-api/protected-resource-client.md new file mode 100644 index 00000000..c0d63c14 --- /dev/null +++ b/docs/admin-rest-api/protected-resource-client.md @@ -0,0 +1,3 @@ +# IKeycloakProtectedResourceClient + +<<< @/../src/Keycloak.AuthServices.Sdk/Admin/IKeycloakProtectedResourceClient.cs diff --git a/docs/admin-rest-api/realm-client.md b/docs/admin-rest-api/realm-client.md new file mode 100644 index 00000000..f0d67c17 --- /dev/null +++ b/docs/admin-rest-api/realm-client.md @@ -0,0 +1,3 @@ +# IKeycloakRealmClient + +<<< @/../src/Keycloak.AuthServices.Sdk/Admin/IKeycloakRealmClient.cs diff --git a/docs/admin-rest-api/user-client.md b/docs/admin-rest-api/user-client.md new file mode 100644 index 00000000..0ef717ef --- /dev/null +++ b/docs/admin-rest-api/user-client.md @@ -0,0 +1,3 @@ +# IKeycloakUserClient + +<<< @/../src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs diff --git a/docs/configuration/configuration-authentication.md b/docs/configuration/configuration-authentication.md index 3c775c96..8faf63b9 100644 --- a/docs/configuration/configuration-authentication.md +++ b/docs/configuration/configuration-authentication.md @@ -1,6 +1,6 @@ # Configure Authentication -**Keycloak.AuthServices.Authentication** provides robust authentication mechanisms for both web APIs and web applications. For web APIs, it supports JWT Bearer token authentication, which allows clients to authenticate to the API by providing a JWT token in the Authorization header of their requests. For web applications, it supports OpenID Connect, a simple identity layer on top of the OAuth 2.0 protocol +[Keycloak.AuthServices.Authentication](https://www.nuget.org/packages/Keycloak.AuthServices.Authentication) provides robust authentication mechanisms for both web APIs and web applications. For web APIs, it supports JWT Bearer token authentication, which allows clients to authenticate to the API by providing a JWT token in the Authorization header of their requests. For web applications, it supports OpenID Connect, a simple identity layer on top of the OAuth 2.0 protocol --- diff --git a/docs/configuration/configuration-authorization.md b/docs/configuration/configuration-authorization.md index 0dc24375..34afd092 100644 --- a/docs/configuration/configuration-authorization.md +++ b/docs/configuration/configuration-authorization.md @@ -2,7 +2,7 @@ *RBAC* (Role-Based Access Control) is a widely used authorization model in software applications. It provides a way to control access to resources based on the roles assigned to users. Keycloak, an open-source identity and access management solution, offers robust support for RBAC. -With Keycloak, you can configure roles by defining realm roles and resource roles. Realm roles are global roles that apply to the entire realm, while resource roles are specific to a particular client or resource. +With [Keycloak.AuthServices.Authorization](https://www.nuget.org/packages/Keycloak.AuthServices.Authorization), you can configure roles by defining realm roles and resource roles. Realm roles are global roles that apply to the entire realm, while resource roles are specific to a particular client or resource. *Table of Contents*: [[toc]] diff --git a/docs/index.md b/docs/index.md index d2fc1b73..01665317 100644 --- a/docs/index.md +++ b/docs/index.md @@ -16,14 +16,14 @@ hero: text: Authorization link: /authorization/authorization-server - theme: alt - text: HTTP REST Admin API - link: /admin-rest-api + text: HTTP Admin REST API + link: /admin-rest-api/admin-rest-api features: - title: 🔒Authentication - details: Keycloak.AuthServices provides robust authentication mechanisms for both web APIs and web applications. For web APIs, it supports JWT Bearer token authentication, which allows clients to authenticate to the API by providing a JWT token in the Authorization header of their requests. For web applications, it supports OpenID Connect, a simple identity layer on top of the OAuth 2.0 protocol, which allows clients to verify the identity of the end-user based on the authentication performed by an authorization server, as well as to obtain basic profile information about the end-user. + details: Keycloak.AuthServices provides robust authentication mechanisms for both web APIs and web applications. For web APIs, it supports JWT Bearer token authentication, which allows clients to authenticate to the API by providing a JWT token in the Authorization header of their requests. For web applications, it supports OpenID Connect, a simple identity layer on top of the OAuth 2.0 protocol, which allows clients to verify the identity of the end-user, obtain basic profile information about the end-user, etc. - title: 🗝️Authorization details: Keycloak.AuthServices allows authorization based role-based access control (RBAC). It also enables Keycloak to function as an Authorization Server, enforcing execution policies based on permissions of authenticated users. This ensures secure access to resources, with the ability to define fine-grained permissions and policies. - - title: ⚙️ HTTP REST Admin API integration - details: Keycloak.AuthServices includes an SDK client that integrates with the Keycloak Admin HTTP REST API. This allows developers to manage and configure Keycloak instances programmatically, providing a high degree of flexibility and automation. + - title: ⚙️ HTTP Admin REST API integration + details: Keycloak.AuthServices includes an SDK client that integrates with the Keycloak Admin API. This allows developers to manage and configure Keycloak instances programmatically, providing a high degree of flexibility and automation. --- diff --git a/docs/migration.md b/docs/migration.md index f82ab8e2..96d70c1b 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -2,6 +2,8 @@ ## Key Changes in 2.0.0 +* Breaking change 💥: Lot's of changes `Keycloak.AuthServices.Sdk` - API has changed, no backward compatibility. +* Breaking change 💥: Removed dependencies on `Refit` and `IdentityModel.AspNetCore`. Tokens are no longer managed by this library and **you need to configure it separately**. * `RolesClaimTransformationSource` changed to `None` from `ResourceAccess` meaning we no longer map to `AspNetCore` roles by default. Renamed to `EnableRolesMapping`. Moved to `Keycloak.AuthServices.Authorization`. * Moved `IKeycloakProtectionClient` to `Keycloak.AuthServices.Authorization`. Removed `AddKeycloakProtectionHttpClient`, added `AddAuthorizationServer` instead. @@ -14,60 +16,11 @@ * Dropped namespace `Keycloak.AuthServices.Sdk.AuthZ` * `AddKeycloakAuthentication` has been deprecated in favor of `AddKeycloakWebApiAuthentication`. -* Breaking change 💥: Changed default Configuration format from kebab-case to PascalCase: - -It is possible to retrieve configuration by specifying special option `KeycloakFormatBinder.Instance`. The tests below explain the changes: - -```csharp -private static readonly KeycloakInstallationOptions Expected = - new() - { - Realm = "Test", - AuthServerUrl = "http://localhost:8080/", - SslRequired = "none", - Resource = "test-client", - VerifyTokenAudience = true, - Credentials = new() { Secret = "secret" }, - }; - -[Fact] -public void TestKebabCaseNotation() -{ - var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); - - var authenticationOptions = configuration - .GetSection("Keycloak1") - .Get(KeycloakFormatBinder.Instance); - - authenticationOptions.Should().BeEquivalentTo(Expected); -} - -[Fact] -public void TestKebabCaseNotationWithExtensionMethod() -{ - var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); - - var authenticationOptions = configuration - .GetKeycloakOptions("Keycloak1"); - - authenticationOptions.Should().BeEquivalentTo(Expected); -} - -[Fact] -public void TestPascalCaseNotation() -{ - var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); - - var authenticationOptions = configuration - .GetSection("Keycloak2") - .Get(); - - authenticationOptions.Should().BeEquivalentTo(Expected); -} -``` +* Breaking change 💥: Changed default Configuration format from kebab-case to PascalCase. See the [KeycloakInstallationOptionsTests.cs](https://github.com/NikiforovAll/keycloak-authorization-services-dotnet/blob/main/tests/Keycloak.AuthServices.Common.Tests/KeycloakInstallationOptionsTests.cs) for more details. ```json { + // new default "Keycloak1": { "realm": "Test", "auth-server-url": "http://localhost:8080/", @@ -78,6 +31,7 @@ public void TestPascalCaseNotation() "secret": "secret" } }, + // old default "Keycloak2": { "Realm": "Test", "AuthServerUrl": "http://localhost:8080/", diff --git a/docs/public/apple-touch-icon.png b/docs/public/apple-touch-icon.png new file mode 100644 index 00000000..2eff5237 --- /dev/null +++ b/docs/public/apple-touch-icon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bdd66c412bad839df17f17df349a46fcb6e77a99b14f2ad48b93e783dac9dc33 +size 10153 diff --git a/docs/public/favicon-16x16.png b/docs/public/favicon-16x16.png new file mode 100644 index 00000000..5cdfad67 --- /dev/null +++ b/docs/public/favicon-16x16.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dcd18fe94bd62f9b528b6dffdda37a7bc142f642cb1f52a19e5ee886a12f6780 +size 1247 diff --git a/docs/public/favicon-32x32.png b/docs/public/favicon-32x32.png new file mode 100644 index 00000000..1dbed119 --- /dev/null +++ b/docs/public/favicon-32x32.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63bf19a9f70ca0387f0cf8def106577afd4b1854b2e3465cfd33facaf7a8c426 +size 2092 diff --git a/docs/public/favicon.ico b/docs/public/favicon.ico new file mode 100644 index 00000000..fa225c74 --- /dev/null +++ b/docs/public/favicon.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:abac6841e997dad3fb390782e50d55bd9a8b58d96478066a054c602e74647cff +size 15086 diff --git a/docs/public/icon.png b/docs/public/icon.png deleted file mode 100644 index bd7381fe..00000000 --- a/docs/public/icon.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:71fd6b87a460eabc22759b80991678f86dcf9ed2abef0f198015bd8f7443e70e -size 5796 diff --git a/docs/public/logo.png b/docs/public/logo.png deleted file mode 100644 index bd7381fe..00000000 --- a/docs/public/logo.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:71fd6b87a460eabc22759b80991678f86dcf9ed2abef0f198015bd8f7443e70e -size 5796 diff --git a/docs/public/site.webmanifest b/docs/public/site.webmanifest new file mode 100644 index 00000000..8af025f7 --- /dev/null +++ b/docs/public/site.webmanifest @@ -0,0 +1,14 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-144x144.png", + "sizes": "144x144", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/src/Keycloak.AuthServices.Authorization/PoliciesBuilderExtensions.cs b/src/Keycloak.AuthServices.Authorization/PoliciesBuilderExtensions.cs index e114a820..1c52febb 100644 --- a/src/Keycloak.AuthServices.Authorization/PoliciesBuilderExtensions.cs +++ b/src/Keycloak.AuthServices.Authorization/PoliciesBuilderExtensions.cs @@ -27,17 +27,17 @@ params string[] roles /// Adds resource role requirement to builder. Ensures that at least one resource role is present in resource claims. /// /// - /// + /// /// /// - public static AuthorizationPolicyBuilder RequireResourceRolesWithSource( + public static AuthorizationPolicyBuilder RequireResourceRolesForClient( this AuthorizationPolicyBuilder builder, - string rolesSource, + string client, string[] roles ) => builder .RequireClaim(KeycloakConstants.ResourceAccessClaimType) - .AddRequirements(new ResourceAccessRequirement(rolesSource, roles)); + .AddRequirements(new ResourceAccessRequirement(client, roles)); /// /// Adds realm role requirement to builder. Ensures that at least one realm role is present in realm claims. diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Constants/KeycloakClientApiConstants.cs b/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs similarity index 93% rename from src/Keycloak.AuthServices.Sdk/Admin/Constants/KeycloakClientApiConstants.cs rename to src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs index e6cf01b4..3e3e74bb 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/Constants/KeycloakClientApiConstants.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs @@ -1,9 +1,9 @@ -namespace Keycloak.AuthServices.Sdk.Admin.Constants; +namespace Keycloak.AuthServices.Sdk.Admin; /// /// Keycloak API endpoints /// -internal static class KeycloakClientApiConstants +internal static class ApiUrls { private const string AdminApiBase = "/admin"; @@ -42,6 +42,9 @@ internal static class KeycloakClientApiConstants internal const string ExecuteActionsEmail = $"{GetRealm}/users/{{id}}/execute-actions-email"; internal const string GetUserGroups = $"{GetRealm}/users/{{id}}/groups"; + #endregion + + #region Group API internal const string GetGroups = $"{GetRealm}/groups"; internal const string CreateGroup = $"{GetRealm}/groups"; diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakClient.cs index d880aafb..4c1369eb 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakClient.cs @@ -1,10 +1,9 @@ namespace Keycloak.AuthServices.Sdk.Admin; - /// /// Keycloak Admin API Client /// /// -/// Aggregates multiple clients. and +/// Aggregates multiple clients /// public interface IKeycloakClient : IKeycloakRealmClient, diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakRealmClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakRealmClient.cs index 46c98e75..67242753 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakRealmClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakRealmClient.cs @@ -21,7 +21,7 @@ async Task GetRealmAsync( { var response = await this.GetRealmWithResponseAsync(realm, cancellationToken); - return (await response.GetAsync(cancellationToken))!; + return (await response.GetResponseAsync(cancellationToken))!; } /// diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs index 87c8ba5a..6b35c08b 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs @@ -1,36 +1,122 @@ namespace Keycloak.AuthServices.Sdk.Admin; +using Keycloak.AuthServices.Sdk.Admin.Models; +using Keycloak.AuthServices.Sdk.Admin.Requests.Users; + /// /// User management /// public interface IKeycloakUserClient { - // /// - // /// Get a stream of users on the realm. - // /// - // /// Realm name (not ID). - // /// Optional query parameters. - // /// A stream of users, filtered according to query parameters. - // Task> GetUsers(string realm, GetUsersRequestParameters? parameters = default); + /// + /// Get a stream of users on the realm. + /// + /// Realm name (not ID). + /// Optional query parameters. + /// + /// A stream of users, filtered according to query parameters. + async Task> GetUsersAsync( + string realm, + GetUsersRequestParameters? parameters = default, + CancellationToken cancellationToken = default + ) + { + var response = await this.GetUsersWithResponseAsync(realm, parameters, cancellationToken); - // /// - // /// Get representation of a user. - // /// - // /// Realm name (not ID). - // /// User ID. - // /// The user representation. - // Task GetUser(string realm, string userId); + return ( + await response.GetResponseAsync>(cancellationToken) + )!; + } - // /// - // /// Create a new user. - // /// - // /// - // /// Username must be unique. - // /// - // /// Realm name (not ID). - // /// User representation. - // /// - // Task CreateUser(string realm, User user); + /// + /// Get a stream of users on the realm. + /// + /// Realm name (not ID). + /// Optional query parameters. + /// + /// A stream of users, filtered according to query parameters. + Task GetUsersWithResponseAsync( + string realm, + GetUsersRequestParameters? parameters = default, + CancellationToken cancellationToken = default + ); + + /// + /// Get representation of a user. + /// + /// Realm name (not ID). + /// User ID. + /// Indicates if the user profile metadata should be added to the response. + /// + /// The user representation. + async Task GetUserAsync( + string realm, + string userId, + bool includeUserProfileMetadata = false, + CancellationToken cancellationToken = default + ) + { + var response = await this.GetUserWithResponseAsync( + realm, + userId, + includeUserProfileMetadata, + cancellationToken + ); + + return (await response.GetResponseAsync(cancellationToken))!; + } + + // + /// Get representation of a user. + /// + /// Realm name (not ID). + /// User ID. + /// Indicates if the user profile metadata should be added to the response. + /// + /// The user representation. + Task GetUserWithResponseAsync( + string realm, + string userId, + bool includeUserProfileMetadata = false, + CancellationToken cancellationToken = default + ); + + /// + /// Create a new user. + /// + /// + /// Username must be unique. + /// + /// Realm name (not ID). + /// User representation. + /// + /// + async Task CreateUserAsync( + string realm, + UserRepresentation user, + CancellationToken cancellationToken = default + ) + { + var response = await this.CreateUserWithResponseAsync(realm, user, cancellationToken); + + await response.EnsureResponseAsync(cancellationToken); + } + + /// + /// Create a new user. + /// + /// + /// Username must be unique. + /// + /// Realm name (not ID). + /// User representation. + /// + /// + Task CreateUserWithResponseAsync( + string realm, + UserRepresentation user, + CancellationToken cancellationToken = default + ); // /// // /// Update the user. diff --git a/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs index 9cae4f70..ba981ebb 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs @@ -1,12 +1,16 @@ namespace Keycloak.AuthServices.Sdk.Admin; -using Keycloak.AuthServices.Sdk.Admin.Constants; +using System.Net.Http.Json; +using Keycloak.AuthServices.Sdk.Admin.Models; +using Keycloak.AuthServices.Sdk.Admin.Requests.Users; +using Keycloak.AuthServices.Sdk.Utils; /// /// TBD: /// public partial class KeycloakClient : IKeycloakClient { + private const string RealmParam = "{realm}"; private readonly HttpClient httpClient; /// @@ -15,13 +19,27 @@ public partial class KeycloakClient : IKeycloakClient /// public KeycloakClient(HttpClient httpClient) => this.httpClient = httpClient; + /// + public async Task CreateUserWithResponseAsync( + string realm, + UserRepresentation user, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.CreateUser.Replace(RealmParam, realm); + + var responseMessage = await this.httpClient.PostAsJsonAsync(path, user, cancellationToken); + + return responseMessage!; + } + /// public async Task GetRealmWithResponseAsync( string realm, CancellationToken cancellationToken = default ) { - var path = KeycloakClientApiConstants.GetRealm.Replace("{realm}", realm); + var path = ApiUrls.GetRealm.Replace(RealmParam, realm); var responseMessage = await this.httpClient.GetAsync( path, @@ -30,4 +48,69 @@ public async Task GetRealmWithResponseAsync( return responseMessage!; } + + /// + public async Task GetUsersWithResponseAsync( + string realm, + GetUsersRequestParameters? parameters = null, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.GetUsers.Replace(RealmParam, realm); + + var query = string.Empty; + + if (parameters is not null) + { +#pragma warning disable CA1305 // Specify IFormatProvider + var queryParameters = new List>() + { + new("briefRepresentation", parameters.BriefRepresentation?.ToString()), + new("email", parameters.Email), + new("emailVerified", parameters.EmailVerified?.ToString()), + new("enabled", parameters.Enabled?.ToString()), + new("exact", parameters.Exact?.ToString()), + new("first", parameters.First?.ToString()), + new("firstName", parameters.FirstName), + new("idpAlias", parameters.IdpAlias), + new("idpUserId", parameters.IdpUserId), + new("lastName", parameters.LastName), + new("max", parameters.Max?.ToString()), + new("q", parameters.Query), + new("search", parameters.Search), + new("username", parameters.Username) + }; +#pragma warning restore CA1305 // Specify IFormatProvider + + query = new QueryBuilder(queryParameters.Where(q => q.Value is not null)!) + .ToQueryString() + .ToString(); + } + + var responseMessage = await this.httpClient.GetAsync(path + query, cancellationToken); + + return responseMessage!; + } + + /// + public async Task GetUserWithResponseAsync( + string realm, + string userId, + bool includeUserProfileMetadata = false, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.GetUser.Replace(RealmParam, realm).Replace("{id}", userId); + + var query = includeUserProfileMetadata + ? new QueryBuilder() + .Add("includeUserProfileMetadata", includeUserProfileMetadata.ToString()) + .ToQueryString() + .ToString() + : string.Empty; + + var responseMessage = await this.httpClient.GetAsync(path + query, cancellationToken); + + return responseMessage!; + } } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Models/Contracts.cs b/src/Keycloak.AuthServices.Sdk/Admin/Models/Contracts.cs index 5ee15e0d..c574b7a0 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/Models/Contracts.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/Models/Contracts.cs @@ -8062,73 +8062,31 @@ public void Dispose() "NSwag", "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" )] -public partial class KeycloakException : System.Exception +public partial class KeycloakHttpClientException : System.Exception { public int StatusCode { get; private set; } - public string? Response { get; private set; } + public string HttpResponse { get; private set; } - public System.Collections.Generic.IReadOnlyDictionary< - string, - System.Collections.Generic.IEnumerable - > Headers { get; private set; } + public ErrorResponse Response { get; private set; } - public KeycloakException( + public KeycloakHttpClientException( string message, int statusCode, - string? response, - System.Collections.Generic.IReadOnlyDictionary< - string, - System.Collections.Generic.IEnumerable - > headers, + string httpResponse, + ErrorResponse response, System.Exception? innerException ) - : base( - message - + "\n\nStatus: " - + statusCode - + "\nResponse: \n" - + ( - (response == null) - ? "(null)" - : response.Substring(0, response.Length >= 512 ? 512 : response.Length) - ), - innerException - ) + : base(message + "\n\nStatus: " + statusCode, innerException) { StatusCode = statusCode; + HttpResponse = httpResponse; Response = response; - Headers = headers; } public override string ToString() { - return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString()); - } -} - -[System.CodeDom.Compiler.GeneratedCode( - "NSwag", - "13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))" -)] -public partial class KeycloakException : KeycloakException -{ - public TResult Result { get; private set; } - - public KeycloakException( - string message, - int statusCode, - string? response, - System.Collections.Generic.IReadOnlyDictionary< - string, - System.Collections.Generic.IEnumerable - > headers, - TResult result, - System.Exception? innerException - ) - : base(message, statusCode, response, headers, innerException) - { - Result = result; + return string.Format("HTTP Response: \n\n{0}\n\n{1}", HttpResponse, base.ToString()); } } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/GetUsersRequestParameters.cs b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/GetUsersRequestParameters.cs index 063fc805..907aa4f9 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/GetUsersRequestParameters.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/GetUsersRequestParameters.cs @@ -1,9 +1,10 @@ namespace Keycloak.AuthServices.Sdk.Admin.Requests.Users; -using Models; +using Keycloak.AuthServices.Sdk.Admin; +using Keycloak.AuthServices.Sdk.Admin.Models; /// -/// Optional request parameters for the endpoint. +/// Optional request parameters for the endpoint. /// It can be called in three different ways. /// /// @@ -15,15 +16,15 @@ namespace Keycloak.AuthServices.Sdk.Admin.Requests.Users; /// /// If is specified, other criteria such as /// will be ignored even though you may set them. The string will be matched against -/// the , , -/// and the of a . +/// the , , +/// and the of a . /// /// /// /// /// If is unspecified but any of , , /// or are specified, then those criteria are matched against -/// their respective fields on a entity. Combined with a logical AND. +/// their respective fields on a entity. Combined with a logical AND. /// /// /// @@ -36,8 +37,8 @@ public class GetUsersRequestParameters public bool? BriefRepresentation { get; init; } /// - /// Search for a string contained in , - /// or the complete if is true. + /// Search for a string contained in , + /// or the complete if is true. /// public string? Email { get; init; } @@ -47,7 +48,7 @@ public class GetUsersRequestParameters public bool? EmailVerified { get; init; } /// - /// Search for whether the is enabled or not. + /// Search for whether the is enabled or not. /// public bool? Enabled { get; init; } @@ -63,24 +64,24 @@ public class GetUsersRequestParameters public int? First { get; init; } /// - /// Search for a string contained in , - /// or the complete if is true. + /// Search for a string contained in , + /// or the complete if is true. /// public string? FirstName { get; init; } /// - /// Search for the alias of an Identity Provider linked to the . + /// Search for the alias of an Identity Provider linked to the . /// public string? IdpAlias { get; init; } /// - /// Search for a at an Identity Provider linked to the . + /// Search for a at an Identity Provider linked to the . /// public string? IdpUserId { get; init; } /// - /// Search for a string contained in , - /// or the complete if is true. + /// Search for a string contained in , + /// or the complete if is true. /// public string? LastName { get; init; } @@ -101,8 +102,8 @@ public class GetUsersRequestParameters public string? Search { get; init; } /// - /// Search for a string contained in , - /// or the complete if is true. + /// Search for a string contained in , + /// or the complete if is true. /// public string? Username { get; init; } } diff --git a/src/Keycloak.AuthServices.Sdk/ErrorResponse.cs b/src/Keycloak.AuthServices.Sdk/ErrorResponse.cs new file mode 100644 index 00000000..6a30df5d --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/ErrorResponse.cs @@ -0,0 +1,21 @@ +namespace Keycloak.AuthServices.Sdk; + +using System.Text.Json.Serialization; + +/// +/// TBD: +/// +public sealed record ErrorResponse +{ + /// + /// TBD: + /// + [JsonPropertyName("error")] + public string Error { get; init; } = default!; + + /// + /// TBD: + /// + [JsonPropertyName("error_description")] + public string ErrorDescription { get; init; } = default!; +} diff --git a/src/Keycloak.AuthServices.Sdk/HttpResponseExtensions.cs b/src/Keycloak.AuthServices.Sdk/HttpResponseExtensions.cs index 1e97e2a5..8ef6b33e 100644 --- a/src/Keycloak.AuthServices.Sdk/HttpResponseExtensions.cs +++ b/src/Keycloak.AuthServices.Sdk/HttpResponseExtensions.cs @@ -1,6 +1,8 @@ namespace Keycloak.AuthServices.Sdk; using System.Net.Http.Json; +using System.Text.Json; +using Keycloak.AuthServices.Sdk.Admin.Models; /// /// TBD: @@ -14,12 +16,12 @@ public static class HttpResponseExtensions /// /// /// - public static async Task GetAsync( + public static async Task GetResponseAsync( this HttpResponseMessage response, CancellationToken cancellationToken = default ) { - response.EnsureSuccessStatusCode(); + await response.EnsureResponseAsync(cancellationToken); var result = await response.Content.ReadFromJsonAsync( cancellationToken: cancellationToken @@ -27,4 +29,35 @@ public static class HttpResponseExtensions return result; } + + /// + /// TBD: + /// + /// + /// + /// + /// + public static async Task EnsureResponseAsync( + this HttpResponseMessage response, + CancellationToken cancellationToken = default + ) + { + try + { + response.EnsureSuccessStatusCode(); + } + catch (Exception exception) + { + var body = await response.Content.ReadAsStringAsync(cancellationToken); + var error = JsonSerializer.Deserialize(body); + + throw new KeycloakHttpClientException( + message: $"Unable to submit the request - '{error?.Error}'", + statusCode: (int)response.StatusCode, + httpResponse: body, + response: error!, + innerException: exception + ); + } + } } diff --git a/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs b/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs index 16ee2d5b..ad005b3a 100644 --- a/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs +++ b/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs @@ -48,6 +48,34 @@ public static IHttpClientBuilder AddKeycloakAdminHttpClient( .AddTypedClient(); } + /// + /// TBD: + /// + /// + /// + /// + /// v + public static IHttpClientBuilder AddKeycloakAdminHttpClient( + this IServiceCollection services, + KeycloakAdminClientOptions keycloakAdminClientOptions, + Action? configureClient = default + ) + { + void configureKeycloakOptions(KeycloakAdminClientOptions options) + { + options.Realm = keycloakAdminClientOptions.Realm; + options.AuthServerUrl = keycloakAdminClientOptions.AuthServerUrl; + options.Resource = keycloakAdminClientOptions.Resource; + // redundant + options.SslRequired = keycloakAdminClientOptions.SslRequired; + options.VerifyTokenAudience = keycloakAdminClientOptions.VerifyTokenAudience; + options.Credentials = keycloakAdminClientOptions.Credentials; + options.TokenClockSkew = keycloakAdminClientOptions.TokenClockSkew; + } + + return services.AddKeycloakAdminHttpClient(configureKeycloakOptions, configureClient); + } + /// /// Adds keycloak confidential client and underlying token management /// diff --git a/src/Keycloak.AuthServices.Sdk/Utils/QueryBuilder.cs b/src/Keycloak.AuthServices.Sdk/Utils/QueryBuilder.cs new file mode 100644 index 00000000..f2b4231f --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/Utils/QueryBuilder.cs @@ -0,0 +1,677 @@ +namespace Keycloak.AuthServices.Sdk.Utils; + +using System.Buffers; +using System.Collections; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.Encodings.Web; +using Microsoft.Extensions.Primitives; +#pragma warning disable IDE0251 // Make member 'readonly' + +/// +/// Allows constructing a query string. +/// +public class QueryBuilder : IEnumerable> +{ + private readonly IList> @params; + + /// + /// Initializes a new instance of . + /// + public QueryBuilder() => this.@params = new List>(); + + /// + /// Initializes a new instance of . + /// + /// The parameters to initialize the instance with. + public QueryBuilder(IEnumerable> parameters) => + this.@params = new List>(parameters); + + /// + /// Initializes a new instance of . + /// + /// The parameters to initialize the instance with. + public QueryBuilder(IEnumerable> parameters) + : this( + parameters.SelectMany( + kvp => kvp.Value, + (kvp, v) => KeyValuePair.Create(kvp.Key, v ?? string.Empty) + ) + ) { } + + /// + /// Adds a query string token to the instance. + /// + /// The query key. + /// The sequence of query values. + // public void Add(string key, IEnumerable values) + // { + // foreach (var value in values) + // { + // this.@params.Add(new KeyValuePair(key, value)); + // } + // } + + /// + /// Adds a query string token to the instance. + /// + /// The query key. + /// The query value. + public QueryBuilder Add(string key, string value) + { + this.@params.Add(new KeyValuePair(key, value)); + + return this; + } + + /// + public override string ToString() + { + using var builder = new ValueStringBuilder(); + var first = true; + for (var i = 0; i < this.@params.Count; i++) + { + var pair = this.@params[i]; + builder.Append(first ? '?' : '&'); + first = false; + builder.Append(UrlEncoder.Default.Encode(pair.Key)); + builder.Append('='); + builder.Append(UrlEncoder.Default.Encode(pair.Value)); + } + + return builder.ToString()!; + } + + /// + /// Constructs a from this . + /// + /// The . + public QueryString ToQueryString() => new(this.ToString()); + + /// + public override int GetHashCode() => this.ToQueryString().GetHashCode(); + + /// + public override bool Equals(object? obj) => this.ToQueryString().Equals(obj); + + /// + public IEnumerator> GetEnumerator() => + this.@params.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => this.@params.GetEnumerator(); +} + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +/// +/// Provides correct handling for QueryString value when needed to reconstruct a request or redirect URI string +/// +public readonly struct QueryString : IEquatable +{ + /// + /// Represents the empty query string. This field is read-only. + /// + public static readonly QueryString Empty = new(string.Empty); + + /// + /// Initialize the query string with a given value. This value must be in escaped and delimited format with + /// a leading '?' character. + /// + /// The query string to be assigned to the Value property. + public QueryString(string? value) + { + if (!string.IsNullOrEmpty(value) && value[0] != '?') + { + throw new ArgumentException( + "The leading '?' must be included for a non-empty query.", + nameof(value) + ); + } + this.Value = value; + } + + /// + /// The escaped query string with the leading '?' character + /// + public string? Value { get; } + + /// + /// True if the query string is not empty + /// + [MemberNotNullWhen(true, nameof(Value))] + public bool HasValue => !string.IsNullOrEmpty(this.Value); + + /// + /// Provides the query string escaped in a way which is correct for combining into the URI representation. + /// A leading '?' character will be included unless the Value is null or empty. Characters which are potentially + /// dangerous are escaped. + /// + /// The query string value + public override string ToString() => this.ToUriComponent(); + + /// + /// Provides the query string escaped in a way which is correct for combining into the URI representation. + /// A leading '?' character will be included unless the Value is null or empty. Characters which are potentially + /// dangerous are escaped. + /// + /// The query string value + public string ToUriComponent() => + // Escape things properly so System.Uri doesn't mis-interpret the data. + this.HasValue + ? this.Value.Replace("#", "%23") + : string.Empty; + + /// + /// Returns an QueryString given the query as it is escaped in the URI format. The string MUST NOT contain any + /// value that is not a query. + /// + /// The escaped query as it appears in the URI format. + /// The resulting QueryString + public static QueryString FromUriComponent(string uriComponent) + { + if (string.IsNullOrEmpty(uriComponent)) + { + return new QueryString(string.Empty); + } + return new QueryString(uriComponent); + } + + /// + /// Returns an QueryString given the query as from a Uri object. Relative Uri objects are not supported. + /// + /// The Uri object + /// The resulting QueryString + public static QueryString FromUriComponent(Uri uri) + { + ArgumentNullException.ThrowIfNull(uri); + + var queryValue = uri.GetComponents(UriComponents.Query, UriFormat.UriEscaped); + if (!string.IsNullOrEmpty(queryValue)) + { + queryValue = "?" + queryValue; + } + return new QueryString(queryValue); + } + + /// + /// Create a query string with a single given parameter name and value. + /// + /// The un-encoded parameter name + /// The un-encoded parameter value + /// The resulting QueryString + public static QueryString Create(string name, string value) + { + ArgumentNullException.ThrowIfNull(name); + + if (!string.IsNullOrEmpty(value)) + { + value = UrlEncoder.Default.Encode(value); + } + return new QueryString($"?{UrlEncoder.Default.Encode(name)}={value}"); + } + + /// + /// Creates a query string composed from the given name value pairs. + /// + /// + /// The resulting QueryString + public static QueryString Create(IEnumerable> parameters) + { + var builder = new StringBuilder(); + var first = true; + foreach (var pair in parameters) + { + AppendKeyValuePair(builder, pair.Key, pair.Value, first); + first = false; + } + + return new QueryString(builder.ToString()); + } + + /// + /// Creates a query string composed from the given name value pairs. + /// + /// + /// The resulting QueryString + public static QueryString Create(IEnumerable> parameters) + { + var builder = new StringBuilder(); + var first = true; + + foreach (var pair in parameters) + { + // If nothing in this pair.Values, append null value and continue + if (StringValues.IsNullOrEmpty(pair.Value)) + { + AppendKeyValuePair(builder, pair.Key, null, first); + first = false; + continue; + } + // Otherwise, loop through values in pair.Value + foreach (var value in pair.Value) + { + AppendKeyValuePair(builder, pair.Key, value, first); + first = false; + } + } + + return new QueryString(builder.ToString()); + } + + /// + /// Concatenates to the current query string. + /// + /// The to concatenate. + /// The concatenated . + public QueryString Add(QueryString other) + { + if (!this.HasValue || this.Value.Equals("?", StringComparison.Ordinal)) + { + return other; + } + if (!other.HasValue || other.Value.Equals("?", StringComparison.Ordinal)) + { + return this; + } + + // ?name1=value1 Add ?name2=value2 returns ?name1=value1&name2=value2 + return new QueryString(string.Concat(this.Value, "&", other.Value.AsSpan(1))); + } + + /// + /// Concatenates a query string with and + /// to the current query string. + /// + /// The name of the query string to concatenate. + /// The value of the query string to concatenate. + /// The concatenated . + public QueryString Add(string name, string value) + { + ArgumentNullException.ThrowIfNull(name); + + if (!this.HasValue || this.Value.Equals("?", StringComparison.Ordinal)) + { + return Create(name, value); + } + + var builder = new StringBuilder(this.Value); + AppendKeyValuePair(builder, name, value, first: false); + return new QueryString(builder.ToString()); + } + + /// + /// Evalutes if the current query string is equal to . + /// + /// The to compare. + /// if the query strings are equal. + public bool Equals(QueryString other) + { + if (!this.HasValue && !other.HasValue) + { + return true; + } + return string.Equals(this.Value, other.Value, StringComparison.Ordinal); + } + + /// + /// Evaluates if the current query string is equal to an object . + /// + /// An object to compare. + /// if the query strings are equal. + public override bool Equals(object? obj) + { + if (obj is null) + { + return !this.HasValue; + } + return obj is QueryString @string && this.Equals(@string); + } + + /// + /// Gets a hash code for the value. + /// + /// The hash code as an . + public override int GetHashCode() => this.HasValue ? this.Value.GetHashCode() : 0; + + /// + /// Evaluates if one query string is equal to another. + /// + /// A instance. + /// A instance. + /// if the query strings are equal. + public static bool operator ==(QueryString left, QueryString right) => left.Equals(right); + + /// + /// Evaluates if one query string is not equal to another. + /// + /// A instance. + /// A instance. + /// if the query strings are not equal. + public static bool operator !=(QueryString left, QueryString right) => !left.Equals(right); + + /// + /// Concatenates and into a single query string. + /// + /// A instance. + /// A instance. + /// The concatenated . + public static QueryString operator +(QueryString left, QueryString right) => left.Add(right); + + private static void AppendKeyValuePair( + StringBuilder builder, + string key, + string? value, + bool first + ) + { + builder.Append(first ? '?' : '&'); + builder.Append(UrlEncoder.Default.Encode(key)); + builder.Append('='); + if (!string.IsNullOrEmpty(value)) + { + builder.Append(UrlEncoder.Default.Encode(value)); + } + } +} + +internal ref partial struct ValueStringBuilder +{ + private char[]? arrayToReturnToPool; + private int position; + + public ValueStringBuilder(Span initialBuffer) + { + this.arrayToReturnToPool = null; + this.RawChars = initialBuffer; + this.position = 0; + } + + public ValueStringBuilder(int initialCapacity) + { + this.arrayToReturnToPool = ArrayPool.Shared.Rent(initialCapacity); + this.RawChars = this.arrayToReturnToPool; + this.position = 0; + } + + public int Length + { + get => this.position; + set + { + Debug.Assert(value >= 0); + Debug.Assert(value <= this.RawChars.Length); + this.position = value; + } + } + + public int Capacity => this.RawChars.Length; + + public void EnsureCapacity(int capacity) + { + // This is not expected to be called this with negative capacity + Debug.Assert(capacity >= 0); + + // If the caller has a bug and calls this with negative capacity, make sure to call Grow to throw an exception. + if ((uint)capacity > (uint)this.RawChars.Length) + { + this.Grow(capacity - this.position); + } + } + + /// + /// Get a pinnable reference to the builder. + /// Does not ensure there is a null char after + /// This overload is pattern matched in the C# 7.3+ compiler so you can omit + /// the explicit method call, and write eg "fixed (char* c = builder)" + /// + public ref char GetPinnableReference() => ref MemoryMarshal.GetReference(this.RawChars); + + /// + /// Get a pinnable reference to the builder. + /// + /// Ensures that the builder has a null char after + public ref char GetPinnableReference(bool terminate) + { + if (terminate) + { + this.EnsureCapacity(this.Length + 1); + this.RawChars[this.Length] = '\0'; + } + return ref MemoryMarshal.GetReference(this.RawChars); + } + + public ref char this[int index] + { + get + { + Debug.Assert(index < this.position); + return ref this.RawChars[index]; + } + } + + public override string ToString() + { + var s = this.RawChars[..this.position].ToString(); + this.Dispose(); + return s; + } + + /// Returns the underlying storage of the builder. + public Span RawChars { get; private set; } + + /// + /// Returns a span around the contents of the builder. + /// + /// Ensures that the builder has a null char after + public ReadOnlySpan AsSpan(bool terminate) + { + if (terminate) + { + this.EnsureCapacity(this.Length + 1); + this.RawChars[this.Length] = '\0'; + } + return this.RawChars[..this.position]; + } + + public ReadOnlySpan AsSpan() => this.RawChars[..this.position]; + + public ReadOnlySpan AsSpan(int start) => this.RawChars[start..this.position]; + + public ReadOnlySpan AsSpan(int start, int length) => this.RawChars.Slice(start, length); + + public bool TryCopyTo(Span destination, out int charsWritten) + { + if (this.RawChars[..this.position].TryCopyTo(destination)) + { + charsWritten = this.position; + this.Dispose(); + return true; + } + else + { + charsWritten = 0; + this.Dispose(); + return false; + } + } + + public void Insert(int index, char value, int count) + { + if (this.position > this.RawChars.Length - count) + { + this.Grow(count); + } + + var remaining = this.position - index; + this.RawChars.Slice(index, remaining).CopyTo(this.RawChars[(index + count)..]); + this.RawChars.Slice(index, count).Fill(value); + this.position += count; + } + + public void Insert(int index, string? s) + { + if (s == null) + { + return; + } + + var count = s.Length; + + if (this.position > this.RawChars.Length - count) + { + this.Grow(count); + } + + var remaining = this.position - index; + this.RawChars.Slice(index, remaining).CopyTo(this.RawChars[(index + count)..]); + s.AsSpan().CopyTo(this.RawChars[index..]); + this.position += count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(char c) + { + var pos = this.position; + if ((uint)pos < (uint)this.RawChars.Length) + { + this.RawChars[pos] = c; + this.position = pos + 1; + } + else + { + this.GrowAndAppend(c); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(string? s) + { + if (s == null) + { + return; + } + + var pos = this.position; + if (s.Length == 1 && (uint)pos < (uint)this.RawChars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc. + { + this.RawChars[pos] = s[0]; + this.position = pos + 1; + } + else + { + this.AppendSlow(s); + } + } + + private void AppendSlow(string s) + { + var pos = this.position; + if (pos > this.RawChars.Length - s.Length) + { + this.Grow(s.Length); + } + + s.AsSpan().CopyTo(this.RawChars[pos..]); + this.position += s.Length; + } + + public void Append(char c, int count) + { + if (this.position > this.RawChars.Length - count) + { + this.Grow(count); + } + + var dst = this.RawChars.Slice(this.position, count); + for (var i = 0; i < dst.Length; i++) + { + dst[i] = c; + } + this.position += count; + } + + public void Append(ReadOnlySpan value) + { + var pos = this.position; + if (pos > this.RawChars.Length - value.Length) + { + this.Grow(value.Length); + } + + value.CopyTo(this.RawChars[this.position..]); + this.position += value.Length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span AppendSpan(int length) + { + var origPos = this.position; + if (origPos > this.RawChars.Length - length) + { + this.Grow(length); + } + + this.position = origPos + length; + return this.RawChars.Slice(origPos, length); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void GrowAndAppend(char c) + { + this.Grow(1); + this.Append(c); + } + + /// + /// Resize the internal buffer either by doubling current buffer size or + /// by adding to + /// whichever is greater. + /// + /// + /// Number of chars requested beyond current position. + /// + [MethodImpl(MethodImplOptions.NoInlining)] + private void Grow(int additionalCapacityBeyondPos) + { + Debug.Assert(additionalCapacityBeyondPos > 0); + Debug.Assert( + this.position > this.RawChars.Length - additionalCapacityBeyondPos, + "Grow called incorrectly, no resize is needed." + ); + + // Make sure to let Rent throw an exception if the caller has a bug and the desired capacity is negative + var poolArray = ArrayPool.Shared.Rent( + (int) + Math.Max( + (uint)(this.position + additionalCapacityBeyondPos), + (uint)this.RawChars.Length * 2 + ) + ); + + this.RawChars[..this.position].CopyTo(poolArray); + + var toReturn = this.arrayToReturnToPool; + this.RawChars = this.arrayToReturnToPool = poolArray; + if (toReturn != null) + { + ArrayPool.Shared.Return(toReturn); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + var toReturn = this.arrayToReturnToPool; + this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again + if (toReturn != null) + { + ArrayPool.Shared.Return(toReturn); + } + } +} +#pragma warning restore IDE0251 // Make member 'readonly' diff --git a/tests/.editorconfig b/tests/.editorconfig index 75c9d089..257d74eb 100644 --- a/tests/.editorconfig +++ b/tests/.editorconfig @@ -6,3 +6,9 @@ dotnet_diagnostic.CA1711.severity = suggestion # IDE0022: Use expression body for method dotnet_diagnostic.IDE0022.severity = suggestion + +# CA1001: Types that own disposable fields should be disposable +dotnet_diagnostic.CA1001.severity = suggestion + +# CA1859: Use concrete types when possible for improved performance +dotnet_diagnostic.CA1859.severity = suggestion diff --git a/tests/Keycloak.AuthServices.Common.Tests/Keycloak.AuthServices.Common.Tests.csproj b/tests/Keycloak.AuthServices.Common.Tests/Keycloak.AuthServices.Common.Tests.csproj index 78798f83..d27a0d79 100644 --- a/tests/Keycloak.AuthServices.Common.Tests/Keycloak.AuthServices.Common.Tests.csproj +++ b/tests/Keycloak.AuthServices.Common.Tests/Keycloak.AuthServices.Common.Tests.csproj @@ -1,6 +1,10 @@ - + + + + + diff --git a/tests/Keycloak.AuthServices.IntegrationTests/Admin/KeycloakRealmClientTests.cs b/tests/Keycloak.AuthServices.IntegrationTests/Admin/KeycloakRealmClientTests.cs index 2a9b2f6d..8f652275 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/Admin/KeycloakRealmClientTests.cs +++ b/tests/Keycloak.AuthServices.IntegrationTests/Admin/KeycloakRealmClientTests.cs @@ -3,6 +3,8 @@ namespace Keycloak.AuthServices.IntegrationTests; using Keycloak.AuthServices.Common; using Keycloak.AuthServices.Sdk; using Keycloak.AuthServices.Sdk.Admin; +using Keycloak.AuthServices.Sdk.Admin.Models; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using static Keycloak.AuthServices.IntegrationTests.Utils; @@ -11,13 +13,60 @@ public class KeycloakRealmClientTests(ITestOutputHelper testOutputHelper) { private static readonly string AppSettings = "appsettings.Master.json"; + [Fact] + public async Task GetRealmAsync_RealmExistsInlineConfiguration_Success() + { + var (services, _) = KeycloakSetup(string.Empty, testOutputHelper); + + #region GetRealmAsync_RealmExistsInlineConfiguration_Success + var tokenClientName = "keycloak_admin_api_token"; + + var keycloakOptions = new KeycloakAdminClientOptions + { + AuthServerUrl = "http://localhost:8080/", + Realm = "master", + Resource = "admin-api", + }; + + services.AddDistributedMemoryCache(); + services + .AddClientCredentialsTokenManagement() + .AddClient(tokenClientName, client => BindKeycloak(client, keycloakOptions)); + + services + .AddKeycloakAdminHttpClient(keycloakOptions) + .AddClientCredentialsTokenHandler(tokenClientName); + + var sp = services.BuildServiceProvider(); + + var client = sp.GetRequiredService(); + + var response = await client.GetRealmWithResponseAsync("Test"); + + var realm = response.GetResponseAsync(); + + realm.Should().NotBeNull(); + + static void BindKeycloak( + Duende.AccessTokenManagement.ClientCredentialsClient client, + KeycloakAdminClientOptions adminClientOptions + ) + { + client.ClientId = adminClientOptions.Resource; + client.ClientSecret = adminClientOptions.Credentials.Secret; + client.TokenEndpoint = adminClientOptions.KeycloakTokenEndpoint; + } + #endregion GetRealmAsync_RealmExistsInlineConfiguration_Success + } + [Fact] public async Task GetRealmAsync_RealmExists_Success() { var (services, configuration) = KeycloakSetup(AppSettings, testOutputHelper); + var tokenClientName = "keycloak_admin_api_token"; #region GetRealmAsync_RealmExists_Success - var tokenClientName = "keycloak_admin_api_token"; + var options = configuration.GetKeycloakOptions()!; services.AddDistributedMemoryCache(); services @@ -26,8 +75,6 @@ public async Task GetRealmAsync_RealmExists_Success() tokenClientName, client => { - var options = configuration.GetKeycloakOptions()!; - client.ClientId = options.Resource; client.ClientSecret = options.Credentials.Secret; client.TokenEndpoint = options.KeycloakTokenEndpoint; @@ -35,16 +82,53 @@ public async Task GetRealmAsync_RealmExists_Success() ); services - .AddKeycloakAdminHttpClient(configuration) + .AddKeycloakAdminHttpClient(options) .AddClientCredentialsTokenHandler(tokenClientName); var sp = services.BuildServiceProvider(); - var client = sp.GetRequiredService(); var realm = await client.GetRealmAsync("Test"); + #endregion GetRealmAsync_RealmExists_Success realm.Should().NotBeNull(); - #endregion GetRealmAsync_RealmExists_Success + } + + [Fact] + public async Task GetRealmAsync_NoRealmExists_ExceptionThrown() + { + var (services, configuration) = KeycloakSetup(AppSettings, testOutputHelper); + var tokenClientName = "keycloak_admin_api_token"; + + #region GetRealmAsync_NoRealmExists_ExceptionThrown + var options = configuration.GetKeycloakOptions()!; + + services.AddDistributedMemoryCache(); + services + .AddClientCredentialsTokenManagement() + .AddClient( + tokenClientName, + client => + { + client.ClientId = options.Resource; + client.ClientSecret = options.Credentials.Secret; + client.TokenEndpoint = options.KeycloakTokenEndpoint; + } + ); + + services + .AddKeycloakAdminHttpClient(options) + .AddClientCredentialsTokenHandler(tokenClientName); + + var sp = services.BuildServiceProvider(); + var client = sp.GetRequiredService(); + + var result = await FluentActions + .Invoking(() => client.GetRealmAsync(Guid.NewGuid().ToString())) + .Should() + .ThrowAsync(); + + result.And.StatusCode.Should().Be(StatusCodes.Status404NotFound); + #endregion GetRealmAsync_NoRealmExists_ExceptionThrown } } diff --git a/tests/Keycloak.AuthServices.IntegrationTests/PolicyTests.cs b/tests/Keycloak.AuthServices.IntegrationTests/PolicyTests.cs index d757e9c0..90f86fcd 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/PolicyTests.cs +++ b/tests/Keycloak.AuthServices.IntegrationTests/PolicyTests.cs @@ -75,7 +75,7 @@ await host.Scenario(_ => [Fact] public async Task RequireClientRoles_TestClientRole_Verified() { - var policyName = "RequireResourceRolesWithSource"; + var policyName = "RequireResourceRolesForClient"; await using var host = await AlbaHost.For( x => { @@ -97,7 +97,7 @@ public async Task RequireClientRoles_TestClientRole_Verified() .AddPolicy( policyName, policy => - policy.RequireResourceRolesWithSource( + policy.RequireResourceRolesForClient( "test-client", [KeycloakRoles.TestClientRole] ) diff --git a/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs b/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs index 1a6e9c17..319a1500 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs +++ b/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs @@ -69,7 +69,7 @@ ITestOutputHelper testOutputHelper var services = new ServiceCollection(); var configuration = new ConfigurationBuilder() - .AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), fileName), optional: false) + .AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), fileName), optional: true) .Build(); services.AddSingleton(configuration); diff --git a/tests/Keycloak.AuthServices.Sdk.Tests/Extensions/MockedRequestExtensions.cs b/tests/Keycloak.AuthServices.Sdk.Tests/Extensions/MockedRequestExtensions.cs index c0abe03f..58cfe7a9 100644 --- a/tests/Keycloak.AuthServices.Sdk.Tests/Extensions/MockedRequestExtensions.cs +++ b/tests/Keycloak.AuthServices.Sdk.Tests/Extensions/MockedRequestExtensions.cs @@ -8,9 +8,11 @@ public static MockedRequest WithAcceptHeader(this MockedRequest request) => request.WithHeaders("Accept", "application/json"); public static MockedRequest WithAcceptAndContentTypeHeaders(this MockedRequest request) => - request.WithHeaders(new Dictionary - { - ["Accept"] = "application/json", - ["Content-Type"] = "application/json" - }); + request.WithHeaders( + new Dictionary + { + ["Accept"] = "application/json", + ["Content-Type"] = "application/json" + } + ); } diff --git a/tests/Keycloak.AuthServices.Sdk.Tests/Keycloak.AuthServices.Sdk.Tests.csproj b/tests/Keycloak.AuthServices.Sdk.Tests/Keycloak.AuthServices.Sdk.Tests.csproj index 381fa27b..4dbc35a2 100644 --- a/tests/Keycloak.AuthServices.Sdk.Tests/Keycloak.AuthServices.Sdk.Tests.csproj +++ b/tests/Keycloak.AuthServices.Sdk.Tests/Keycloak.AuthServices.Sdk.Tests.csproj @@ -4,4 +4,12 @@ + + + + + + + + diff --git a/tests/Keycloak.AuthServices.Sdk.Tests/KeycloakRealmClientTests.cs b/tests/Keycloak.AuthServices.Sdk.Tests/KeycloakRealmClientTests.cs index d96b4878..61000840 100644 --- a/tests/Keycloak.AuthServices.Sdk.Tests/KeycloakRealmClientTests.cs +++ b/tests/Keycloak.AuthServices.Sdk.Tests/KeycloakRealmClientTests.cs @@ -1,12 +1,11 @@ namespace Keycloak.AuthServices.Sdk.Tests; using System.Net; +using Keycloak.AuthServices.Sdk.Admin; +using Keycloak.AuthServices.Sdk.Admin.Models; using RichardSzalay.MockHttp; -using Sdk.Admin; -#pragma warning disable CA1001 // Types that own disposable fields should be disposable public class KeycloakRealmClientTests -#pragma warning restore CA1001 // Types that own disposable fields should be disposable { private const string BaseAddress = "http://localhost:8080"; @@ -18,14 +17,14 @@ public KeycloakRealmClientTests() var httpClient = this.handler.ToHttpClient(); httpClient.BaseAddress = new Uri(BaseAddress); - this.keycloakRealmClient = RestService.For(httpClient); + this.keycloakRealmClient = new KeycloakClient(httpClient); } [Fact] public async Task GetRealmShouldCallRealmEndpoint() { - this.handler.Expect(HttpMethod.Get, $"{BaseAddress}/admin/realms/master") - .Respond(HttpStatusCode.OK); + this.handler.Expect(HttpMethod.Get, "/admin/realms/master") + .Respond(HttpStatusCode.OK, "application/json", "{}"); await this.keycloakRealmClient.GetRealmAsync("master"); @@ -35,14 +34,20 @@ public async Task GetRealmShouldCallRealmEndpoint() [Fact] public async Task GetRealmShouldThrowNotFoundApiExceptionWhenRealmDoesNotExist() { - this.handler.Expect(HttpMethod.Get, $"{BaseAddress}/admin/realms/nonexistent") - .Respond(HttpStatusCode.NotFound); + this.handler.Expect(HttpMethod.Get, "/admin/realms/nonexistent") + .Respond( + HttpStatusCode.NotFound, + "application/json", + /*lang=json,strict*/"{\"error\": \"Realm not found\"}" + ); - var exception = await Assert.ThrowsAsync( - () => this.keycloakRealmClient.GetRealmAsync("nonexistent") - ); + var exception = await FluentActions + .Invoking(() => this.keycloakRealmClient.GetRealmAsync("nonexistent")) + .Should() + .ThrowAsync(); + + exception.And.StatusCode.Should().Be((int)HttpStatusCode.NotFound); - Assert.Equal(HttpStatusCode.NotFound, exception.StatusCode); this.handler.VerifyNoOutstandingExpectation(); } } diff --git a/tests/Keycloak.AuthServices.Sdk.Tests/KeycloakUserClientTests.cs b/tests/Keycloak.AuthServices.Sdk.Tests/KeycloakUserClientTests.cs index c230da76..413661b4 100644 --- a/tests/Keycloak.AuthServices.Sdk.Tests/KeycloakUserClientTests.cs +++ b/tests/Keycloak.AuthServices.Sdk.Tests/KeycloakUserClientTests.cs @@ -1,17 +1,14 @@ namespace Keycloak.AuthServices.Sdk.Tests; using System.Net; -using Extensions; -using FluentAssertions; -using Keycloak.AuthServices.Sdk; +using System.Text.Json; +using Keycloak.AuthServices.Sdk.Admin; +using Keycloak.AuthServices.Sdk.Admin.Models; +using Keycloak.AuthServices.Sdk.Admin.Requests.Users; +using Keycloak.AuthServices.Sdk.Utils; using RichardSzalay.MockHttp; -using Sdk.Admin; -using Sdk.Admin.Models; -using Sdk.Admin.Requests.Users; -#pragma warning disable CA1001 // Types that own disposable fields should be disposable public class KeycloakUserClientTests -#pragma warning restore CA1001 // Types that own disposable fields should be disposable { private const string BaseAddress = "http://localhost:8080"; @@ -23,23 +20,18 @@ public KeycloakUserClientTests() var httpClient = this.handler.ToHttpClient(); httpClient.BaseAddress = new Uri(BaseAddress); - this.keycloakUserClient = RestService.For( - httpClient, - ServiceCollectionExtensions.GetKeycloakClientRefitSettings() - ); + this.keycloakUserClient = new KeycloakClient(httpClient); } - [Fact] - public async Task GetUserShouldCallCorrectEndpoint() + [Theory, AutoData] + public async Task GetUserShouldCallCorrectEndpoint(UserRepresentation userFixture) { - var userId = Guid.NewGuid(); - var userRepresentation = GetUserRepresentation(userId); + var userId = userFixture.Id; - this.handler.Expect(HttpMethod.Get, $"{BaseAddress}/admin/realms/master/users/{userId}") - .WithAcceptHeader() - .Respond(HttpStatusCode.OK, "application/json", userRepresentation); + this.handler.Expect(HttpMethod.Get, $"/admin/realms/master/users/{userId}") + .Respond(HttpStatusCode.OK, "application/json", JsonSerializer.Serialize(userFixture)); - var user = await this.keycloakUserClient.GetUser("master", userId.ToString()); + var user = await this.keycloakUserClient.GetUserAsync("master", userId.ToString()); user.Id.Should().Be(userId.ToString()); this.handler.VerifyNoOutstandingExpectation(); @@ -53,15 +45,16 @@ public async Task GetUserShouldShouldThrowNotFoundApiExceptionWhenUserDoesNotExi "{\"error\":\"User not found\"}"; this.handler.Expect(HttpMethod.Get, $"{BaseAddress}/admin/realms/master/users/{userId}") - .WithAcceptHeader() .Respond(HttpStatusCode.NotFound, "application/json", errorMessage); - var exception = await Assert.ThrowsAsync( - () => this.keycloakUserClient.GetUser("master", userId.ToString()) - ); + var exception = await FluentActions + .Invoking(() => this.keycloakUserClient.GetUserAsync("master", userId.ToString())) + .Should() + .ThrowAsync(); + + exception.And.StatusCode.Should().Be((int)HttpStatusCode.NotFound); + exception.And.Response?.Error.Should().Be("User not found"); - exception.StatusCode.Should().Be(HttpStatusCode.NotFound); - exception.Content.Should().Be(errorMessage); this.handler.VerifyNoOutstandingExpectation(); } @@ -80,10 +73,9 @@ public async Task GetUsersShouldCallCorrectEndpoint() var response = $"[{string.Join(",", users.Select(u => u.Representation))}]"; this.handler.Expect(HttpMethod.Get, $"{BaseAddress}/admin/realms/master/users") - .WithAcceptHeader() .Respond(HttpStatusCode.OK, "application/json", response); - var result = await this.keycloakUserClient.GetUsers("master"); + var result = await this.keycloakUserClient.GetUsersAsync("master"); result.Select(u => u.Id).Should().BeEquivalentTo(users.Select(u => u.Id)); this.handler.VerifyNoOutstandingExpectation(); @@ -132,10 +124,9 @@ public async Task GetUsersShouldCallCorrectEndpointWithOptionalQueryParameters() var response = $"[{GetUserRepresentation(Guid.NewGuid())}]"; this.handler.Expect(HttpMethod.Get, url + queryBuilder.ToQueryString()) - .WithAcceptHeader() .Respond(HttpStatusCode.OK, "application/json", response); - _ = await this.keycloakUserClient.GetUsers("master", getUsersRequestParameters); + _ = await this.keycloakUserClient.GetUsersAsync("master", getUsersRequestParameters); this.handler.VerifyNoOutstandingExpectation(); } @@ -144,10 +135,9 @@ public async Task GetUsersShouldCallCorrectEndpointWithOptionalQueryParameters() public async Task CreateUserShouldCallCorrectEndpoint() { this.handler.Expect(HttpMethod.Post, $"{BaseAddress}/admin/realms/master/users") - .WithAcceptAndContentTypeHeaders() .Respond(HttpStatusCode.Created); - await this.keycloakUserClient.CreateUser( + await this.keycloakUserClient.CreateUserAsync( "master", new() { Username = "email@example.com" } ); @@ -161,183 +151,188 @@ public async Task CreateUserShouldReturnBadRequestWhenRequestIsInvalid() const string errorMessage = /*lang=json,strict*/ "{\"errorMessage\":\"User name is missing\"}"; - this.handler.Expect(HttpMethod.Post, $"{BaseAddress}/admin/realms/master/users") - .WithAcceptAndContentTypeHeaders() + this.handler.Expect(HttpMethod.Post, $"/admin/realms/master/users") .Respond(HttpStatusCode.BadRequest, "application/json", errorMessage); - var response = await this.keycloakUserClient.CreateUser("master", new User()); - - var content = await response.Content.ReadAsStringAsync(); - content.Should().Be(errorMessage); - response.StatusCode.Should().Be(HttpStatusCode.BadRequest); - this.handler.VerifyNoOutstandingExpectation(); - } - - [Fact] - public async Task UpdateUserShouldCallCorrectEndpoint() - { - var userId = Guid.NewGuid(); - - this.handler.Expect(HttpMethod.Put, $"{BaseAddress}/admin/realms/master/users/{userId}") - .WithAcceptAndContentTypeHeaders() - .WithContent( /*lang=json,strict*/ - "{\"firstName\":\"FirstName\"}" + var exception = await FluentActions + .Invoking( + () => this.keycloakUserClient.CreateUserAsync("master", new UserRepresentation()) ) - .Respond(HttpStatusCode.NoContent); + .Should() + .ThrowAsync(); - await this.keycloakUserClient.UpdateUser( - "master", - userId.ToString(), - new() { FirstName = "FirstName" } - ); + exception.And.StatusCode.Should().Be((int)HttpStatusCode.BadRequest); this.handler.VerifyNoOutstandingExpectation(); } - [Fact] - public async Task UpdateUserShouldThrowNotFoundApiExceptionWhenUserDoesNotExist() - { - var userId = Guid.NewGuid(); - const string errorMessage = /*lang=json,strict*/ - "{\"errorMessage\":\"User name is missing\"}"; - - this.handler.Expect(HttpMethod.Put, $"{BaseAddress}/admin/realms/master/users/{userId}") - .WithAcceptAndContentTypeHeaders() - .Respond(HttpStatusCode.NotFound, "application/json", errorMessage); - - var exception = await Assert.ThrowsAsync( - () => this.keycloakUserClient.UpdateUser("master", userId.ToString(), new User()) - ); - - exception.StatusCode.Should().Be(HttpStatusCode.NotFound); - exception.Content.Should().Be(errorMessage); - this.handler.VerifyNoOutstandingExpectation(); - } - - [Fact] - public async Task DeleteUserShouldCallCorrectEndpoint() - { - var userId = Guid.NewGuid(); - - this.handler.Expect(HttpMethod.Delete, $"{BaseAddress}/admin/realms/master/users/{userId}") - .WithAcceptAndContentTypeHeaders() - .WithContent(string.Empty) - .Respond(HttpStatusCode.NoContent); - - await this.keycloakUserClient.DeleteUser("master", userId.ToString()); - - this.handler.VerifyNoOutstandingExpectation(); - } - - [Fact] - public async Task DeleteUserShouldThrowNotFoundApiExceptionWhenUserDoesNotExist() - { - var userId = Guid.NewGuid(); - const string errorMessage = /*lang=json,strict*/ - "{\"errorMessage\":\"User name is missing\"}"; - - this.handler.Expect( - HttpMethod.Delete, - $"{BaseAddress}/admin/realms/master/users/{userId.ToString()}" - ) - .WithAcceptAndContentTypeHeaders() - .Respond(HttpStatusCode.BadRequest, "application/json", errorMessage); - - var exception = await Assert.ThrowsAsync( - () => this.keycloakUserClient.DeleteUser("master", userId.ToString()) - ); - - exception.StatusCode.Should().Be(HttpStatusCode.BadRequest); - exception.Content.Should().Be(errorMessage); - this.handler.VerifyNoOutstandingExpectation(); - } - - [Fact] - public async Task SendVerifyEmailShouldCallCorrectEndpoint() - { - var userId = Guid.NewGuid(); - - this.handler.Expect( - HttpMethod.Put, - $"{BaseAddress}/admin/realms/master/users/{userId}/send-verify-email" - ) - .WithAcceptHeader() - .Respond(HttpStatusCode.NoContent); - - await this.keycloakUserClient.SendVerifyEmail("master", userId.ToString()); - - this.handler.VerifyNoOutstandingExpectation(); - } - - [Fact] - public async Task SendVerifyEmailShouldCallCorrectEndpointWithOptionalQueryParameters() - { - var userId = Guid.NewGuid(); - const string clientId = "client-id"; - const string redirectUri = "https://localhost:5001"; - - var url = - $"{BaseAddress}/admin/realms/master/users/{userId}/send-verify-email" - + $"?client_id={clientId}" - + $"&redirect_uri={redirectUri}"; - - this.handler.Expect(HttpMethod.Put, url) - .WithAcceptHeader() - .Respond(HttpStatusCode.NoContent); - - await this.keycloakUserClient.SendVerifyEmail( - "master", - userId.ToString(), - clientId, - redirectUri - ); - - this.handler.VerifyNoOutstandingExpectation(); - } - - [Fact] - public async Task SendVerifyEmailShouldThrowNotFoundApiExceptionWhenUserDoesNotExist() - { - var userId = Guid.NewGuid(); - const string errorMessage = /*lang=json,strict*/ - "{\"error\":\"User not found\"}"; - - this.handler.Expect( - HttpMethod.Put, - $"{BaseAddress}/admin/realms/master/users/{userId}/send-verify-email" - ) - .WithAcceptHeader() - .Respond(HttpStatusCode.NotFound, "application/json", errorMessage); - - var exception = await Assert.ThrowsAsync( - () => this.keycloakUserClient.SendVerifyEmail("master", userId.ToString()) - ); - - exception.StatusCode.Should().Be(HttpStatusCode.NotFound); - exception.Content.Should().Be(errorMessage); - this.handler.VerifyNoOutstandingExpectation(); - } + // [Fact] + // public async Task UpdateUserShouldCallCorrectEndpoint() + // { + // var userId = Guid.NewGuid(); + + // this.handler.Expect(HttpMethod.Put, $"{BaseAddress}/admin/realms/master/users/{userId}") + // .WithAcceptAndContentTypeHeaders() + // .WithContent( /*lang=json,strict*/ + // "{\"firstName\":\"FirstName\"}" + // ) + // .Respond(HttpStatusCode.NoContent); + + // await this.keycloakUserClient.UpdateUser( + // "master", + // userId.ToString(), + // new() { FirstName = "FirstName" } + // ); + + // this.handler.VerifyNoOutstandingExpectation(); + // } + + // [Fact] + // public async Task UpdateUserShouldThrowNotFoundApiExceptionWhenUserDoesNotExist() + // { + // var userId = Guid.NewGuid(); + // const string errorMessage = /*lang=json,strict*/ + // "{\"errorMessage\":\"User name is missing\"}"; + + // this.handler.Expect(HttpMethod.Put, $"{BaseAddress}/admin/realms/master/users/{userId}") + // .WithAcceptAndContentTypeHeaders() + // .Respond(HttpStatusCode.NotFound, "application/json", errorMessage); + + // var exception = await Assert.ThrowsAsync( + // () => this.keycloakUserClient.UpdateUser("master", userId.ToString(), new User()) + // ); + + // exception.StatusCode.Should().Be(HttpStatusCode.NotFound); + // exception.Content.Should().Be(errorMessage); + // this.handler.VerifyNoOutstandingExpectation(); + // } + + // [Fact] + // public async Task DeleteUserShouldCallCorrectEndpoint() + // { + // var userId = Guid.NewGuid(); + + // this.handler.Expect(HttpMethod.Delete, $"{BaseAddress}/admin/realms/master/users/{userId}") + // .WithAcceptAndContentTypeHeaders() + // .WithContent(string.Empty) + // .Respond(HttpStatusCode.NoContent); + + // await this.keycloakUserClient.DeleteUser("master", userId.ToString()); + + // this.handler.VerifyNoOutstandingExpectation(); + // } + + // [Fact] + // public async Task DeleteUserShouldThrowNotFoundApiExceptionWhenUserDoesNotExist() + // { + // var userId = Guid.NewGuid(); + // const string errorMessage = /*lang=json,strict*/ + // "{\"errorMessage\":\"User name is missing\"}"; + + // this.handler.Expect( + // HttpMethod.Delete, + // $"{BaseAddress}/admin/realms/master/users/{userId.ToString()}" + // ) + // .WithAcceptAndContentTypeHeaders() + // .Respond(HttpStatusCode.BadRequest, "application/json", errorMessage); + + // var exception = await Assert.ThrowsAsync( + // () => this.keycloakUserClient.DeleteUser("master", userId.ToString()) + // ); + + // exception.StatusCode.Should().Be(HttpStatusCode.BadRequest); + // exception.Content.Should().Be(errorMessage); + // this.handler.VerifyNoOutstandingExpectation(); + // } + + // [Fact] + // public async Task SendVerifyEmailShouldCallCorrectEndpoint() + // { + // var userId = Guid.NewGuid(); + + // this.handler.Expect( + // HttpMethod.Put, + // $"{BaseAddress}/admin/realms/master/users/{userId}/send-verify-email" + // ) + // .WithAcceptHeader() + // .Respond(HttpStatusCode.NoContent); + + // await this.keycloakUserClient.SendVerifyEmail("master", userId.ToString()); + + // this.handler.VerifyNoOutstandingExpectation(); + // } + + // [Fact] + // public async Task SendVerifyEmailShouldCallCorrectEndpointWithOptionalQueryParameters() + // { + // var userId = Guid.NewGuid(); + // const string clientId = "client-id"; + // const string redirectUri = "https://localhost:5001"; + + // var url = + // $"{BaseAddress}/admin/realms/master/users/{userId}/send-verify-email" + // + $"?client_id={clientId}" + // + $"&redirect_uri={redirectUri}"; + + // this.handler.Expect(HttpMethod.Put, url) + // .WithAcceptHeader() + // .Respond(HttpStatusCode.NoContent); + + // await this.keycloakUserClient.SendVerifyEmail( + // "master", + // userId.ToString(), + // clientId, + // redirectUri + // ); + + // this.handler.VerifyNoOutstandingExpectation(); + // } + + // [Fact] + // public async Task SendVerifyEmailShouldThrowNotFoundApiExceptionWhenUserDoesNotExist() + // { + // var userId = Guid.NewGuid(); + // const string errorMessage = /*lang=json,strict*/ + // "{\"error\":\"User not found\"}"; + + // this.handler.Expect( + // HttpMethod.Put, + // $"{BaseAddress}/admin/realms/master/users/{userId}/send-verify-email" + // ) + // .WithAcceptHeader() + // .Respond(HttpStatusCode.NotFound, "application/json", errorMessage); + + // var exception = await Assert.ThrowsAsync( + // () => this.keycloakUserClient.SendVerifyEmail("master", userId.ToString()) + // ); + + // exception.StatusCode.Should().Be(HttpStatusCode.NotFound); + // exception.Content.Should().Be(errorMessage); + // this.handler.VerifyNoOutstandingExpectation(); + // } private static string GetUserRepresentation(Guid userId) => - /*lang=json,strict*/$@"{{ - ""id"": ""{userId}"", - ""createdTimestamp"": 1670000000000, - ""username"": ""email@domain.com"", - ""enabled"": true, - ""totp"": false, - ""emailVerified"": true, - ""firstName"": ""Test"", - ""lastName"": ""User"", - ""email"": ""email@domain.com"", - ""disableableCredentialTypes"": [], - ""requiredActions"": [], - ""notBefore"": 0, - ""access"": {{ - ""manageGroupMembership"": true, - ""view"": true, - ""mapRoles"": true, - ""impersonate"": true, - ""manage"": true - }} - }}"; + /*lang=json,strict*/""" + { + "id": "{userId}", + "createdTimestamp": 1670000000000, + "username": "email@domain.com", + "enabled": true, + "totp": false, + "emailVerified": true, + "firstName": "Test", + "lastName": "User", + "email": "email@domain.com", + "disableableCredentialTypes": [], + "requiredActions": [], + "notBefore": 0, + "access": { + "manageGroupMembership": true, + "view": true, + "mapRoles": true, + "impersonate": true, + "manage": true + } + } + """.Replace("{userId}", userId.ToString()); } From 64f0aa69a6b2b55f7f527d8e08022241e48d11a4 Mon Sep 17 00:00:00 2001 From: nikiforovall Date: Fri, 3 May 2024 23:43:09 +0300 Subject: [PATCH 3/6] feat: Finish UserClient; add AuthorzationOptions overloads --- .../configuration-authorization.md | 21 +- .../PoliciesBuilderExtensions.cs | 1 + .../Requirements/ResourceAccessRequirement.cs | 2 +- .../ServiceCollectionExtensions.cs | 56 +-- .../Admin/ApiUrls.cs | 4 + .../Admin/IKeycloakGroupClient.cs | 67 +++ .../Admin/IKeycloakProtectedResourceClient.cs | 77 ++++ .../Admin/IKeycloakUserClient.cs | 375 +++++++++++++---- .../Admin/KeycloakClient.cs | 200 ++++++++- .../Groups/GetGroupRequestParameters.cs | 62 --- .../Users/ExecuteActionsEmailRequest.cs | 26 ++ .../Users/GetGroupRequestParameters.cs | 41 ++ .../ServiceCollectionExtensions.cs | 57 ++- .../PolicyTests.cs | 82 +++- .../KeycloakUserClientTests.cs | 392 +++++++++++------- 15 files changed, 1090 insertions(+), 373 deletions(-) delete mode 100644 src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupRequestParameters.cs create mode 100644 src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/ExecuteActionsEmailRequest.cs create mode 100644 src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/GetGroupRequestParameters.cs diff --git a/docs/configuration/configuration-authorization.md b/docs/configuration/configuration-authorization.md index 34afd092..846fd496 100644 --- a/docs/configuration/configuration-authorization.md +++ b/docs/configuration/configuration-authorization.md @@ -15,13 +15,30 @@ With [Keycloak.AuthServices.Authorization](https://www.nuget.org/packages/Keyclo <<< @/../tests/Keycloak.AuthServices.IntegrationTests/PolicyTests.cs#RequireClientRoles_TestClientRole_Verified -Configure default source: +Configure default source. The client name is taken from the `KeycloakAuthorizationOptions.Resource`: <<< @/../tests/Keycloak.AuthServices.IntegrationTests/PolicyTests.cs#RequireClientRoles_TestClientRoleWithConfiguration_Verified +```json +{ + "Keycloak": { + "resource": "test-client" + } +} +``` + +> [!IMPORTANT] +> If you don't configure the default source `KeycloakException` exception will be thrown. + +Override default source with `KeycloakAuthorizationOptions.RolesResource`: + +<<< @/../tests/Keycloak.AuthServices.IntegrationTests/PolicyTests.cs#RequireClientRoles_TestClientRoleWithInlineConfiguration_Verified + +Note it has more priority over the `KeycloakAuthorizationOptions.Resource` + ## Keycloak Role Claims Transformation -Keycloak roles can be automatically transformed to [AspNetCore Roles](https://learn.microsoft.com/en-us/aspnet/core/security/authorization/roles). This feature is disabled by default and is based on `KeycloakRolesClaimsTransformation`. +Keycloak roles can be automatically transformed to [AspNetCore Roles](https://learn.microsoft.com/en-us/aspnet/core/security/authorization/roles). This feature is **disabled by default** and is based on `KeycloakRolesClaimsTransformation`. Specify `KeycloakAuthorizationOptions.EnableRolesMapping` to enable it. E.g.: diff --git a/src/Keycloak.AuthServices.Authorization/PoliciesBuilderExtensions.cs b/src/Keycloak.AuthServices.Authorization/PoliciesBuilderExtensions.cs index 1c52febb..10b9c78f 100644 --- a/src/Keycloak.AuthServices.Authorization/PoliciesBuilderExtensions.cs +++ b/src/Keycloak.AuthServices.Authorization/PoliciesBuilderExtensions.cs @@ -11,6 +11,7 @@ public static class PoliciesBuilderExtensions { /// /// Adds resource role requirement to builder. Ensures that at least one resource role is present in resource claims. + /// Note, make sure role source is configure. See documentation for more details. /// /// /// diff --git a/src/Keycloak.AuthServices.Authorization/Requirements/ResourceAccessRequirement.cs b/src/Keycloak.AuthServices.Authorization/Requirements/ResourceAccessRequirement.cs index 0091a2a3..230e50d8 100644 --- a/src/Keycloak.AuthServices.Authorization/Requirements/ResourceAccessRequirement.cs +++ b/src/Keycloak.AuthServices.Authorization/Requirements/ResourceAccessRequirement.cs @@ -82,7 +82,7 @@ ResourceAccessRequirement requirement if (string.IsNullOrWhiteSpace(clientId)) { throw new KeycloakException( - $"Unable to resolve Resource for Role Validation - please make sure {nameof(KeycloakAuthorizationOptions)} are configured." + $"Unable to resolve Resource for Role Validation - please make sure {nameof(KeycloakAuthorizationOptions)} are configured. \n\n See documentation for more details - https://nikiforovall.github.io/keycloak-authorization-services-dotnet/configuration/configuration-authorization.html#require-resource-roles" ); } diff --git a/src/Keycloak.AuthServices.Authorization/ServiceCollectionExtensions.cs b/src/Keycloak.AuthServices.Authorization/ServiceCollectionExtensions.cs index 72c43206..f574d7ea 100644 --- a/src/Keycloak.AuthServices.Authorization/ServiceCollectionExtensions.cs +++ b/src/Keycloak.AuthServices.Authorization/ServiceCollectionExtensions.cs @@ -20,26 +20,33 @@ public static class ServiceCollectionExtensions /// /// /// - /// + /// /// - [Obsolete( - "Method overload will be removed in future versions. Use AddKeycloakAuthorization together with AddAuthorizationServer instead" - )] public static IServiceCollection AddKeycloakAuthorization( this IServiceCollection services, IConfiguration configuration, - string keycloakClientSectionName = KeycloakProtectionClientOptions.Section + string configSectionName = KeycloakAuthorizationOptions.Section ) { - services.AddAuthorizationServer(configuration, keycloakClientSectionName); - - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + var configurationSection = configuration.GetSection(configSectionName); - return services; + return services.AddKeycloakAuthorization(configurationSection); } + /// + /// Adds keycloak authorization services + /// + /// + /// + /// + public static IServiceCollection AddKeycloakAuthorization( + this IServiceCollection services, + IConfigurationSection configurationSection + ) => + services.AddKeycloakAuthorization(options => + configurationSection.BindKeycloakOptions(options) + ); + /// /// Adds keycloak authorization services /// @@ -50,6 +57,9 @@ public static IServiceCollection AddKeycloakAuthorization( Action? configureKeycloakAuthorizationOptions = null ) { + configureKeycloakAuthorizationOptions ??= _ => { }; + services.Configure(configureKeycloakAuthorizationOptions); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -66,9 +76,6 @@ public static IServiceCollection AddKeycloakAuthorization( keycloakOptions.RolesResource ?? keycloakOptions.Resource ); }); - configureKeycloakAuthorizationOptions ??= _ => { }; - - services.Configure(configureKeycloakAuthorizationOptions); return services; } @@ -84,14 +91,13 @@ public static IServiceCollection AddKeycloakAuthorization( public static IHttpClientBuilder AddAuthorizationServer( this IServiceCollection services, IConfiguration configuration, - string configSectionName = KeycloakProtectionClientOptions.Section, - Action? configureClient = default - ) - { - var configurationSection = configuration.GetSection(configSectionName); - - return services.AddAuthorizationServer(configurationSection, configureClient); - } + Action? configureClient = default, + string configSectionName = KeycloakProtectionClientOptions.Section + ) => + services.AddAuthorizationServer( + configuration.GetSection(configSectionName), + configureClient + ); /// /// Adds Keycloak Protection client and auto header propagation @@ -123,10 +129,14 @@ public static IHttpClientBuilder AddAuthorizationServer( Action? configureClient = default ) { + configureKeycloakOptions ??= _ => { }; + services.Configure(configureKeycloakOptions); + services.AddHttpContextAccessor(); services.AddSingleton(); + // (!) resolved locally, will not work with PostConfigure and IOptions pattern var keycloakOptions = new KeycloakProtectionClientOptions(); configureKeycloakOptions.Invoke(keycloakOptions); @@ -135,8 +145,6 @@ public static IHttpClientBuilder AddAuthorizationServer( services.AddSingleton(); } - services.AddOptions().Configure(configureKeycloakOptions); - return services .AddHttpClient() .ConfigureHttpClient( diff --git a/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs b/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs index 3e3e74bb..52c4cd74 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs @@ -42,6 +42,10 @@ internal static class ApiUrls internal const string ExecuteActionsEmail = $"{GetRealm}/users/{{id}}/execute-actions-email"; internal const string GetUserGroups = $"{GetRealm}/users/{{id}}/groups"; + + internal const string JoinGroup = $"{GetRealm}/users/{{id}}/groups/{{groupId}}"; + + internal const string LeaveGroup = $"{GetRealm}/users/{{id}}/groups/{{groupId}}"; #endregion #region Group API diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs index 8bbb16f4..4647c2c1 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs @@ -43,4 +43,71 @@ public interface IKeycloakGroupClient // /// Group representation. // /// // Task UpdateGroup(string realm, string groupId, Group group); + + // /// + // /// Get a stream of groups on the realm. + // /// + // /// Realm name (not ID). + // /// Optional query parameters. + // /// A stream of groups, filtered according to query parameters. + // [Get(KeycloakClientApiConstants.GetGroups)] + // [Headers("Accept: application/json")] + // Task> GetGroups(string realm, [Query] GetGroupsRequestParameters? parameters = default); + + // /// + // /// Get representation of a Group. + // /// + // /// Realm name (not ID). + // /// Group ID. + // /// The group representation. + // [Get(KeycloakClientApiConstants.GetGroup)] + // [Headers("Accept: application/json")] + // Task GetGroup(string realm, [AliasAs("id")] string groupId); + + // /// + // /// Create a new Group. + // /// + // /// + // /// Name must be unique. + // /// + // /// Realm name (not ID). + // /// Group representation. + // /// + // [Post(KeycloakClientApiConstants.CreateGroup)] + // [Headers("Content-Type: application/json")] + // Task CreateGroup(string realm, [Body] Group group); + + // /// + // /// Update the Group. + // /// + // /// Realm name (not ID). + // /// Group ID. + // /// Group representation. + // /// + // [Put(KeycloakClientApiConstants.UpdateGroup)] + // [Headers("Content-Type: application/json")] + // Task UpdateGroup(string realm, [AliasAs("id")] string groupId, [Body] Group group); + + // /// + // /// Create a new child group. + // /// + // /// + // /// Name must be unique. + // /// + // /// Realm name (not ID). + // /// Group ID. + // /// Group representation. + // /// + // [Post(KeycloakClientApiConstants.CreateChildGroup)] + // [Headers("Content-Type: application/json")] + // Task CreateChildGroup(string realm, [AliasAs("id")] string groupId, [Body] Group group); + + // /// + // /// Delete a group. Note: the Keycloak documentation does not specify if this deletes subgroups. + // /// + // /// Realm name (not ID). + // /// Group ID. + // /// + // [Delete(KeycloakClientApiConstants.DeleteGroup)] + // Task DeleteGroup(string realm, [AliasAs("id")] string groupId); } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakProtectedResourceClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakProtectedResourceClient.cs index 098569e7..23d23a03 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakProtectedResourceClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakProtectedResourceClient.cs @@ -50,4 +50,81 @@ public interface IKeycloakProtectedResourceClient // /// // /// // Task UpdateResource(string realm, string id, Resource resource); + + // /// + // /// Searches for resource + // /// + // /// Realm name (not ID). + // /// Optional query parameters + // /// + // [Get(KeycloakClientApiConstants.GetResources)] + // [Headers("Accept: application/json")] + // Task> GetResources(string realm, [Query] GetResourcesRequestParameters? getResourcesRequestParameters = default); + + // /// + // /// Searches for resources. NOTE: you must set the property to true + // /// + // /// Realm name (not ID). + // /// Query parameters + // /// + // [Get(KeycloakClientApiConstants.GetResources)] + // [Headers("Accept: application/json")] + // Task> GetResourcesDeep(string realm, [Query] GetResourcesDeepRequestParameters getResourcesDeepRequestParameters); + + // /// + // /// Gets resource by Id + // /// + // /// Realm name (not ID). + // /// Resource ID. + // /// + // [Get(KeycloakClientApiConstants.GetResource)] + // [Headers("Accept: application/json")] + // Task GetResource(string realm, [AliasAs("id")] string resourceId); + + // /// + // /// Gets resource by Name + // /// + // /// + // /// https://github.com/keycloak/keycloak/blob/main/docs/documentation/authorization_services/topics/service-protection-resources-api-papi.adoc#querying-resources + // /// + // /// Realm name (not ID). + // /// + // /// + // [Get(KeycloakClientApiConstants.GetResourceByExactName)] + // [Headers("Accept: application/json")] + // Task SearchResourcesByName(string realm, [Query] string name); + + // /// + // /// Creates resource + // /// + // /// Realm name (not ID). + // /// + // /// + // [Post(KeycloakClientApiConstants.CreateResource)] + // [Headers("Accept: application/json", "Content-Type: application/json")] + // Task CreateResource(string realm, [Body] Resource resource); + + // /// + // /// Updates resource + // /// + // /// + // /// Docs: https://github.com/keycloak/keycloak-documentation/blob/main/authorization_services/topics/service-protection-resources-api-papi.adoc#updating-resources + // /// + // /// Realm name (not ID). + // /// Resource ID. + // /// + // /// + // [Put(KeycloakClientApiConstants.PutResource)] + // [Headers("Accept: application/json", "Content-Type: application/json")] + // Task UpdateResource(string realm, [AliasAs("id")] string resourceId, [Body] Resource resource); + + // /// + // /// Delete a resource + // /// + // /// Realm name (not ID). + // /// Resource ID. + // /// + // [Delete(KeycloakClientApiConstants.DeleteResource)] + // [Headers("Accept: application/json")] + // Task DeleteResource(string realm, [AliasAs("id")] string resourceId); } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs index 6b35c08b..9b3dbede 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs @@ -15,18 +15,11 @@ public interface IKeycloakUserClient /// Optional query parameters. /// /// A stream of users, filtered according to query parameters. - async Task> GetUsersAsync( + Task GetUsersWithResponseAsync( string realm, GetUsersRequestParameters? parameters = default, CancellationToken cancellationToken = default - ) - { - var response = await this.GetUsersWithResponseAsync(realm, parameters, cancellationToken); - - return ( - await response.GetResponseAsync>(cancellationToken) - )!; - } + ); /// /// Get a stream of users on the realm. @@ -35,11 +28,17 @@ await response.GetResponseAsync>(cancellationTok /// Optional query parameters. /// /// A stream of users, filtered according to query parameters. - Task GetUsersWithResponseAsync( + async Task> GetUsersAsync( string realm, GetUsersRequestParameters? parameters = default, CancellationToken cancellationToken = default - ); + ) + { + var response = await this.GetUsersWithResponseAsync(realm, parameters, cancellationToken); + + return await response.GetResponseAsync>(cancellationToken) + ?? Enumerable.Empty(); + } /// /// Get representation of a user. @@ -63,7 +62,7 @@ async Task GetUserAsync( cancellationToken ); - return (await response.GetResponseAsync(cancellationToken))!; + return await response.GetResponseAsync(cancellationToken) ?? new(); } // @@ -81,6 +80,22 @@ Task GetUserWithResponseAsync( CancellationToken cancellationToken = default ); + /// + /// Create a new user. + /// + /// + /// Username must be unique. + /// + /// Realm name (not ID). + /// User representation. + /// + /// + Task CreateUserWithResponseAsync( + string realm, + UserRepresentation user, + CancellationToken cancellationToken = default + ); + /// /// Create a new user. /// @@ -103,83 +118,281 @@ async Task CreateUserAsync( } /// - /// Create a new user. + /// Update the user. /// - /// - /// Username must be unique. - /// /// Realm name (not ID). + /// User ID. /// User representation. /// /// - Task CreateUserWithResponseAsync( + Task UpdateUserWithResponseAsync( string realm, + string userId, UserRepresentation user, CancellationToken cancellationToken = default ); - // /// - // /// Update the user. - // /// - // /// Realm name (not ID). - // /// User ID. - // /// User representation. - // /// - // Task UpdateUser(string realm, string userId, User user); - - // /// - // /// Delete the given user. - // /// - // /// Realm name (not ID). - // /// User ID. - // /// - // Task DeleteUser(string realm, string userId); - - // /// - // /// Send an email-verification email to the user. - // /// - // /// - // /// An email contains a link the user can click to verify their email address. - // /// - // /// Realm name (not ID). - // /// User ID. - // /// Client ID. - // /// Redirect URI. The default for the redirect is the account client. - // Task SendVerifyEmail( - // string realm, - // string userId, - // string? clientId = default, - // string? redirectUri = default - // ); - - // /// - // /// Execute actions email for the user. - // /// - // /// Realm name (not ID). - // /// User ID. - // /// Client ID. - // /// Number of seconds after which the generated token expires - // /// Redirect URI. The default for the redirect is the account client. - // /// Actions - // Task ExecuteActionsEmail( - // string realm, - // string userId, - // string? clientId = default, - // int? lifespan = default, - // string? redirectUri = default, - // List? actions = default - // ); - - // /// - // /// Get a users's groups. - // /// - // /// Realm name (not ID). - // /// User ID. - // /// Optional query parameters. - // /// A stream of users, filtered according to query parameters. - // Task> GetUserGroups( - // string realm, - // string userId, - // GetGroupRequestParameters? parameters = default - // ); + /// + /// Update the user. + /// + /// Realm name (not ID). + /// User ID. + /// User representation. + /// + /// + async Task UpdateUserAsync( + string realm, + string userId, + UserRepresentation user, + CancellationToken cancellationToken = default + ) + { + var response = await this.UpdateUserWithResponseAsync( + realm, + userId, + user, + cancellationToken + ); + + await response.EnsureResponseAsync(cancellationToken); + } + + /// + /// Delete the given user. + /// + /// Realm name (not ID). + /// User ID. + /// + /// + Task DeleteUserWithResponseAsync( + string realm, + string userId, + CancellationToken cancellationToken = default + ); + + /// + /// Delete the given user. + /// + /// Realm name (not ID). + /// User ID. + /// + /// + async Task DeleteUserAsync( + string realm, + string userId, + CancellationToken cancellationToken = default + ) + { + var response = await this.DeleteUserWithResponseAsync(realm, userId, cancellationToken); + + await response.EnsureResponseAsync(cancellationToken); + } + + /// + /// Send an email-verification email to the user. + /// + /// + /// An email contains a link the user can click to verify their email address. + /// + /// Realm name (not ID). + /// User ID. + /// Client ID. + /// Redirect URI. The default for the redirect is the account client. + /// + Task SendVerifyEmailWithResponseAsync( + string realm, + string userId, + string? clientId = default, + string? redirectUri = default, + CancellationToken cancellationToken = default + ); + + /// + /// Send an email-verification email to the user. + /// + /// + /// An email contains a link the user can click to verify their email address. + /// + /// Realm name (not ID). + /// User ID. + /// Client ID. + /// Redirect URI. The default for the redirect is the account client. + /// + async Task SendVerifyEmailAsync( + string realm, + string userId, + string? clientId = default, + string? redirectUri = default, + CancellationToken cancellationToken = default + ) + { + var response = await this.SendVerifyEmailWithResponseAsync( + realm, + userId, + clientId, + redirectUri, + cancellationToken + ); + + await response.EnsureResponseAsync(cancellationToken); + } + + /// + /// Execute actions email for the user. + /// + /// Realm name (not ID). + /// User ID. + /// + /// + Task ExecuteActionsEmailWithResponseAsync( + string realm, + string userId, + ExecuteActionsEmailRequest request, + CancellationToken cancellationToken = default + ); + + /// + /// Execute actions email for the user. + /// + /// Realm name (not ID). + /// User ID. + /// + /// + async Task ExecuteActionsEmailAsync( + string realm, + string userId, + ExecuteActionsEmailRequest request, + CancellationToken cancellationToken = default + ) + { + var response = await this.ExecuteActionsEmailWithResponseAsync( + realm, + userId, + request, + cancellationToken + ); + + await response.EnsureResponseAsync(cancellationToken); + } + + /// + /// Get a users's groups. + /// + /// Realm name (not ID). + /// User ID. + /// Optional query parameters. + /// + /// A stream of users, filtered according to query parameters. + Task GetUserGroupsWithResponseAsync( + string realm, + string userId, + GetUserGroupsRequestParameters parameters, + CancellationToken cancellationToken = default + ); + + /// + /// Get a users's groups. + /// + /// Realm name (not ID). + /// User ID. + /// Optional query parameters. + /// + /// A stream of users, filtered according to query parameters. + async Task> GetUserGroupsAsync( + string realm, + string userId, + GetUserGroupsRequestParameters parameters, + CancellationToken cancellationToken = default + ) + { + var response = await this.GetUserGroupsWithResponseAsync( + realm, + userId, + parameters, + cancellationToken + ); + + return await response.GetResponseAsync>(cancellationToken) + ?? Enumerable.Empty(); + } + + /// + /// Join a group + /// + /// Realm name(not ID). + /// User ID. + /// Group ID. + /// + /// + Task JoinGroupWithResponseAsync( + string realm, + string userId, + string groupId, + CancellationToken cancellationToken = default + ); + + /// + /// Join a group + /// + /// Realm name(not ID). + /// User ID. + /// Group ID. + /// + /// + async Task JoinGroupAsync( + string realm, + string userId, + string groupId, + CancellationToken cancellationToken = default + ) + { + var response = await this.JoinGroupWithResponseAsync( + realm, + userId, + groupId, + cancellationToken + ); + + await response.EnsureResponseAsync(cancellationToken); + } + + /// + /// Leave a group + /// + /// Realm name(not ID). + /// User ID. + /// Group ID. + /// + /// + Task LeaveGroupWithResponseAsync( + string realm, + string userId, + string groupId, + CancellationToken cancellationToken = default + ); + + /// + /// Leave a group + /// + /// Realm name(not ID). + /// User ID. + /// Group ID. + /// + /// + async Task LeaveGroupAsync( + string realm, + string userId, + string groupId, + CancellationToken cancellationToken = default + ) + { + var response = await this.LeaveGroupWithResponseAsync( + realm, + userId, + groupId, + cancellationToken + ); + + await response.EnsureResponseAsync(cancellationToken); + } } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs index ba981ebb..1d66aa7d 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs @@ -19,32 +19,35 @@ public partial class KeycloakClient : IKeycloakClient /// public KeycloakClient(HttpClient httpClient) => this.httpClient = httpClient; + #region Realm /// - public async Task CreateUserWithResponseAsync( + public async Task GetRealmWithResponseAsync( string realm, - UserRepresentation user, CancellationToken cancellationToken = default ) { - var path = ApiUrls.CreateUser.Replace(RealmParam, realm); + var path = ApiUrls.GetRealm.Replace(RealmParam, realm); - var responseMessage = await this.httpClient.PostAsJsonAsync(path, user, cancellationToken); + var responseMessage = await this.httpClient.GetAsync( + path, + cancellationToken: cancellationToken + ); return responseMessage!; } + #endregion + #region User /// - public async Task GetRealmWithResponseAsync( + public async Task CreateUserWithResponseAsync( string realm, + UserRepresentation user, CancellationToken cancellationToken = default ) { - var path = ApiUrls.GetRealm.Replace(RealmParam, realm); + var path = ApiUrls.CreateUser.Replace(RealmParam, realm); - var responseMessage = await this.httpClient.GetAsync( - path, - cancellationToken: cancellationToken - ); + var responseMessage = await this.httpClient.PostAsJsonAsync(path, user, cancellationToken); return responseMessage!; } @@ -113,4 +116,181 @@ public async Task GetUserWithResponseAsync( return responseMessage!; } + + /// + public async Task UpdateUserWithResponseAsync( + string realm, + string userId, + UserRepresentation user, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.UpdateUser.Replace(RealmParam, realm).Replace("{id}", userId); + + var responseMessage = await this.httpClient.PutAsJsonAsync(path, user, cancellationToken); + + return responseMessage!; + } + + /// + public async Task DeleteUserWithResponseAsync( + string realm, + string userId, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.DeleteUser.Replace(RealmParam, realm).Replace("{id}", userId); + + var responseMessage = await this.httpClient.DeleteAsync(path, cancellationToken); + + return responseMessage!; + } + + /// + public async Task SendVerifyEmailWithResponseAsync( + string realm, + string userId, + string? clientId = null, + string? redirectUri = null, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.SendVerifyEmail.Replace(RealmParam, realm).Replace("{id}", userId); + + var queryBuilder = new QueryBuilder(); + + if (clientId is not null) + { + queryBuilder.Add("client_id", clientId); + } + + if (redirectUri is not null) + { + queryBuilder.Add("redirect_uri", redirectUri); + } + + var url = path + queryBuilder.ToQueryString(); + + var responseMessage = await this.httpClient.PutAsync( + url, + new StringContent(string.Empty), + cancellationToken + ); + + return responseMessage!; + } + + /// + public async Task ExecuteActionsEmailWithResponseAsync( + string realm, + string userId, + ExecuteActionsEmailRequest request, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.ExecuteActionsEmail.Replace("{realm}", realm).Replace("{id}", userId); + + var queryBuilder = new QueryBuilder(); + + if (request.ClientId is not null) + { + queryBuilder.Add("client_id", request.ClientId); + } + + if (request.RedirectUri is not null) + { + queryBuilder.Add("redirect_uri", request.RedirectUri); + } + + if (request.Lifespan.HasValue) + { + queryBuilder.Add("lifespan", request.Lifespan?.ToString()!); + } + + var url = path + queryBuilder.ToQueryString(); + + var responseMessage = await this.httpClient.PutAsJsonAsync( + url, + request.Actions, + cancellationToken + ); + + return responseMessage!; + } + + /// + public async Task GetUserGroupsWithResponseAsync( + string realm, + string userId, + GetUserGroupsRequestParameters parameters, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.GetUserGroups.Replace("{realm}", realm).Replace("{id}", userId); + + var queryBuilder = new QueryBuilder(); + + if (parameters.BriefRepresentation.HasValue) + { + queryBuilder.Add("briefRepresentation", parameters.BriefRepresentation.ToString()); + } + + if (parameters.First.HasValue) + { + queryBuilder.Add("first", parameters.First.ToString()); + } + + if (parameters.Max.HasValue) + { + queryBuilder.Add("max", parameters.Max.ToString()!); + } + + var url = path + queryBuilder.ToQueryString(); + + var responseMessage = await this.httpClient.GetAsync(url, cancellationToken); + + return responseMessage!; + } + + /// + public async Task JoinGroupWithResponseAsync( + string realm, + string userId, + string groupId, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls + .JoinGroup.Replace(RealmParam, realm) + .Replace("{id}", userId) + .Replace("{groupId}", groupId); + + var responseMessage = await this.httpClient.PutAsync( + path, + new StringContent(string.Empty), + cancellationToken + ); + + return responseMessage!; + } + + /// + public async Task LeaveGroupWithResponseAsync( + string realm, + string userId, + string groupId, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls + .LeaveGroup.Replace(RealmParam, realm) + .Replace("{id}", userId) + .Replace("{groupId}", groupId); + + var responseMessage = await this.httpClient.DeleteAsync(path, cancellationToken); + + return responseMessage!; + } + + #endregion } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupRequestParameters.cs b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupRequestParameters.cs deleted file mode 100644 index 3986f2c5..00000000 --- a/src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupRequestParameters.cs +++ /dev/null @@ -1,62 +0,0 @@ -namespace Keycloak.AuthServices.Sdk.Admin.Requests.Groups; - -/// -/// Optional request parameters for the GetGroups endpoint. -/// It can be called in three different ways. -/// -/// -/// -/// Don’t specify any criteria. A stream of all groups within that realm will be returned (limited by pagination). -/// -/// -/// -/// -/// If is specified, other criteria such as LastName -/// will be ignored even though you may set them. The string will be matched against -/// the User.FirstName, User.LastName, User.Username -/// and the User.Email of a User/>. -/// -/// -/// -/// -/// If is unspecified but any of LastName, FirstName, -/// Email or Username are specified, then those criteria are matched against -/// their respective fields on a User entity. Combined with a logical AND. -/// -/// -/// -/// -public class GetGroupRequestParameters -{ - /// - /// Defines whether brief representations are returned. Default is false. - /// - public bool? BriefRepresentation { get; init; } - - /// - /// Defines whether the params LastName, FirstName, - /// Email and Username must match exactly - /// - public bool? Exact { get; init; } - - /// - /// Pagination offset. - /// - public int? First { get; init; } - - /// - /// Maximum results size. Default is 100. - /// - public int? Max { get; init; } - - /// - /// A query to search for custom attributes, in the format "key1:value2 key2:value2". - /// - public string? Query { get; init; } - - /// - /// Search for a string contained in Username, FirstName, - /// LastName or Email. - /// - public string? Search { get; init; } -} diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/ExecuteActionsEmailRequest.cs b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/ExecuteActionsEmailRequest.cs new file mode 100644 index 00000000..56fa5bbc --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/ExecuteActionsEmailRequest.cs @@ -0,0 +1,26 @@ +namespace Keycloak.AuthServices.Sdk.Admin.Requests.Users; + +/// +/// Send an email to the user with a link they can click to execute particular actions. +/// +public class ExecuteActionsEmailRequest +{ + /// + /// Client id + /// + public string? ClientId { get; init; } + + /// + /// Number of seconds after which the generated token expires + /// + public int? Lifespan { get; init; } + + /// + /// Redirect uri + /// + public string? RedirectUri { get; init; } + + /// + /// + public List? Actions { get; init; } +} diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/GetGroupRequestParameters.cs b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/GetGroupRequestParameters.cs new file mode 100644 index 00000000..9f99baa6 --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/GetGroupRequestParameters.cs @@ -0,0 +1,41 @@ +namespace Keycloak.AuthServices.Sdk.Admin.Requests.Users; + +/// +/// Optional request parameters for the GetUserGroups endpoint. +/// It can be called in three different ways. +/// +/// +/// +/// Don’t specify any criteria. A stream of all groups within that realm will be returned (limited by pagination). +/// +/// +/// +/// +/// If is specified, other criteria will be ignored even though you may set them. +/// +/// +/// +/// +public class GetUserGroupsRequestParameters +{ + /// + /// Defines whether brief representations are returned. Default is false. + /// + public bool? BriefRepresentation { get; init; } + + /// + /// Pagination offset. + /// + public int? First { get; init; } + + /// + /// Maximum results size. Default is 100. + /// + public int? Max { get; init; } + + /// + /// Search for a string contained in Username, FirstName, + /// LastName or Email. + /// + public string? Search { get; init; } +} diff --git a/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs b/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs index ad005b3a..269703fa 100644 --- a/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs +++ b/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs @@ -11,6 +11,42 @@ namespace Keycloak.AuthServices.Sdk; /// public static class ServiceCollectionExtensions { + /// + /// TBD: + /// + /// + /// + /// + /// + /// + public static IHttpClientBuilder AddKeycloakAdminHttpClient( + this IServiceCollection services, + IConfiguration configuration, + Action? configureClient = default, + string keycloakClientSectionName = KeycloakAdminClientOptions.Section + ) => + services.AddKeycloakAdminHttpClient( + options => configuration.BindKeycloakOptions(options, keycloakClientSectionName), + configureClient + ); + + /// + /// TBD: + /// + /// + /// + /// + /// + public static IHttpClientBuilder AddKeycloakAdminHttpClient( + this IServiceCollection services, + IConfigurationSection configurationSection, + Action? configureClient = default + ) => + services.AddKeycloakAdminHttpClient( + options => configurationSection.BindKeycloakOptions(options), + configureClient + ); + /// /// TBD: /// @@ -24,7 +60,7 @@ public static IHttpClientBuilder AddKeycloakAdminHttpClient( Action? configureClient = default ) { - services.Configure(configureKeycloakOptions); + services.Configure(configureKeycloakOptions); services.AddTransient(sp => sp.GetRequiredService()); services.AddTransient(sp => @@ -75,23 +111,4 @@ void configureKeycloakOptions(KeycloakAdminClientOptions options) return services.AddKeycloakAdminHttpClient(configureKeycloakOptions, configureClient); } - - /// - /// Adds keycloak confidential client and underlying token management - /// - /// - /// - /// - /// - /// - public static IHttpClientBuilder AddKeycloakAdminHttpClient( - this IServiceCollection services, - IConfiguration configuration, - Action? configureClient = default, - string keycloakClientSectionName = KeycloakAdminClientOptions.Section - ) => - services.AddKeycloakAdminHttpClient( - options => configuration.BindKeycloakOptions(options, keycloakClientSectionName), - configureClient - ); } diff --git a/tests/Keycloak.AuthServices.IntegrationTests/PolicyTests.cs b/tests/Keycloak.AuthServices.IntegrationTests/PolicyTests.cs index 90f86fcd..e8a98f06 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/PolicyTests.cs +++ b/tests/Keycloak.AuthServices.IntegrationTests/PolicyTests.cs @@ -64,10 +64,7 @@ await host.Scenario(_ => await host.Scenario(_ => { _.Get.Url(RunPolicyBuyName(policyName)); - _.UserAndPasswordIs( - TestUsers.Tester.UserName, - TestUsers.Tester.Password - ); + _.UserAndPasswordIs(TestUsers.Tester.UserName, TestUsers.Tester.Password); _.StatusCodeShouldBe(HttpStatusCode.Forbidden); }); } @@ -124,16 +121,13 @@ await host.Scenario(_ => await host.Scenario(_ => { _.Get.Url(RunPolicyBuyName(policyName)); - _.UserAndPasswordIs( - TestUsers.Tester.UserName, - TestUsers.Tester.Password - ); + _.UserAndPasswordIs(TestUsers.Tester.UserName, TestUsers.Tester.Password); _.StatusCodeShouldBe(HttpStatusCode.OK); }); } [Fact] - public async Task RequireClientRoles_TestClientRoleWithConfiguration_Verified() + public async Task RequireClientRoles_TestClientRoleWithInlineConfiguration_Verified() { var policyName = "RequireResourceRoles"; await using var host = await AlbaHost.For( @@ -145,7 +139,7 @@ public async Task RequireClientRoles_TestClientRoleWithConfiguration_Verified() x.ConfigureServices( (context, services) => { - #region RequireClientRoles_TestClientRoleWithConfiguration_Verified + #region RequireClientRoles_TestClientRoleWithInlineConfiguration_Verified services .AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddKeycloakWebApi(context.Configuration); @@ -161,6 +155,59 @@ public async Task RequireClientRoles_TestClientRoleWithConfiguration_Verified() policy => policy.RequireResourceRoles(KeycloakRoles.TestClientRole) ); + #endregion RequireClientRoles_TestClientRoleWithInlineConfiguration_Verified + + services.PostConfigure(options => + options.WithKeycloakFixture(this.Keycloak) + ); + } + ); + }, + UserPasswordFlow(ReadKeycloakAuthenticationOptions(AppSettings)) + ); + + await host.Scenario(_ => + { + _.Get.Url(RunPolicyBuyName(policyName)); + _.UserAndPasswordIs(TestUsers.Admin.UserName, TestUsers.Admin.Password); + _.StatusCodeShouldBe(HttpStatusCode.Forbidden); + }); + + await host.Scenario(_ => + { + _.Get.Url(RunPolicyBuyName(policyName)); + _.UserAndPasswordIs(TestUsers.Tester.UserName, TestUsers.Tester.Password); + _.StatusCodeShouldBe(HttpStatusCode.OK); + }); + } + + [Fact] + public async Task RequireClientRoles_TestClientRoleWithConfiguration_Verified() + { + var policyName = "RequireResourceRoles"; + await using var host = await AlbaHost.For( + x => + { + x.WithLogging(testOutputHelper); + x.WithConfiguration(AppSettings); + + x.ConfigureServices( + (context, services) => + { + #region RequireClientRoles_TestClientRoleWithConfiguration_Verified + services + .AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddKeycloakWebApi(context.Configuration); + + services + .AddAuthorization() + .AddKeycloakAuthorization(context.Configuration) + .AddAuthorizationBuilder() + .AddPolicy( + policyName, + policy => policy.RequireResourceRoles(KeycloakRoles.TestClientRole) + ); + #endregion RequireClientRoles_TestClientRoleWithConfiguration_Verified services.PostConfigure(options => @@ -182,10 +229,7 @@ await host.Scenario(_ => await host.Scenario(_ => { _.Get.Url(RunPolicyBuyName(policyName)); - _.UserAndPasswordIs( - TestUsers.Tester.UserName, - TestUsers.Tester.Password - ); + _.UserAndPasswordIs(TestUsers.Tester.UserName, TestUsers.Tester.Password); _.StatusCodeShouldBe(HttpStatusCode.OK); }); } @@ -243,10 +287,7 @@ await host.Scenario(_ => await host.Scenario(_ => { _.Get.Url(RunPolicyBuyName(policyName)); - _.UserAndPasswordIs( - TestUsers.Tester.UserName, - TestUsers.Tester.Password - ); + _.UserAndPasswordIs(TestUsers.Tester.UserName, TestUsers.Tester.Password); _.StatusCodeShouldBe(HttpStatusCode.Forbidden); }); } @@ -304,10 +345,7 @@ await host.Scenario(_ => await host.Scenario(_ => { _.Get.Url(RunPolicyBuyName(policyName)); - _.UserAndPasswordIs( - TestUsers.Tester.UserName, - TestUsers.Tester.Password - ); + _.UserAndPasswordIs(TestUsers.Tester.UserName, TestUsers.Tester.Password); _.StatusCodeShouldBe(HttpStatusCode.OK); }); } diff --git a/tests/Keycloak.AuthServices.Sdk.Tests/KeycloakUserClientTests.cs b/tests/Keycloak.AuthServices.Sdk.Tests/KeycloakUserClientTests.cs index 413661b4..6137c3e9 100644 --- a/tests/Keycloak.AuthServices.Sdk.Tests/KeycloakUserClientTests.cs +++ b/tests/Keycloak.AuthServices.Sdk.Tests/KeycloakUserClientTests.cs @@ -11,7 +11,7 @@ namespace Keycloak.AuthServices.Sdk.Tests; public class KeycloakUserClientTests { private const string BaseAddress = "http://localhost:8080"; - + private const string MediaType = "application/json"; private readonly MockHttpMessageHandler handler = new(); private readonly IKeycloakUserClient keycloakUserClient; @@ -29,7 +29,7 @@ public async Task GetUserShouldCallCorrectEndpoint(UserRepresentation userFixtur var userId = userFixture.Id; this.handler.Expect(HttpMethod.Get, $"/admin/realms/master/users/{userId}") - .Respond(HttpStatusCode.OK, "application/json", JsonSerializer.Serialize(userFixture)); + .Respond(HttpStatusCode.OK, MediaType, JsonSerializer.Serialize(userFixture)); var user = await this.keycloakUserClient.GetUserAsync("master", userId.ToString()); @@ -45,7 +45,7 @@ public async Task GetUserShouldShouldThrowNotFoundApiExceptionWhenUserDoesNotExi "{\"error\":\"User not found\"}"; this.handler.Expect(HttpMethod.Get, $"{BaseAddress}/admin/realms/master/users/{userId}") - .Respond(HttpStatusCode.NotFound, "application/json", errorMessage); + .Respond(HttpStatusCode.NotFound, MediaType, errorMessage); var exception = await FluentActions .Invoking(() => this.keycloakUserClient.GetUserAsync("master", userId.ToString())) @@ -73,7 +73,7 @@ public async Task GetUsersShouldCallCorrectEndpoint() var response = $"[{string.Join(",", users.Select(u => u.Representation))}]"; this.handler.Expect(HttpMethod.Get, $"{BaseAddress}/admin/realms/master/users") - .Respond(HttpStatusCode.OK, "application/json", response); + .Respond(HttpStatusCode.OK, MediaType, response); var result = await this.keycloakUserClient.GetUsersAsync("master"); @@ -102,7 +102,7 @@ public async Task GetUsersShouldCallCorrectEndpointWithOptionalQueryParameters() Username = "username" }; - var url = $"{BaseAddress}/admin/realms/master/users"; + var url = $"/admin/realms/master/users"; var queryBuilder = new QueryBuilder { { "briefRepresentation", "False" }, @@ -124,7 +124,7 @@ public async Task GetUsersShouldCallCorrectEndpointWithOptionalQueryParameters() var response = $"[{GetUserRepresentation(Guid.NewGuid())}]"; this.handler.Expect(HttpMethod.Get, url + queryBuilder.ToQueryString()) - .Respond(HttpStatusCode.OK, "application/json", response); + .Respond(HttpStatusCode.OK, MediaType, response); _ = await this.keycloakUserClient.GetUsersAsync("master", getUsersRequestParameters); @@ -152,7 +152,7 @@ public async Task CreateUserShouldReturnBadRequestWhenRequestIsInvalid() "{\"errorMessage\":\"User name is missing\"}"; this.handler.Expect(HttpMethod.Post, $"/admin/realms/master/users") - .Respond(HttpStatusCode.BadRequest, "application/json", errorMessage); + .Respond(HttpStatusCode.BadRequest, MediaType, errorMessage); var exception = await FluentActions .Invoking( @@ -166,150 +166,240 @@ public async Task CreateUserShouldReturnBadRequestWhenRequestIsInvalid() this.handler.VerifyNoOutstandingExpectation(); } - // [Fact] - // public async Task UpdateUserShouldCallCorrectEndpoint() - // { - // var userId = Guid.NewGuid(); - - // this.handler.Expect(HttpMethod.Put, $"{BaseAddress}/admin/realms/master/users/{userId}") - // .WithAcceptAndContentTypeHeaders() - // .WithContent( /*lang=json,strict*/ - // "{\"firstName\":\"FirstName\"}" - // ) - // .Respond(HttpStatusCode.NoContent); - - // await this.keycloakUserClient.UpdateUser( - // "master", - // userId.ToString(), - // new() { FirstName = "FirstName" } - // ); - - // this.handler.VerifyNoOutstandingExpectation(); - // } - - // [Fact] - // public async Task UpdateUserShouldThrowNotFoundApiExceptionWhenUserDoesNotExist() - // { - // var userId = Guid.NewGuid(); - // const string errorMessage = /*lang=json,strict*/ - // "{\"errorMessage\":\"User name is missing\"}"; - - // this.handler.Expect(HttpMethod.Put, $"{BaseAddress}/admin/realms/master/users/{userId}") - // .WithAcceptAndContentTypeHeaders() - // .Respond(HttpStatusCode.NotFound, "application/json", errorMessage); - - // var exception = await Assert.ThrowsAsync( - // () => this.keycloakUserClient.UpdateUser("master", userId.ToString(), new User()) - // ); - - // exception.StatusCode.Should().Be(HttpStatusCode.NotFound); - // exception.Content.Should().Be(errorMessage); - // this.handler.VerifyNoOutstandingExpectation(); - // } - - // [Fact] - // public async Task DeleteUserShouldCallCorrectEndpoint() - // { - // var userId = Guid.NewGuid(); - - // this.handler.Expect(HttpMethod.Delete, $"{BaseAddress}/admin/realms/master/users/{userId}") - // .WithAcceptAndContentTypeHeaders() - // .WithContent(string.Empty) - // .Respond(HttpStatusCode.NoContent); - - // await this.keycloakUserClient.DeleteUser("master", userId.ToString()); - - // this.handler.VerifyNoOutstandingExpectation(); - // } - - // [Fact] - // public async Task DeleteUserShouldThrowNotFoundApiExceptionWhenUserDoesNotExist() - // { - // var userId = Guid.NewGuid(); - // const string errorMessage = /*lang=json,strict*/ - // "{\"errorMessage\":\"User name is missing\"}"; - - // this.handler.Expect( - // HttpMethod.Delete, - // $"{BaseAddress}/admin/realms/master/users/{userId.ToString()}" - // ) - // .WithAcceptAndContentTypeHeaders() - // .Respond(HttpStatusCode.BadRequest, "application/json", errorMessage); - - // var exception = await Assert.ThrowsAsync( - // () => this.keycloakUserClient.DeleteUser("master", userId.ToString()) - // ); - - // exception.StatusCode.Should().Be(HttpStatusCode.BadRequest); - // exception.Content.Should().Be(errorMessage); - // this.handler.VerifyNoOutstandingExpectation(); - // } - - // [Fact] - // public async Task SendVerifyEmailShouldCallCorrectEndpoint() - // { - // var userId = Guid.NewGuid(); - - // this.handler.Expect( - // HttpMethod.Put, - // $"{BaseAddress}/admin/realms/master/users/{userId}/send-verify-email" - // ) - // .WithAcceptHeader() - // .Respond(HttpStatusCode.NoContent); - - // await this.keycloakUserClient.SendVerifyEmail("master", userId.ToString()); - - // this.handler.VerifyNoOutstandingExpectation(); - // } - - // [Fact] - // public async Task SendVerifyEmailShouldCallCorrectEndpointWithOptionalQueryParameters() - // { - // var userId = Guid.NewGuid(); - // const string clientId = "client-id"; - // const string redirectUri = "https://localhost:5001"; - - // var url = - // $"{BaseAddress}/admin/realms/master/users/{userId}/send-verify-email" - // + $"?client_id={clientId}" - // + $"&redirect_uri={redirectUri}"; - - // this.handler.Expect(HttpMethod.Put, url) - // .WithAcceptHeader() - // .Respond(HttpStatusCode.NoContent); - - // await this.keycloakUserClient.SendVerifyEmail( - // "master", - // userId.ToString(), - // clientId, - // redirectUri - // ); - - // this.handler.VerifyNoOutstandingExpectation(); - // } - - // [Fact] - // public async Task SendVerifyEmailShouldThrowNotFoundApiExceptionWhenUserDoesNotExist() - // { - // var userId = Guid.NewGuid(); - // const string errorMessage = /*lang=json,strict*/ - // "{\"error\":\"User not found\"}"; - - // this.handler.Expect( - // HttpMethod.Put, - // $"{BaseAddress}/admin/realms/master/users/{userId}/send-verify-email" - // ) - // .WithAcceptHeader() - // .Respond(HttpStatusCode.NotFound, "application/json", errorMessage); - - // var exception = await Assert.ThrowsAsync( - // () => this.keycloakUserClient.SendVerifyEmail("master", userId.ToString()) - // ); - - // exception.StatusCode.Should().Be(HttpStatusCode.NotFound); - // exception.Content.Should().Be(errorMessage); - // this.handler.VerifyNoOutstandingExpectation(); - // } + [Fact] + public async Task UpdateUserShouldCallCorrectEndpoint() + { + var userId = Guid.NewGuid(); + + this.handler.Expect(HttpMethod.Put, $"/admin/realms/master/users/{userId}") + .WithContent( /*lang=json,strict*/ + "{\"firstName\":\"FirstName\"}" + ) + .Respond(HttpStatusCode.NoContent); + + await this.keycloakUserClient.UpdateUserAsync( + "master", + userId.ToString(), + new() { FirstName = "FirstName" } + ); + + this.handler.VerifyNoOutstandingExpectation(); + } + + [Fact] + public async Task UpdateUserShouldThrowNotFoundApiExceptionWhenUserDoesNotExist() + { + var userId = Guid.NewGuid().ToString(); + const string errorMessage = /*lang=json,strict*/ + "{\"errorMessage\":\"User name is missing\"}"; + + this.handler.Expect(HttpMethod.Put, $"/admin/realms/master/users/{userId}") + .Respond(HttpStatusCode.NotFound, MediaType, errorMessage); + + var exception = await FluentActions + .Invoking( + () => + this.keycloakUserClient.UpdateUserAsync( + "master", + userId, + new UserRepresentation() + ) + ) + .Should() + .ThrowAsync(); + + exception.And.StatusCode.Should().Be((int)HttpStatusCode.NotFound); + + this.handler.VerifyNoOutstandingExpectation(); + } + + [Fact] + public async Task DeleteUserShouldCallCorrectEndpoint() + { + var userId = Guid.NewGuid(); + + this.handler.Expect(HttpMethod.Delete, $"/admin/realms/master/users/{userId}") + .Respond(HttpStatusCode.NoContent); + + await this.keycloakUserClient.DeleteUserAsync("master", userId.ToString()); + + this.handler.VerifyNoOutstandingExpectation(); + } + + [Fact] + public async Task DeleteUserShouldThrowNotFoundApiExceptionWhenUserDoesNotExist() + { + var userId = Guid.NewGuid().ToString(); + const string errorMessage = /*lang=json,strict*/ + "{\"errorMessage\":\"User name is missing\"}"; + + this.handler.Expect(HttpMethod.Delete, $"/admin/realms/master/users/{userId}") + .Respond(HttpStatusCode.NotFound, MediaType, errorMessage); + + var exception = await FluentActions + .Invoking(() => this.keycloakUserClient.DeleteUserAsync("master", userId)) + .Should() + .ThrowAsync(); + + exception.And.StatusCode.Should().Be((int)HttpStatusCode.NotFound); + + this.handler.VerifyNoOutstandingExpectation(); + } + + [Fact] + public async Task SendVerifyEmailShouldCallCorrectEndpoint() + { + var userId = Guid.NewGuid().ToString(); + + this.handler.Expect( + HttpMethod.Put, + $"/admin/realms/master/users/{userId}/send-verify-email" + ) + .Respond(HttpStatusCode.OK); + + await this.keycloakUserClient.SendVerifyEmailAsync("master", userId.ToString()); + + this.handler.VerifyNoOutstandingExpectation(); + } + + [Fact] + public async Task SendVerifyEmailShouldCallCorrectEndpointWithOptionalQueryParameters() + { + var userId = Guid.NewGuid(); + const string clientId = "client-id"; + const string redirectUri = "https://localhost:5001"; + + var queryBuilder = new QueryBuilder + { + { "client_id", clientId }, + { "redirect_uri", redirectUri }, + }; + + this.handler.Expect( + HttpMethod.Put, + $"/admin/realms/master/users/{userId}/send-verify-email{queryBuilder.ToQueryString()}" + ) + .Respond(HttpStatusCode.OK); + + await this.keycloakUserClient.SendVerifyEmailAsync( + "master", + userId.ToString(), + clientId, + redirectUri + ); + + this.handler.VerifyNoOutstandingExpectation(); + } + + [Fact] + public async Task SendVerifyEmailShouldThrowNotFoundApiExceptionWhenUserDoesNotExist() + { + var userId = Guid.NewGuid(); + const string errorMessage = /*lang=json,strict*/ + "{\"error\":\"User not found\"}"; + + this.handler.Expect( + HttpMethod.Put, + $"/admin/realms/master/users/{userId}/send-verify-email" + ) + .Respond(HttpStatusCode.NotFound, MediaType, errorMessage); + + var exception = await FluentActions + .Invoking( + () => this.keycloakUserClient.SendVerifyEmailAsync("master", userId.ToString()) + ) + .Should() + .ThrowAsync(); + + exception.And.StatusCode.Should().Be((int)HttpStatusCode.NotFound); + + this.handler.VerifyNoOutstandingExpectation(); + } + + [Fact] + public async Task ExecuteActionsEmailAsyncShouldCallCorrectEndpoint() + { + // Arrange + var userId = Guid.NewGuid().ToString(); + var actions = new List { "UPDATE_PASSWORD" }; + var realm = "master"; + var expectedUrl = + $"{BaseAddress}/admin/realms/{realm}/users/{userId}/execute-actions-email"; + + var request = new ExecuteActionsEmailRequest + { + ClientId = "client-id", + Lifespan = 1800, + RedirectUri = "http://localhost:3000/callback", + Actions = actions + }; + + this.handler.Expect(HttpMethod.Put, expectedUrl).Respond(HttpStatusCode.OK); + + // Act + await this.keycloakUserClient.ExecuteActionsEmailAsync(realm, userId, request); + + // Assert + this.handler.VerifyNoOutstandingExpectation(); + } + + [Fact] + public async Task GetUserGroupsAsyncShouldCallCorrectEndpoint() + { + // Arrange + var userId = Guid.NewGuid().ToString(); + var realm = "master"; + var expectedUrl = $"/admin/realms/{realm}/users/{userId}/groups"; + + this.handler.Expect(HttpMethod.Get, expectedUrl) + .Respond( + HttpStatusCode.OK, + MediaType, + JsonSerializer.Serialize(Array.Empty()) + ); + + // Act + await this.keycloakUserClient.GetUserGroupsAsync(realm, userId, new()); + + // Assert + this.handler.VerifyNoOutstandingExpectation(); + } + + [Fact] + public async Task JoinGroupAsyncShouldCallCorrectEndpoint() + { + // Arrange + var userId = Guid.NewGuid().ToString(); + var groupId = Guid.NewGuid().ToString(); + var realm = "master"; + var expectedUrl = $"/admin/realms/{realm}/users/{userId}/groups/{groupId}"; + + this.handler.Expect(HttpMethod.Put, expectedUrl).Respond(HttpStatusCode.NoContent); + + // Act + await this.keycloakUserClient.JoinGroupAsync(realm, userId, groupId); + + // Assert + this.handler.VerifyNoOutstandingExpectation(); + } + + [Fact] + public async Task LeaveGroupAsyncShouldCallCorrectEndpoint() + { + // Arrange + var userId = Guid.NewGuid().ToString(); + var groupId = Guid.NewGuid().ToString(); + var realm = "master"; + var expectedUrl = $"/admin/realms/{realm}/users/{userId}/groups/{groupId}"; + + this.handler.Expect(HttpMethod.Delete, expectedUrl).Respond(HttpStatusCode.NoContent); + + // Act + await this.keycloakUserClient.LeaveGroupAsync(realm, userId, groupId); + + // Assert + this.handler.VerifyNoOutstandingExpectation(); + } private static string GetUserRepresentation(Guid userId) => /*lang=json,strict*/""" From e1bba76fa775551ec1720086737db9ce121c0419 Mon Sep 17 00:00:00 2001 From: nikiforovall Date: Sat, 4 May 2024 01:05:47 +0300 Subject: [PATCH 4/6] feat: Add IKeycloakGroupClient --- .../configuration-authorization.md | 4 +- .../Admin/ApiUrls.cs | 13 +- .../Admin/IKeycloakGroupClient.cs | 323 ++++++++++++------ .../Admin/IKeycloakUserClient.cs | 4 +- .../Admin/KeycloakClient.cs | 148 +++++++- .../Groups/GetGroupsRequestParameters.cs | 34 ++ .../ServiceCollectionExtensions.cs | 4 +- .../Admin/KeycloakGroupClientTests.cs | 49 +++ .../Admin/KeycloakRealmClientTests.cs | 3 + ...cloak.AuthServices.IntegrationTests.csproj | 2 + .../Utils.cs | 33 ++ 11 files changed, 492 insertions(+), 125 deletions(-) create mode 100644 src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupsRequestParameters.cs create mode 100644 tests/Keycloak.AuthServices.IntegrationTests/Admin/KeycloakGroupClientTests.cs diff --git a/docs/configuration/configuration-authorization.md b/docs/configuration/configuration-authorization.md index 846fd496..a260a0d4 100644 --- a/docs/configuration/configuration-authorization.md +++ b/docs/configuration/configuration-authorization.md @@ -15,7 +15,7 @@ With [Keycloak.AuthServices.Authorization](https://www.nuget.org/packages/Keyclo <<< @/../tests/Keycloak.AuthServices.IntegrationTests/PolicyTests.cs#RequireClientRoles_TestClientRole_Verified -Configure default source. The client name is taken from the `KeycloakAuthorizationOptions.Resource`: +Configure default roles source. The client name is taken from the `KeycloakAuthorizationOptions.Resource`: <<< @/../tests/Keycloak.AuthServices.IntegrationTests/PolicyTests.cs#RequireClientRoles_TestClientRoleWithConfiguration_Verified @@ -28,7 +28,7 @@ Configure default source. The client name is taken from the `KeycloakAuthorizati ``` > [!IMPORTANT] -> If you don't configure the default source `KeycloakException` exception will be thrown. +> If you don't configure the default roles source `KeycloakException` exception will be thrown. Override default source with `KeycloakAuthorizationOptions.RolesResource`: diff --git a/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs b/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs index 52c4cd74..70d5796e 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs @@ -7,9 +7,11 @@ internal static class ApiUrls { private const string AdminApiBase = "/admin"; + private const string RealmParam = "{realm}"; + private const string Realms = "realms"; - internal const string GetRealm = $"{AdminApiBase}/{Realms}/{{realm}}"; + internal const string GetRealm = $"{AdminApiBase}/{Realms}/{RealmParam}"; #region Resource API @@ -51,10 +53,19 @@ internal static class ApiUrls #region Group API internal const string GetGroups = $"{GetRealm}/groups"; + internal const string CreateGroup = $"{GetRealm}/groups"; internal const string GetGroup = $"{GetRealm}/groups/{{id}}"; + internal const string UpdateGroup = $"{GetRealm}/groups/{{id}}"; + internal const string DeleteGroup = $"{GetRealm}/groups"; + + internal const string CreateChildGroup = $"{GetRealm}/groups/{{id}}/children"; + #endregion + + public static string WithRealm(this string path, string realm) => + path.Replace(RealmParam, realm); } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs index 4647c2c1..b7da2fb0 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs @@ -1,113 +1,228 @@ namespace Keycloak.AuthServices.Sdk.Admin; +using Keycloak.AuthServices.Sdk.Admin.Models; +using Keycloak.AuthServices.Sdk.Admin.Requests.Groups; + /// /// Group management /// public interface IKeycloakGroupClient { - // /// - // /// Get a stream of groups on the realm. - // /// - // /// Realm name. - // /// Optional query parameters. - // /// A stream of groups, filtered according to query parameters. - // Task> GetGroups( - // string realm, - // GetGroupRequestParameters? parameters = default - // ); - - // /// - // /// Get representation of a group. - // /// - // /// Realm name. - // /// group ID. - // /// The group representation. - // Task GetGroup(string realm, string groupId); - - // /// - // /// Create a new group. - // /// - // /// - // /// Group name must be unique. - // /// - // /// Realm name (not ID). - // /// Group representation. - // /// - // Task CreateGroup(string realm, Group group); - - // /// - // /// Update the group. - // /// - // /// Realm name (not ID). - // /// group ID. - // /// Group representation. - // /// - // Task UpdateGroup(string realm, string groupId, Group group); - - // /// - // /// Get a stream of groups on the realm. - // /// - // /// Realm name (not ID). - // /// Optional query parameters. - // /// A stream of groups, filtered according to query parameters. + /// + /// Get a collection of groups on the realm. + /// + /// Realm name (not ID). + /// Optional query parameters. + /// + /// A stream of groups, filtered according to query parameters. // [Get(KeycloakClientApiConstants.GetGroups)] - // [Headers("Accept: application/json")] - // Task> GetGroups(string realm, [Query] GetGroupsRequestParameters? parameters = default); - - // /// - // /// Get representation of a Group. - // /// - // /// Realm name (not ID). - // /// Group ID. - // /// The group representation. - // [Get(KeycloakClientApiConstants.GetGroup)] - // [Headers("Accept: application/json")] - // Task GetGroup(string realm, [AliasAs("id")] string groupId); - - // /// - // /// Create a new Group. - // /// - // /// - // /// Name must be unique. - // /// - // /// Realm name (not ID). - // /// Group representation. - // /// - // [Post(KeycloakClientApiConstants.CreateGroup)] - // [Headers("Content-Type: application/json")] - // Task CreateGroup(string realm, [Body] Group group); - - // /// - // /// Update the Group. - // /// - // /// Realm name (not ID). - // /// Group ID. - // /// Group representation. - // /// - // [Put(KeycloakClientApiConstants.UpdateGroup)] - // [Headers("Content-Type: application/json")] - // Task UpdateGroup(string realm, [AliasAs("id")] string groupId, [Body] Group group); - - // /// - // /// Create a new child group. - // /// - // /// - // /// Name must be unique. - // /// - // /// Realm name (not ID). - // /// Group ID. - // /// Group representation. - // /// - // [Post(KeycloakClientApiConstants.CreateChildGroup)] - // [Headers("Content-Type: application/json")] - // Task CreateChildGroup(string realm, [AliasAs("id")] string groupId, [Body] Group group); - - // /// - // /// Delete a group. Note: the Keycloak documentation does not specify if this deletes subgroups. - // /// - // /// Realm name (not ID). - // /// Group ID. - // /// - // [Delete(KeycloakClientApiConstants.DeleteGroup)] - // Task DeleteGroup(string realm, [AliasAs("id")] string groupId); + Task GetGroupsWithResponseAsync( + string realm, + GetGroupsRequestParameters? parameters = default, + CancellationToken cancellationToken = default + ); + + /// + /// Get group hierarchy. Only name and ids are returned. + /// + /// Realm name (not ID). + /// Optional query parameters. + /// + /// A stream of groups, filtered according to query parameters. + async Task> GetGroupsAsync( + string realm, + GetGroupsRequestParameters? parameters = default, + CancellationToken cancellationToken = default + ) + { + var response = await this.GetGroupsWithResponseAsync(realm, parameters, cancellationToken); + + return await response.GetResponseAsync>(cancellationToken) + ?? Enumerable.Empty(); + } + + /// + /// Get representation of a Group. + /// + /// Realm name (not ID). + /// Group ID. + /// + /// The group representation. + Task GetGroupWithResponseAsync( + string realm, + string groupId, + CancellationToken cancellationToken = default + ); + + /// + /// Get representation of a Group. + /// + /// Realm name (not ID). + /// Group ID. + /// + /// The group representation. + async Task GetGroupAsync( + string realm, + string groupId, + CancellationToken cancellationToken = default + ) + { + var response = await this.GetGroupWithResponseAsync(realm, groupId, cancellationToken); + + return await response.GetResponseAsync(cancellationToken) ?? new(); + } + + /// + /// Create or add a top level realm groupSet or create child. + /// + /// + /// This will update the group and set the parent if it exists. Create it and set the parent if the group doesn’t exist. + /// + /// Realm name (not ID). + /// Group representation. + /// + /// + Task CreateGroupWithResponseAsync( + string realm, + GroupRepresentation group, + CancellationToken cancellationToken = default + ); + + /// + /// Create or add a top level realm groupSet or create child. + /// + /// + /// This will update the group and set the parent if it exists. Create it and set the parent if the group doesn’t exist. + /// + /// Realm name (not ID). + /// Group representation. + /// + /// + async Task CreateGroupAsync( + string realm, + GroupRepresentation group, + CancellationToken cancellationToken = default + ) + { + var response = await this.CreateGroupWithResponseAsync(realm, group, cancellationToken); + + await response.EnsureResponseAsync(cancellationToken); + } + + /// + /// Update group, ignores subgroups. + /// + /// Realm name (not ID). + /// Group ID. + /// Group representation. + /// + /// + Task UpdateGroupWithResponseAsync( + string realm, + string groupId, + GroupRepresentation group, + CancellationToken cancellationToken = default + ); + + /// + /// Update group, ignores subgroups. + /// + /// Realm name (not ID). + /// Group ID. + /// Group representation. + /// + /// + async Task UpdateGroupAsync( + string realm, + string groupId, + GroupRepresentation group, + CancellationToken cancellationToken = default + ) + { + var response = await this.UpdateGroupWithResponseAsync( + realm, + groupId, + group, + cancellationToken + ); + + await response.EnsureResponseAsync(cancellationToken); + } + + /// + /// Set or create child. + /// + /// + /// This will just set the parent if it exists. Create it and set the parent if the group doesn’t exist. + /// + /// Realm name (not ID). + /// Group ID. + /// Group representation. + /// + /// + Task CreateChildGroupWithResponseAsync( + string realm, + string groupId, + GroupRepresentation group, + CancellationToken cancellationToken = default + ); + + /// + /// Set or create child. + /// + /// + /// This will just set the parent if it exists. Create it and set the parent if the group doesn’t exist. + /// + /// Realm name (not ID). + /// Group ID. + /// Group representation. + /// + /// + async Task CreateChildGroupAsync( + string realm, + string groupId, + GroupRepresentation group, + CancellationToken cancellationToken = default + ) + { + var response = await this.CreateChildGroupWithResponseAsync( + realm, + groupId, + group, + cancellationToken + ); + + await response.EnsureResponseAsync(cancellationToken); + } + + /// + /// Delete a group. Note: the Keycloak documentation does not specify if this deletes subgroups. + /// + /// Realm name (not ID). + /// Group ID. + /// + /// + Task DeleteGroupWithResponseAsync( + string realm, + string groupId, + CancellationToken cancellationToken = default + ); + + /// + /// Delete a group. Note: the Keycloak documentation does not specify if this deletes subgroups. + /// + /// Realm name (not ID). + /// Group ID. + /// + /// + async Task DeleteGroupAsync( + string realm, + string groupId, + CancellationToken cancellationToken = default + ) + { + var response = await this.DeleteGroupWithResponseAsync(realm, groupId, cancellationToken); + + await response.EnsureResponseAsync(cancellationToken); + } } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs index 9b3dbede..77d92e5d 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs @@ -286,7 +286,7 @@ async Task ExecuteActionsEmailAsync( Task GetUserGroupsWithResponseAsync( string realm, string userId, - GetUserGroupsRequestParameters parameters, + GetUserGroupsRequestParameters? parameters = default, CancellationToken cancellationToken = default ); @@ -301,7 +301,7 @@ Task GetUserGroupsWithResponseAsync( async Task> GetUserGroupsAsync( string realm, string userId, - GetUserGroupsRequestParameters parameters, + GetUserGroupsRequestParameters? parameters = default, CancellationToken cancellationToken = default ) { diff --git a/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs index 1d66aa7d..746fac28 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs @@ -2,6 +2,7 @@ using System.Net.Http.Json; using Keycloak.AuthServices.Sdk.Admin.Models; +using Keycloak.AuthServices.Sdk.Admin.Requests.Groups; using Keycloak.AuthServices.Sdk.Admin.Requests.Users; using Keycloak.AuthServices.Sdk.Utils; @@ -10,7 +11,6 @@ /// public partial class KeycloakClient : IKeycloakClient { - private const string RealmParam = "{realm}"; private readonly HttpClient httpClient; /// @@ -19,14 +19,14 @@ public partial class KeycloakClient : IKeycloakClient /// public KeycloakClient(HttpClient httpClient) => this.httpClient = httpClient; - #region Realm + #region RealmRegion /// public async Task GetRealmWithResponseAsync( string realm, CancellationToken cancellationToken = default ) { - var path = ApiUrls.GetRealm.Replace(RealmParam, realm); + var path = ApiUrls.GetRealm.WithRealm(realm); var responseMessage = await this.httpClient.GetAsync( path, @@ -37,7 +37,7 @@ public async Task GetRealmWithResponseAsync( } #endregion - #region User + #region UserRegion /// public async Task CreateUserWithResponseAsync( string realm, @@ -45,7 +45,7 @@ public async Task CreateUserWithResponseAsync( CancellationToken cancellationToken = default ) { - var path = ApiUrls.CreateUser.Replace(RealmParam, realm); + var path = ApiUrls.CreateUser.WithRealm(realm); var responseMessage = await this.httpClient.PostAsJsonAsync(path, user, cancellationToken); @@ -59,7 +59,7 @@ public async Task GetUsersWithResponseAsync( CancellationToken cancellationToken = default ) { - var path = ApiUrls.GetUsers.Replace(RealmParam, realm); + var path = ApiUrls.GetUsers.WithRealm(realm); var query = string.Empty; @@ -103,7 +103,7 @@ public async Task GetUserWithResponseAsync( CancellationToken cancellationToken = default ) { - var path = ApiUrls.GetUser.Replace(RealmParam, realm).Replace("{id}", userId); + var path = ApiUrls.GetUser.WithRealm(realm).Replace("{id}", userId); var query = includeUserProfileMetadata ? new QueryBuilder() @@ -125,7 +125,7 @@ public async Task UpdateUserWithResponseAsync( CancellationToken cancellationToken = default ) { - var path = ApiUrls.UpdateUser.Replace(RealmParam, realm).Replace("{id}", userId); + var path = ApiUrls.UpdateUser.WithRealm(realm).Replace("{id}", userId); var responseMessage = await this.httpClient.PutAsJsonAsync(path, user, cancellationToken); @@ -139,7 +139,7 @@ public async Task DeleteUserWithResponseAsync( CancellationToken cancellationToken = default ) { - var path = ApiUrls.DeleteUser.Replace(RealmParam, realm).Replace("{id}", userId); + var path = ApiUrls.DeleteUser.WithRealm(realm).Replace("{id}", userId); var responseMessage = await this.httpClient.DeleteAsync(path, cancellationToken); @@ -155,7 +155,7 @@ public async Task SendVerifyEmailWithResponseAsync( CancellationToken cancellationToken = default ) { - var path = ApiUrls.SendVerifyEmail.Replace(RealmParam, realm).Replace("{id}", userId); + var path = ApiUrls.SendVerifyEmail.WithRealm(realm).Replace("{id}", userId); var queryBuilder = new QueryBuilder(); @@ -188,7 +188,7 @@ public async Task ExecuteActionsEmailWithResponseAsync( CancellationToken cancellationToken = default ) { - var path = ApiUrls.ExecuteActionsEmail.Replace("{realm}", realm).Replace("{id}", userId); + var path = ApiUrls.ExecuteActionsEmail.WithRealm(realm).Replace("{id}", userId); var queryBuilder = new QueryBuilder(); @@ -222,7 +222,7 @@ public async Task ExecuteActionsEmailWithResponseAsync( public async Task GetUserGroupsWithResponseAsync( string realm, string userId, - GetUserGroupsRequestParameters parameters, + GetUserGroupsRequestParameters? parameters = default, CancellationToken cancellationToken = default ) { @@ -230,6 +230,7 @@ public async Task GetUserGroupsWithResponseAsync( var queryBuilder = new QueryBuilder(); + parameters ??= new(); if (parameters.BriefRepresentation.HasValue) { queryBuilder.Add("briefRepresentation", parameters.BriefRepresentation.ToString()); @@ -261,7 +262,7 @@ public async Task JoinGroupWithResponseAsync( ) { var path = ApiUrls - .JoinGroup.Replace(RealmParam, realm) + .JoinGroup.WithRealm(realm) .Replace("{id}", userId) .Replace("{groupId}", groupId); @@ -283,7 +284,7 @@ public async Task LeaveGroupWithResponseAsync( ) { var path = ApiUrls - .LeaveGroup.Replace(RealmParam, realm) + .LeaveGroup.WithRealm(realm) .Replace("{id}", userId) .Replace("{groupId}", groupId); @@ -291,6 +292,125 @@ public async Task LeaveGroupWithResponseAsync( return responseMessage!; } + #endregion + + #region GroupRegion + + /// + public async Task GetGroupsWithResponseAsync( + string realm, + GetGroupsRequestParameters? parameters = default, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.GetGroups.WithRealm(realm); + + var queryBuilder = new QueryBuilder(); + + parameters ??= new(); + if (parameters.BriefRepresentation.HasValue) + { + queryBuilder.Add("briefRepresentation", parameters.BriefRepresentation.ToString()); + } + + if (parameters.First.HasValue) + { + queryBuilder.Add("first", parameters.First.ToString()); + } + + if (parameters.Exact.HasValue) + { + queryBuilder.Add("exact", parameters.Exact.ToString()); + } + + if (parameters.Max.HasValue) + { + queryBuilder.Add("max", parameters.Max.ToString()!); + } + + if (parameters.Search is not null) + { + queryBuilder.Add("search", parameters.Search.ToString()!); + } + + var url = path + queryBuilder.ToQueryString(); + + var responseMessage = await this.httpClient.GetAsync(url, cancellationToken); + + return responseMessage!; + } + + /// + public async Task GetGroupWithResponseAsync( + string realm, + string groupId, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.GetGroup.WithRealm(realm).Replace("{id}", groupId); + + var responseMessage = await this.httpClient.GetAsync(path, cancellationToken); + + return responseMessage!; + } + + /// + public async Task CreateGroupWithResponseAsync( + string realm, + GroupRepresentation group, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.CreateGroup.WithRealm(realm); + + var responseMessage = await this.httpClient.PostAsJsonAsync(path, group, cancellationToken); + + return responseMessage!; + } + + /// + public async Task UpdateGroupWithResponseAsync( + string realm, + string groupId, + GroupRepresentation group, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.UpdateGroup.WithRealm(realm).Replace("{id}", groupId); + + var responseMessage = await this.httpClient.PutAsJsonAsync(path, group, cancellationToken); + + return responseMessage!; + } + + /// + public async Task CreateChildGroupWithResponseAsync( + string realm, + string groupId, + GroupRepresentation group, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.CreateChildGroup.WithRealm(realm).Replace("{id}", groupId); + + var responseMessage = await this.httpClient.PostAsJsonAsync(path, group, cancellationToken); + + return responseMessage!; + } + + /// + public async Task DeleteGroupWithResponseAsync( + string realm, + string groupId, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.DeleteGroup.WithRealm(realm).Replace("{id}", groupId); + + var responseMessage = await this.httpClient.DeleteAsync(path, cancellationToken); + + return responseMessage!; + } #endregion } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupsRequestParameters.cs b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupsRequestParameters.cs new file mode 100644 index 00000000..18693853 --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupsRequestParameters.cs @@ -0,0 +1,34 @@ +namespace Keycloak.AuthServices.Sdk.Admin.Requests.Groups; + +using Keycloak.AuthServices.Sdk.Admin.Models; + +/// +/// Optional parameters for the endpoint. +/// +public class GetGroupsRequestParameters +{ + /// + /// Defines whether brief representations are returned. Default is false. + /// + public bool? BriefRepresentation { get; init; } + + /// + /// Pagination offset. + /// + public int? First { get; init; } + + /// + /// Whether to match the search exactly or not + /// + public bool? Exact { get; init; } + + /// + /// Maximum results size. Default is 100. + /// + public int? Max { get; init; } + + /// + /// Search for a string contained in or . + /// + public string? Search { get; init; } +} diff --git a/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs b/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs index 269703fa..48f393ca 100644 --- a/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs +++ b/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs @@ -63,11 +63,11 @@ public static IHttpClientBuilder AddKeycloakAdminHttpClient( services.Configure(configureKeycloakOptions); services.AddTransient(sp => sp.GetRequiredService()); + services.AddTransient(sp => sp.GetRequiredService()); + services.AddTransient(sp => sp.GetRequiredService()); services.AddTransient(sp => sp.GetRequiredService() ); - services.AddTransient(sp => sp.GetRequiredService()); - return services .AddHttpClient( "keycloak_admin_api", diff --git a/tests/Keycloak.AuthServices.IntegrationTests/Admin/KeycloakGroupClientTests.cs b/tests/Keycloak.AuthServices.IntegrationTests/Admin/KeycloakGroupClientTests.cs new file mode 100644 index 00000000..e8b85486 --- /dev/null +++ b/tests/Keycloak.AuthServices.IntegrationTests/Admin/KeycloakGroupClientTests.cs @@ -0,0 +1,49 @@ +namespace Keycloak.AuthServices.IntegrationTests; + +using Keycloak.AuthServices.Sdk.Admin; +using Microsoft.Extensions.DependencyInjection; +using static Keycloak.AuthServices.IntegrationTests.Utils; + +public class KeycloakGroupClientTests(ITestOutputHelper testOutputHelper) + : AuthenticationScenarioNoKeycloak() +{ + private static readonly string AppSettings = "appsettings.Master.json"; + + [Fact] + public async Task GetGroupsAsync_RealmExists_Success() + { + var (services, _) = AdminHttpClientSetup(AppSettings, testOutputHelper); + + #region GetGroupsAsync_RealmExists_Success + + var sp = services.BuildServiceProvider(); + + var client = sp.GetRequiredService(); + + var groups = await client.GetGroupsAsync("Test"); + + groups.Should().NotBeNull(); + #endregion GetGroupsAsync_RealmExists_Success + } + + [Theory, AutoData] + public async Task CreateGroupAsync_NewGroup_Success(string groupName) + { + var (services, _) = AdminHttpClientSetup(AppSettings, testOutputHelper); + + #region CreateGroupAsync_NewGroup_Success + + var sp = services.BuildServiceProvider(); + + var client = sp.GetRequiredService(); + + await client.CreateGroupAsync("Test", new() { Name = groupName }); + + var groups = await client.GetGroupsAsync("Test", new() { Search = groupName, }); + + var group = await client.GetGroupAsync("Test", groups.First().Id!); + + group.Should().NotBeNull(); + #endregion CreateGroupAsync_NewGroup_Success + } +} diff --git a/tests/Keycloak.AuthServices.IntegrationTests/Admin/KeycloakRealmClientTests.cs b/tests/Keycloak.AuthServices.IntegrationTests/Admin/KeycloakRealmClientTests.cs index 8f652275..191fe782 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/Admin/KeycloakRealmClientTests.cs +++ b/tests/Keycloak.AuthServices.IntegrationTests/Admin/KeycloakRealmClientTests.cs @@ -8,6 +8,9 @@ namespace Keycloak.AuthServices.IntegrationTests; using Microsoft.Extensions.DependencyInjection; using static Keycloak.AuthServices.IntegrationTests.Utils; +/// +/// Used for demonstration/docs, this is why this class is so verbose +/// public class KeycloakRealmClientTests(ITestOutputHelper testOutputHelper) : AuthenticationScenarioNoKeycloak() { diff --git a/tests/Keycloak.AuthServices.IntegrationTests/Keycloak.AuthServices.IntegrationTests.csproj b/tests/Keycloak.AuthServices.IntegrationTests/Keycloak.AuthServices.IntegrationTests.csproj index 00e68a07..ae436419 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/Keycloak.AuthServices.IntegrationTests.csproj +++ b/tests/Keycloak.AuthServices.IntegrationTests/Keycloak.AuthServices.IntegrationTests.csproj @@ -12,10 +12,12 @@ + + diff --git a/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs b/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs index 319a1500..2ffb6c42 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs +++ b/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs @@ -3,6 +3,7 @@ using Alba.Security; using Keycloak.AuthServices.Authentication; using Keycloak.AuthServices.Common; +using Keycloak.AuthServices.Sdk; using Meziantou.Extensions.Logging.Xunit; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Hosting; @@ -91,6 +92,38 @@ ITestOutputHelper testOutputHelper return (services, configuration); } + public static (IServiceCollection services, IConfiguration configuration1) AdminHttpClientSetup( + string fileName, + ITestOutputHelper testOutputHelper + ) + { + var (services, configuration) = KeycloakSetup(fileName, testOutputHelper); + + var tokenClientName = "keycloak_admin_api_token"; + var keycloakOptions = configuration.GetKeycloakOptions()!; + + services.AddDistributedMemoryCache(); + services + .AddClientCredentialsTokenManagement() + .AddClient(tokenClientName, client => BindKeycloak(client, keycloakOptions)); + + services + .AddKeycloakAdminHttpClient(configuration) + .AddClientCredentialsTokenHandler(tokenClientName); + + return (services, configuration); + } + + private static void BindKeycloak( + Duende.AccessTokenManagement.ClientCredentialsClient client, + KeycloakAdminClientOptions adminClientOptions + ) + { + client.ClientId = adminClientOptions.Resource; + client.ClientSecret = adminClientOptions.Credentials.Secret; + client.TokenEndpoint = adminClientOptions.KeycloakTokenEndpoint; + } + public static KeycloakAuthenticationOptions ReadKeycloakAuthenticationOptions(string fileName) { var configuration = new ConfigurationBuilder() From 99e014da44fa71041b40e104f37573ca2a9079ec Mon Sep 17 00:00:00 2001 From: nikiforovall Date: Sat, 4 May 2024 03:14:35 +0300 Subject: [PATCH 5/6] feat: Add IKeycloakProtectionClient, rename Auth Server to IAuthorizationServer --- docs/.vitepress/config.mts | 11 +- docs/admin-rest-api/admin-api-reference.md | 2 +- docs/admin-rest-api/admin-rest-api.md | 2 +- .../protected-resource-client.md | 3 - docs/index.md | 3 + docs/introduction.md | 2 +- .../protection-api-reference.md | 3 + docs/protection-api/protection-api.md | 38 +++ .../Commands/CreateWorkspaceCommand.cs | 6 +- .../Controllers/AuthZController.cs | 4 +- .../Controllers/KeycloakAdminController.cs | 5 +- .../AccessTokenPropagationExtensions.cs | 2 +- .../AccessTokenPropagationHandler.cs | 6 +- ...Client.cs => AuthorizationServerClient.cs} | 13 +- ...lient.cs => IAuthorizationServerClient.cs} | 4 +- ...ycloakAuthorizationServerClientOptions.cs} | 2 +- .../Requirements/DecisionRequirement.cs | 4 +- .../ServiceCollectionExtensions.cs | 16 +- .../Admin/ApiUrls.cs | 15 -- .../Admin/IKeycloakClient.cs | 2 +- .../Admin/IKeycloakProtectedResourceClient.cs | 130 ---------- .../KeycloakProtectionClientOptions.cs | 14 + .../Protection/ApiUrls.cs | 20 ++ .../Protection/IKeycloakProtectionClient.cs | 239 ++++++++++++++++++ .../Protection/KeycloakProtectionClient.cs | 150 +++++++++++ .../Protection/Models/Resource.cs | 56 ++++ .../Protection/Models/ResourceResponse.cs | 84 ++++++ .../Requests/GetResourcesRequestParameters.cs | 42 +++ .../ServiceCollectionExtensions.cs | 120 ++++++++- .../AuthorizationServerPolicyTests.cs | 2 +- .../KeycloakProtectionClientTests.cs | 67 +++++ .../Utils.cs | 40 ++- 32 files changed, 905 insertions(+), 202 deletions(-) delete mode 100644 docs/admin-rest-api/protected-resource-client.md create mode 100644 docs/protection-api/protection-api-reference.md create mode 100644 docs/protection-api/protection-api.md rename src/Keycloak.AuthServices.Authorization/AuthorizationServer/{KeycloakProtectionClient.cs => AuthorizationServerClient.cs} (92%) rename src/Keycloak.AuthServices.Authorization/AuthorizationServer/{IKeycloakProtectionClient.cs => IAuthorizationServerClient.cs} (93%) rename src/Keycloak.AuthServices.Authorization/AuthorizationServer/{KeycloakProtectionClientOptions.cs => KeycloakAuthorizationServerClientOptions.cs} (93%) delete mode 100644 src/Keycloak.AuthServices.Sdk/Admin/IKeycloakProtectedResourceClient.cs create mode 100644 src/Keycloak.AuthServices.Sdk/KeycloakProtectionClientOptions.cs create mode 100644 src/Keycloak.AuthServices.Sdk/Protection/ApiUrls.cs create mode 100644 src/Keycloak.AuthServices.Sdk/Protection/IKeycloakProtectionClient.cs create mode 100644 src/Keycloak.AuthServices.Sdk/Protection/KeycloakProtectionClient.cs create mode 100644 src/Keycloak.AuthServices.Sdk/Protection/Models/Resource.cs create mode 100644 src/Keycloak.AuthServices.Sdk/Protection/Models/ResourceResponse.cs create mode 100644 src/Keycloak.AuthServices.Sdk/Protection/Requests/GetResourcesRequestParameters.cs create mode 100644 tests/Keycloak.AuthServices.IntegrationTests/Protection/KeycloakProtectionClientTests.cs diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index cf482218..50f4e7eb 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -59,12 +59,21 @@ export default withMermaid({ { text: 'Realm Client', link: '/admin-rest-api/realm-client' }, { text: 'User Client', link: '/admin-rest-api/user-client' }, { text: 'Group Client', link: '/admin-rest-api/group-client' }, - { text: 'Protected Resource Client', link: '/admin-rest-api/protected-resource-client' }, ] }, { text: 'OpenAPI Support', link: '/admin-rest-api/admin-api-openapi' }, ] }, + { + text: 'Protection API ⚙️', + collapsed: true, + items: [ + { text: 'Introduction', link: '/protection-api/protection-api' }, + { + text: 'Protection API Reference', link: '/protection-api/protection-api-reference', + }, + ] + }, { text: 'Examples', collapsed: false, diff --git a/docs/admin-rest-api/admin-api-reference.md b/docs/admin-rest-api/admin-api-reference.md index 13bddcb9..042dba2c 100644 --- a/docs/admin-rest-api/admin-api-reference.md +++ b/docs/admin-rest-api/admin-api-reference.md @@ -2,7 +2,7 @@ [Keycloak.AuthServices.Sdk](https://www.nuget.org/packages/Keycloak.AuthServices.Sdk/) provides a basic support for common and most popular API endpoints. -The full API documentation see . +The full API documentation - . >[!IMPORTANT] > **Keycloak.AuthServices** is an open source project with limited developer capacity. Many API endpoints might be missing. But **contributions are encouraged**! diff --git a/docs/admin-rest-api/admin-rest-api.md b/docs/admin-rest-api/admin-rest-api.md index 4e7016c4..9bb90c33 100644 --- a/docs/admin-rest-api/admin-rest-api.md +++ b/docs/admin-rest-api/admin-rest-api.md @@ -6,7 +6,7 @@ The Admin REST API in Keycloak provides a programmatic way to manage and administer Keycloak instances. It allows you to perform various administrative tasks such as creating and managing realms, users, roles, clients, and more. To interact with the Admin REST API, you can use HTTP requests to send commands and retrieve data. The API follows the REST architectural style and is designed to be simple and intuitive to use. > [!NOTE] -> See full list of API endpoints - [Admin REST API](https://www.keycloak.org/docs-api/24.0.1/rest-api/#_overview) +> See full list of API endpoints - [Admin REST API](https://www.keycloak.org/docs-api/latest/rest-api/#_overview) Keycloak provides a comprehensive set of endpoints that cover a wide range of administrative operations. These endpoints are organized into different resource types, such as realms, users, roles, and clients, making it easy to navigate and manipulate the Keycloak configuration. diff --git a/docs/admin-rest-api/protected-resource-client.md b/docs/admin-rest-api/protected-resource-client.md deleted file mode 100644 index c0d63c14..00000000 --- a/docs/admin-rest-api/protected-resource-client.md +++ /dev/null @@ -1,3 +0,0 @@ -# IKeycloakProtectedResourceClient - -<<< @/../src/Keycloak.AuthServices.Sdk/Admin/IKeycloakProtectedResourceClient.cs diff --git a/docs/index.md b/docs/index.md index 01665317..fbdb2d03 100644 --- a/docs/index.md +++ b/docs/index.md @@ -18,6 +18,9 @@ hero: - theme: alt text: HTTP Admin REST API link: /admin-rest-api/admin-rest-api + - theme: alt + text: Protection REST API + link: /protection-api/protection-api features: - title: 🔒Authentication diff --git a/docs/introduction.md b/docs/introduction.md index cce2f528..2c39dbb0 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -21,4 +21,4 @@ Keycloak.AuthServices is a [family of packages](https://www.nuget.org/packages?q | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Keycloak.AuthServices.Authentication](https://www.nuget.org/packages/Keycloak.AuthServices.Authentication/2.0.0-pre-1) | As an OAuth2, OpenID Connect, and SAML compliant server, Keycloak can secure any application and service as long as the technology stack they are using supports any of these protocols. This library provides AspNetCore integration for JwtBearer and OpenIdConnect. | | [Keycloak.AuthServices.Authorization](https://www.nuget.org/packages/Keycloak.AuthServices.Authorization/2.0.0-pre-1) | Provides Role-Based Access Control (RBAC) and integration with the Keycloak Authorization Server. | -| [Keycloak.AuthServices.Sdk](https://www.nuget.org/packages/Keycloak.AuthServices.Sdk/2.0.0-pre-1) | Provides integration with the [Keycloak Admin REST API](https://www.keycloak.org/docs-api/21.1.1/rest-api/), allowing you to manage users, roles, clients, and realms programmatically. | +| [Keycloak.AuthServices.Sdk](https://www.nuget.org/packages/Keycloak.AuthServices.Sdk/2.0.0-pre-1) | Provides integration with the [Keycloak Admin REST API](https://www.keycloak.org/docs-api/21.1.1/rest-api/), allowing you to manage users, roles, clients, and realms programmatically. Provides integration with the [Protection REST API](https://www.keycloak.org/docs/latest/authorization_services/index.html#_service_protection_api) - a UMA-compliant set of endpoints. | diff --git a/docs/protection-api/protection-api-reference.md b/docs/protection-api/protection-api-reference.md new file mode 100644 index 00000000..8fd0480e --- /dev/null +++ b/docs/protection-api/protection-api-reference.md @@ -0,0 +1,3 @@ +# Protection API Reference + +<<< @/../src/Keycloak.AuthServices.Sdk/Protection/IKeycloakProtectionClient.cs diff --git a/docs/protection-api/protection-api.md b/docs/protection-api/protection-api.md new file mode 100644 index 00000000..ecb6a83a --- /dev/null +++ b/docs/protection-api/protection-api.md @@ -0,0 +1,38 @@ +# Protection API + +The Protection API provides a UMA-compliant set of endpoints providing: + +* Resource Management - With this endpoint, resource servers can manage their resources remotely and enable policy enforcers to query the server for the resources that need protection. + +* Permission Management - In the UMA protocol, resource servers access this endpoint to create permission tickets. Keycloak also provides endpoints to manage the state of permissions and query permissions. + +* Policy API - Keycloak leverages the UMA Protection API to allow resource servers to manage permissions for their users. In addition to the Resource and Permission APIs, Keycloak provides a Policy API from where permissions can be set to resources by resource servers on behalf of their users. + +See documentation for more details: + +## Add to your code + +Install [Keycloak.AuthServices.Sdk](https://www.nuget.org/packages/Keycloak.AuthServices.Sdk): + +```bash +dotnet add package Keycloak.AuthServices.Sdk +``` + +> [!IMPORTANT] +> Protection API is protected so you need to acquire access token somehow. See [Access Token Management](/admin-rest-api/access-token) + +You can use `IKeycloakProtectionClient` from Web APIs, Worker, Console apps, etc. It is fully integrated with [IHttpClientFactory](https://learn.microsoft.com/en-us/dotnet/core/extensions/httpclient-factory) and therefore you don't need to worry about `HttpClient` lifetime and the way you work with it. + +To add it to DI, you can use convenience extensions method `AddKeycloakProtectionHttpClient`: + +```csharp +public static IHttpClientBuilder AddKeycloakProtectionHttpClient( + this IServiceCollection services, + KeycloakProtectionClientOptions keycloakOptions, + Action? configureClient = default +) +``` + +Here is how to use it: + +<<< @/../tests/Keycloak.AuthServices.IntegrationTests/Protection/KeycloakProtectionClientTests.cs#GetResourcesAsync_Success {17,21,23 cs:line-numbers} diff --git a/samples/AuthorizationAndCleanArchitecture/Application/Commands/CreateWorkspaceCommand.cs b/samples/AuthorizationAndCleanArchitecture/Application/Commands/CreateWorkspaceCommand.cs index 820d0534..f9cf4a39 100644 --- a/samples/AuthorizationAndCleanArchitecture/Application/Commands/CreateWorkspaceCommand.cs +++ b/samples/AuthorizationAndCleanArchitecture/Application/Commands/CreateWorkspaceCommand.cs @@ -5,8 +5,8 @@ namespace Api.Application.Commands; using Authorization; using Authorization.Abstractions; using Data; -using Keycloak.AuthServices.Sdk.Admin; using Keycloak.AuthServices.Sdk.Admin.Models; +using Keycloak.AuthServices.Sdk.Protection; using MediatR; [AuthorizeProtectedResource("workspaces", "workspaces:create")] @@ -15,12 +15,12 @@ public record CreateWorkspaceCommand(string Name, IList? Projects = def public class CreateWorkspaceCommandHandler : IRequestHandler { private readonly IApplicationDbContext db; - private readonly IKeycloakProtectedResourceClient resourceClient; + private readonly IKeycloakProtectionClient resourceClient; private readonly IIdentityService identityService; public CreateWorkspaceCommandHandler( IApplicationDbContext db, - IKeycloakProtectedResourceClient resourceClient, + IKeycloakProtectionClient resourceClient, IIdentityService identityService) { this.db = db ?? throw new ArgumentNullException(nameof(db)); diff --git a/samples/AuthorizationAndCleanArchitecture/Controllers/AuthZController.cs b/samples/AuthorizationAndCleanArchitecture/Controllers/AuthZController.cs index d94605ea..4be9cff2 100644 --- a/samples/AuthorizationAndCleanArchitecture/Controllers/AuthZController.cs +++ b/samples/AuthorizationAndCleanArchitecture/Controllers/AuthZController.cs @@ -6,9 +6,9 @@ namespace Api.Controllers; [Route("api/authz")] public class KeycloakAuthZController : ApiControllerBase { - private readonly IKeycloakProtectionClient protectionClient; + private readonly IAuthorizationServerClient protectionClient; - public KeycloakAuthZController(IKeycloakProtectionClient protectionClient) => + public KeycloakAuthZController(IAuthorizationServerClient protectionClient) => this.protectionClient = protectionClient; [HttpGet("try-resource")] diff --git a/samples/AuthorizationAndCleanArchitecture/Controllers/KeycloakAdminController.cs b/samples/AuthorizationAndCleanArchitecture/Controllers/KeycloakAdminController.cs index ed76d51b..72fd26f5 100644 --- a/samples/AuthorizationAndCleanArchitecture/Controllers/KeycloakAdminController.cs +++ b/samples/AuthorizationAndCleanArchitecture/Controllers/KeycloakAdminController.cs @@ -2,18 +2,19 @@ namespace Api.Controllers; using Keycloak.AuthServices.Sdk.Admin; using Keycloak.AuthServices.Sdk.Admin.Models; +using Keycloak.AuthServices.Sdk.Protection; using Microsoft.AspNetCore.Mvc; [Route("api/keycloak-api")] public class KeycloakAdminController : ApiControllerBase { private readonly IKeycloakRealmClient keycloakRealmClient; - private readonly IKeycloakProtectedResourceClient protectedResourceClient; + private readonly IKeycloakProtectionClient protectedResourceClient; private const string DefaultRealm = "authz"; public KeycloakAdminController( IKeycloakRealmClient keycloakRealmClient, - IKeycloakProtectedResourceClient protectedResourceClient) + IKeycloakProtectionClient protectedResourceClient) { this.keycloakRealmClient = keycloakRealmClient; this.protectedResourceClient = protectedResourceClient; diff --git a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AccessTokenPropagationExtensions.cs b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AccessTokenPropagationExtensions.cs index 8be168a4..860f9d75 100644 --- a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AccessTokenPropagationExtensions.cs +++ b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AccessTokenPropagationExtensions.cs @@ -20,7 +20,7 @@ public static IHttpClientBuilder AddHeaderPropagation(this IHttpClientBuilder bu (sp) => { var contextAccessor = sp.GetRequiredService(); - var options = sp.GetRequiredService>(); + var options = sp.GetRequiredService>(); return new AccessTokenPropagationHandler(contextAccessor, options); } diff --git a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AccessTokenPropagationHandler.cs b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AccessTokenPropagationHandler.cs index a5f0c0c8..d6e651a9 100644 --- a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AccessTokenPropagationHandler.cs +++ b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AccessTokenPropagationHandler.cs @@ -12,16 +12,16 @@ public class AccessTokenPropagationHandler : DelegatingHandler { private readonly IHttpContextAccessor contextAccessor; - private readonly KeycloakProtectionClientOptions options; + private readonly KeycloakAuthorizationServerClientOptions options; /// /// Initializes a new instance of the class. /// /// The HTTP context accessor. - /// The Keycloak protection client options. + /// The Keycloak client options. public AccessTokenPropagationHandler( IHttpContextAccessor contextAccessor, - IOptions options + IOptions options ) { this.contextAccessor = contextAccessor; diff --git a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/KeycloakProtectionClient.cs b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AuthorizationServerClient.cs similarity index 92% rename from src/Keycloak.AuthServices.Authorization/AuthorizationServer/KeycloakProtectionClient.cs rename to src/Keycloak.AuthServices.Authorization/AuthorizationServer/AuthorizationServerClient.cs index b4601400..7bb357e0 100644 --- a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/KeycloakProtectionClient.cs +++ b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AuthorizationServerClient.cs @@ -7,23 +7,22 @@ namespace Keycloak.AuthServices.Authorization.AuthorizationServer; using Microsoft.Extensions.Options; /// -public class KeycloakProtectionClient : IKeycloakProtectionClient +public class AuthorizationServerClient : IAuthorizationServerClient { private readonly HttpClient httpClient; - private readonly IOptions options; - private readonly ILogger logger; + private readonly IOptions options; + private readonly ILogger logger; /// - /// Constructs KeycloakProtectionClient /// /// /// /// /// - public KeycloakProtectionClient( + public AuthorizationServerClient( HttpClient httpClient, - IOptions clientOptions, - ILogger logger + IOptions clientOptions, + ILogger logger ) { this.httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); diff --git a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/IKeycloakProtectionClient.cs b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/IAuthorizationServerClient.cs similarity index 93% rename from src/Keycloak.AuthServices.Authorization/AuthorizationServer/IKeycloakProtectionClient.cs rename to src/Keycloak.AuthServices.Authorization/AuthorizationServer/IAuthorizationServerClient.cs index 1c304d6e..940f3643 100644 --- a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/IKeycloakProtectionClient.cs +++ b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/IAuthorizationServerClient.cs @@ -1,9 +1,9 @@ namespace Keycloak.AuthServices.Authorization.AuthorizationServer; /// -/// Keycloak Protection API +/// Keycloak AuthorizationServer API /// -public interface IKeycloakProtectionClient +public interface IAuthorizationServerClient { /// /// Verifies access to the protected resource. Sends decision request to token endpoint {resource}#{scope} diff --git a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/KeycloakProtectionClientOptions.cs b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/KeycloakAuthorizationServerClientOptions.cs similarity index 93% rename from src/Keycloak.AuthServices.Authorization/AuthorizationServer/KeycloakProtectionClientOptions.cs rename to src/Keycloak.AuthServices.Authorization/AuthorizationServer/KeycloakAuthorizationServerClientOptions.cs index 57748f76..b0d5ee3e 100644 --- a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/KeycloakProtectionClientOptions.cs +++ b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/KeycloakAuthorizationServerClientOptions.cs @@ -5,7 +5,7 @@ namespace Keycloak.AuthServices.Authorization.AuthorizationServer; /// /// Defines a set of options used to perform Authorization Server calls /// -public sealed class KeycloakProtectionClientOptions : KeycloakInstallationOptions +public sealed class KeycloakAuthorizationServerClientOptions : KeycloakInstallationOptions { /// /// Default section name. diff --git a/src/Keycloak.AuthServices.Authorization/Requirements/DecisionRequirement.cs b/src/Keycloak.AuthServices.Authorization/Requirements/DecisionRequirement.cs index 1a021eff..6714d08c 100644 --- a/src/Keycloak.AuthServices.Authorization/Requirements/DecisionRequirement.cs +++ b/src/Keycloak.AuthServices.Authorization/Requirements/DecisionRequirement.cs @@ -69,7 +69,7 @@ public override string ToString() => /// public partial class DecisionRequirementHandler : AuthorizationHandler { - private readonly IKeycloakProtectionClient client; + private readonly IAuthorizationServerClient client; private readonly ILogger logger; /// @@ -78,7 +78,7 @@ public partial class DecisionRequirementHandler : AuthorizationHandler /// public DecisionRequirementHandler( - IKeycloakProtectionClient client, + IAuthorizationServerClient client, ILogger logger ) { diff --git a/src/Keycloak.AuthServices.Authorization/ServiceCollectionExtensions.cs b/src/Keycloak.AuthServices.Authorization/ServiceCollectionExtensions.cs index f574d7ea..9263b9b0 100644 --- a/src/Keycloak.AuthServices.Authorization/ServiceCollectionExtensions.cs +++ b/src/Keycloak.AuthServices.Authorization/ServiceCollectionExtensions.cs @@ -81,7 +81,7 @@ public static IServiceCollection AddKeycloakAuthorization( } /// - /// Adds Keycloak Protection client and auto header propagation + /// Adds Keycloak client and auto header propagation /// /// /// @@ -92,7 +92,7 @@ public static IHttpClientBuilder AddAuthorizationServer( this IServiceCollection services, IConfiguration configuration, Action? configureClient = default, - string configSectionName = KeycloakProtectionClientOptions.Section + string configSectionName = KeycloakAuthorizationServerClientOptions.Section ) => services.AddAuthorizationServer( configuration.GetSection(configSectionName), @@ -100,7 +100,7 @@ public static IHttpClientBuilder AddAuthorizationServer( ); /// - /// Adds Keycloak Protection client and auto header propagation + /// Adds Keycloak client and auto header propagation /// /// /// @@ -117,7 +117,7 @@ public static IHttpClientBuilder AddAuthorizationServer( ); /// - /// Adds Keycloak Protection client and auto header propagation + /// Adds Keycloak client and auto header propagation /// /// /// @@ -125,7 +125,7 @@ public static IHttpClientBuilder AddAuthorizationServer( /// public static IHttpClientBuilder AddAuthorizationServer( this IServiceCollection services, - Action configureKeycloakOptions, + Action configureKeycloakOptions, Action? configureClient = default ) { @@ -137,7 +137,7 @@ public static IHttpClientBuilder AddAuthorizationServer( services.AddSingleton(); // (!) resolved locally, will not work with PostConfigure and IOptions pattern - var keycloakOptions = new KeycloakProtectionClientOptions(); + var keycloakOptions = new KeycloakAuthorizationServerClientOptions(); configureKeycloakOptions.Invoke(keycloakOptions); if (keycloakOptions.UseProtectedResourcePolicyProvider) @@ -146,12 +146,12 @@ public static IHttpClientBuilder AddAuthorizationServer( } return services - .AddHttpClient() + .AddHttpClient() .ConfigureHttpClient( (serviceProvider, client) => { var keycloakOptions = serviceProvider - .GetRequiredService>() + .GetRequiredService>() .Value; client.BaseAddress = new Uri(keycloakOptions.KeycloakUrlRealm); diff --git a/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs b/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs index 70d5796e..9bbf1d5c 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs @@ -13,21 +13,6 @@ internal static class ApiUrls internal const string GetRealm = $"{AdminApiBase}/{Realms}/{RealmParam}"; - #region Resource API - - internal const string GetResources = "/realms/{realm}/authz/protection/resource_set"; - - internal const string GetResource = $"{GetResources}/{{id}}"; - - internal const string CreateResource = $"{GetResources}"; - - internal const string PutResource = $"{GetResource}"; - - internal const string GetResourceByExactName = - "/realms/{realm}/authz/protection/resource_set?&exactName=true"; - - #endregion - #region User API internal const string GetUsers = $"{GetRealm}/users"; diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakClient.cs index 4c1369eb..acdff586 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakClient.cs @@ -1,4 +1,5 @@ namespace Keycloak.AuthServices.Sdk.Admin; + /// /// Keycloak Admin API Client /// @@ -7,6 +8,5 @@ namespace Keycloak.AuthServices.Sdk.Admin; /// public interface IKeycloakClient : IKeycloakRealmClient, - IKeycloakProtectedResourceClient, IKeycloakUserClient, IKeycloakGroupClient { } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakProtectedResourceClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakProtectedResourceClient.cs deleted file mode 100644 index 23d23a03..00000000 --- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakProtectedResourceClient.cs +++ /dev/null @@ -1,130 +0,0 @@ -namespace Keycloak.AuthServices.Sdk.Admin; - -/// -/// Access to protected resource API -/// -public interface IKeycloakProtectedResourceClient -{ - // /// - // /// Searches for resource - // /// - // /// - // /// - // Task> GetResources(string realm); - - // /// - // /// Gets resource by Id - // /// - // /// - // /// - // /// - // Task GetResource(string realm, string id); - - // /// - // /// Gets resource by Name - // /// - // /// - // /// https://github.com/keycloak/keycloak-documentation/blob/main/authorization_services/topics/service-protection-resources-api-papi.adoc#querying-resources - // /// - // /// - // /// - // /// - // Task SearchResourcesByName(string realm, string name); - - // /// - // /// Creates resource - // /// - // /// - // /// - // /// - // Task CreateResource(string realm, Resource resource); - - // /// - // /// Updates resource - // /// - // /// - // /// Docs: https://github.com/keycloak/keycloak-documentation/blob/main/authorization_services/topics/service-protection-resources-api-papi.adoc#updating-resources - // /// - // /// - // /// - // /// - // /// - // Task UpdateResource(string realm, string id, Resource resource); - - // /// - // /// Searches for resource - // /// - // /// Realm name (not ID). - // /// Optional query parameters - // /// - // [Get(KeycloakClientApiConstants.GetResources)] - // [Headers("Accept: application/json")] - // Task> GetResources(string realm, [Query] GetResourcesRequestParameters? getResourcesRequestParameters = default); - - // /// - // /// Searches for resources. NOTE: you must set the property to true - // /// - // /// Realm name (not ID). - // /// Query parameters - // /// - // [Get(KeycloakClientApiConstants.GetResources)] - // [Headers("Accept: application/json")] - // Task> GetResourcesDeep(string realm, [Query] GetResourcesDeepRequestParameters getResourcesDeepRequestParameters); - - // /// - // /// Gets resource by Id - // /// - // /// Realm name (not ID). - // /// Resource ID. - // /// - // [Get(KeycloakClientApiConstants.GetResource)] - // [Headers("Accept: application/json")] - // Task GetResource(string realm, [AliasAs("id")] string resourceId); - - // /// - // /// Gets resource by Name - // /// - // /// - // /// https://github.com/keycloak/keycloak/blob/main/docs/documentation/authorization_services/topics/service-protection-resources-api-papi.adoc#querying-resources - // /// - // /// Realm name (not ID). - // /// - // /// - // [Get(KeycloakClientApiConstants.GetResourceByExactName)] - // [Headers("Accept: application/json")] - // Task SearchResourcesByName(string realm, [Query] string name); - - // /// - // /// Creates resource - // /// - // /// Realm name (not ID). - // /// - // /// - // [Post(KeycloakClientApiConstants.CreateResource)] - // [Headers("Accept: application/json", "Content-Type: application/json")] - // Task CreateResource(string realm, [Body] Resource resource); - - // /// - // /// Updates resource - // /// - // /// - // /// Docs: https://github.com/keycloak/keycloak-documentation/blob/main/authorization_services/topics/service-protection-resources-api-papi.adoc#updating-resources - // /// - // /// Realm name (not ID). - // /// Resource ID. - // /// - // /// - // [Put(KeycloakClientApiConstants.PutResource)] - // [Headers("Accept: application/json", "Content-Type: application/json")] - // Task UpdateResource(string realm, [AliasAs("id")] string resourceId, [Body] Resource resource); - - // /// - // /// Delete a resource - // /// - // /// Realm name (not ID). - // /// Resource ID. - // /// - // [Delete(KeycloakClientApiConstants.DeleteResource)] - // [Headers("Accept: application/json")] - // Task DeleteResource(string realm, [AliasAs("id")] string resourceId); -} diff --git a/src/Keycloak.AuthServices.Sdk/KeycloakProtectionClientOptions.cs b/src/Keycloak.AuthServices.Sdk/KeycloakProtectionClientOptions.cs new file mode 100644 index 00000000..d7fa9c0a --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/KeycloakProtectionClientOptions.cs @@ -0,0 +1,14 @@ +namespace Keycloak.AuthServices.Sdk; + +using Common; + +/// +/// Defines a set of options used to perform Protection API HTTP Client calls +/// +public sealed class KeycloakProtectionClientOptions : KeycloakInstallationOptions +{ + /// + /// Default section name + /// + public const string Section = ConfigurationConstants.ConfigurationPrefix; +} diff --git a/src/Keycloak.AuthServices.Sdk/Protection/ApiUrls.cs b/src/Keycloak.AuthServices.Sdk/Protection/ApiUrls.cs new file mode 100644 index 00000000..a32bd36e --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/Protection/ApiUrls.cs @@ -0,0 +1,20 @@ +namespace Keycloak.AuthServices.Sdk.Protection; + +/// +/// Keycloak Protection API +/// +internal static class ApiUrls +{ + internal const string GetResources = "/realms/{realm}/authz/protection/resource_set"; + + internal const string GetResource = $"{GetResources}/{{id}}"; + + internal const string CreateResource = $"{GetResources}"; + + internal const string UpdateResource = $"{GetResource}"; + + internal const string DeleteResource = $"{GetResource}"; + + public static string WithRealm(this string path, string realm) => + path.Replace("{realm}", realm); +} diff --git a/src/Keycloak.AuthServices.Sdk/Protection/IKeycloakProtectionClient.cs b/src/Keycloak.AuthServices.Sdk/Protection/IKeycloakProtectionClient.cs new file mode 100644 index 00000000..2a20d513 --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/Protection/IKeycloakProtectionClient.cs @@ -0,0 +1,239 @@ +namespace Keycloak.AuthServices.Sdk.Protection; + +using Keycloak.AuthServices.Sdk.Protection.Models; +using Keycloak.AuthServices.Sdk.Protection.Requests; + +/// +/// Access to protected resource API. +/// +/// +/// See: https://www.keycloak.org/docs/latest/authorization_services/index.html#_service_protection_api +/// +public interface IKeycloakProtectionClient +{ + #region ResourcesRegion + /// + /// Searches for resource + /// + /// Realm name (not ID). + /// Optional query parameters + /// + /// + Task GetResourcesIdsWithResponseAsync( + string realm, + GetResourcesRequestParameters? parameters = default, + CancellationToken cancellationToken = default + ); + + /// + /// Searches for resource + /// + /// Realm name (not ID). + /// Optional query parameters + /// + /// + async Task> GetResourcesIdsAsync( + string realm, + GetResourcesRequestParameters? parameters = default, + CancellationToken cancellationToken = default + ) + { + var response = await this.GetResourcesIdsWithResponseAsync( + realm, + parameters, + cancellationToken + ); + + return await response.GetResponseAsync>(cancellationToken) + ?? Array.Empty(); + } + + /// + /// Searches for resources + /// + /// Realm name (not ID). + /// Optional query parameters + /// + /// + Task GetResourcesWithResponseAsync( + string realm, + GetResourcesRequestParameters? parameters = default, + CancellationToken cancellationToken = default + ); + + /// + /// Searches for resources + /// + /// Realm name (not ID). + /// Optional query parameters + /// + /// + async Task> GetResourcesAsync( + string realm, + GetResourcesRequestParameters? parameters = default, + CancellationToken cancellationToken = default + ) + { + var response = await this.GetResourcesWithResponseAsync( + realm, + parameters, + cancellationToken + ); + + return await response.GetResponseAsync>(cancellationToken) + ?? Array.Empty(); + } + + /// + /// Gets resource by Id + /// + /// Realm name (not ID). + /// Resource ID. + /// + /// + Task GetResourceWithResponseAsync( + string realm, + string resourceId, + CancellationToken cancellationToken = default + ); + + /// + /// Gets resource by Id + /// + /// Realm name (not ID). + /// Resource ID. + /// + /// + async Task GetResourceAsync( + string realm, + string resourceId, + CancellationToken cancellationToken = default + ) + { + var response = await this.GetResourceWithResponseAsync( + realm, + resourceId, + cancellationToken + ); + + return await response.GetResponseAsync(cancellationToken) ?? new(); + } + + /// + /// Creates resource + /// + /// Realm name (not ID). + /// + /// + /// + Task CreateResourceWithResponseAsync( + string realm, + Resource resource, + CancellationToken cancellationToken = default + ); + + /// + /// Creates resource + /// + /// Realm name (not ID). + /// + /// + /// + async Task CreateResourceAsync( + string realm, + Resource resource, + CancellationToken cancellationToken = default + ) + { + var response = await this.CreateResourceWithResponseAsync( + realm, + resource, + cancellationToken + ); + + return await response.GetResponseAsync(cancellationToken) ?? new(); + } + + /// + /// Updates resource + /// + /// + /// Docs: https://github.com/keycloak/keycloak-documentation/blob/main/authorization_services/topics/service-protection-resources-api-papi.adoc#updating-resources + /// + /// Realm name (not ID). + /// Resource ID. + /// + /// + /// + Task UpdateResourceWithResponseAsync( + string realm, + string resourceId, + Resource resource, + CancellationToken cancellationToken = default + ); + + /// + /// Updates resource + /// + /// + /// Docs: https://github.com/keycloak/keycloak-documentation/blob/main/authorization_services/topics/service-protection-resources-api-papi.adoc#updating-resources + /// + /// Realm name (not ID). + /// Resource ID. + /// + /// + /// + async Task UpdateResourceAsync( + string realm, + string resourceId, + Resource resource, + CancellationToken cancellationToken = default + ) + { + var response = await this.UpdateResourceWithResponseAsync( + realm, + resourceId, + resource, + cancellationToken + ); + + await response.EnsureResponseAsync(cancellationToken); + } + + /// + /// Deletes a resource + /// + /// Realm name (not ID). + /// Resource ID. + /// + /// + Task DeleteResourceWithResponseAsync( + string realm, + string resourceId, + CancellationToken cancellationToken = default + ); + + /// + /// Deletes a resource + /// + /// Realm name (not ID). + /// Resource ID. + /// + /// + async Task DeleteResourceAsync( + string realm, + string resourceId, + CancellationToken cancellationToken = default + ) + { + var response = await this.DeleteResourceWithResponseAsync( + realm, + resourceId, + cancellationToken + ); + + await response.EnsureResponseAsync(cancellationToken); + } + + #endregion +} diff --git a/src/Keycloak.AuthServices.Sdk/Protection/KeycloakProtectionClient.cs b/src/Keycloak.AuthServices.Sdk/Protection/KeycloakProtectionClient.cs new file mode 100644 index 00000000..fc178221 --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/Protection/KeycloakProtectionClient.cs @@ -0,0 +1,150 @@ +namespace Keycloak.AuthServices.Sdk.Protection; + +using System.Net.Http.Json; +using Keycloak.AuthServices.Sdk.Protection.Models; +using Keycloak.AuthServices.Sdk.Protection.Requests; +using Keycloak.AuthServices.Sdk.Utils; + +/// +/// TBD: +/// +public partial class KeycloakProtectionClient : IKeycloakProtectionClient +{ + private readonly HttpClient httpClient; + + /// + /// TBD: + /// + /// + public KeycloakProtectionClient(HttpClient httpClient) => this.httpClient = httpClient; + + /// + public async Task CreateResourceWithResponseAsync( + string realm, + Resource resource, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.CreateResource.WithRealm(realm); + + var responseMessage = await this.httpClient.PostAsJsonAsync( + path, + resource, + cancellationToken + ); + + return responseMessage!; + } + + /// + public async Task DeleteResourceWithResponseAsync( + string realm, + string resourceId, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.DeleteResource.WithRealm(realm).Replace("{id}", resourceId); + + var responseMessage = await this.httpClient.DeleteAsync(path, cancellationToken); + + return responseMessage!; + } + + /// + public Task GetResourcesIdsWithResponseAsync( + string realm, + GetResourcesRequestParameters? parameters = null, + CancellationToken cancellationToken = default + ) => this.GetResourcesWithResponseCoreAsync(realm, false, parameters, cancellationToken); + + /// + public Task GetResourcesWithResponseAsync( + string realm, + GetResourcesRequestParameters? parameters = null, + CancellationToken cancellationToken = default + ) => this.GetResourcesWithResponseCoreAsync(realm, true, parameters, cancellationToken); + + /// + public async Task GetResourceWithResponseAsync( + string realm, + string resourceId, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.GetResource.WithRealm(realm).Replace("{id}", resourceId); + + var responseMessage = await this.httpClient.GetAsync(path, cancellationToken); + + return responseMessage!; + } + + /// + public async Task UpdateResourceWithResponseAsync( + string realm, + string resourceId, + Resource resource, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.UpdateResource.WithRealm(realm).Replace("{id}", resourceId); + + var responseMessage = await this.httpClient.PutAsJsonAsync( + path, + resource, + cancellationToken + ); + + return responseMessage!; + } + + private async Task GetResourcesWithResponseCoreAsync( + string realm, + bool deep, + GetResourcesRequestParameters? parameters = null, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.GetResources.WithRealm(realm); + + var queryBuilder = new QueryBuilder { { "deep", deep.ToString() } }; + + if (parameters != null) + { + if (parameters.Name is not null) + { + queryBuilder.Add("name", parameters.Name); + } + + if (parameters.ExactName.HasValue) + { + queryBuilder.Add("exactName", parameters.ExactName.ToString()); + } + + if (parameters.Uri is not null) + { + queryBuilder.Add("uri", parameters.Uri); + } + + if (parameters.Owner is not null) + { + queryBuilder.Add("owner", parameters.Owner); + } + + if (parameters.ResourceType is not null) + { + queryBuilder.Add("type", parameters.ResourceType); + } + + if (parameters.Scope is not null) + { + queryBuilder.Add("scope", parameters.Scope); + } + } + + var url = path + queryBuilder.ToQueryString(); + + var responseMessage = await this.httpClient.GetAsync(url, cancellationToken); + + return responseMessage!; + } +} diff --git a/src/Keycloak.AuthServices.Sdk/Protection/Models/Resource.cs b/src/Keycloak.AuthServices.Sdk/Protection/Models/Resource.cs new file mode 100644 index 00000000..ce982884 --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/Protection/Models/Resource.cs @@ -0,0 +1,56 @@ +namespace Keycloak.AuthServices.Sdk.Protection.Models; + +using System.Text.Json.Serialization; + +/// +/// +public class Resource +{ + /// + /// Constructs resource + /// + /// Name of the resource + /// Scope of the resource. Usually, it is in a form of verb + public Resource(string name, string[] scopes) + { + this.Name = name; + this.Scopes = scopes; + } + + /// + /// Resource name + /// + public string Name { get; } + + /// + /// Display name + /// + public string? DisplayName { get; init; } + + /// + /// Resource type + /// + /// urn:workspace-authz:resource:workspaces + public string? Type { get; init; } + + /// + /// Resource scopes + /// + [JsonPropertyName("resource_scopes")] + public string[] Scopes { get; } + + /// + /// Resource attributes + /// + public Dictionary Attributes { get; init; } = new Dictionary(); + + /// + /// When set, will be used to enforce ownership at the time of the request + /// + public string? Owner { get; set; } + + /// + /// When set, will enforce owner-managed-access. + /// + public bool? OwnerManagedAccess { get; set; } +} diff --git a/src/Keycloak.AuthServices.Sdk/Protection/Models/ResourceResponse.cs b/src/Keycloak.AuthServices.Sdk/Protection/Models/ResourceResponse.cs new file mode 100644 index 00000000..026f0852 --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/Protection/Models/ResourceResponse.cs @@ -0,0 +1,84 @@ +namespace Keycloak.AuthServices.Sdk.Protection.Models; + +using System.Text.Json.Serialization; +using Keycloak.AuthServices.Sdk.Admin.Models; + +/// +/// Represents a response containing information about a resource. +/// +public class ResourceResponse +{ + /// + /// Gets or sets the ID of the resource. + /// + [JsonPropertyName("_id")] + public string Id { get; set; } = default!; + + /// + /// Gets or sets the name of the resource. + /// + public string? Name { get; set; } + + /// + /// Gets or sets the type of the resource. + /// + public string? Type { get; set; } + + /// + /// Gets or sets the owner of the resource. + /// + public Owner? Owner { get; set; } + + /// + /// Gets or sets a value indicating whether the access to the resource is managed by the owner. + /// + public bool? OwnerManagedAccess { get; set; } + + /// + /// Gets or sets the display name of the resource. + /// + public string? DisplayName { get; set; } + + /// + /// Gets or sets the attributes associated with the resource. + /// + public Dictionary?>? Attributes { get; set; } + + /// + /// Gets or sets the URIs associated with the resource. + /// + public List? Uris { get; set; } + + /// + /// Gets or sets the resource scopes associated with the resource. + /// + [JsonPropertyName("resource_scopes")] + public List? ResourceScopes { get; set; } + + /// + /// Gets or sets the scopes associated with the resource. + /// + public List? Scopes { get; set; } +} + +/// +/// Represents a resource scope. +/// +public class ResourceScope +{ + /// + /// Gets or sets the name of the resource scope. + /// + public string Name { get; set; } = default!; +} + +/// +/// Represents a scope. +/// +public class Scope +{ + /// + /// Gets or sets the name of the scope. + /// + public string Name { get; set; } = default!; +} diff --git a/src/Keycloak.AuthServices.Sdk/Protection/Requests/GetResourcesRequestParameters.cs b/src/Keycloak.AuthServices.Sdk/Protection/Requests/GetResourcesRequestParameters.cs new file mode 100644 index 00000000..62d769ba --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/Protection/Requests/GetResourcesRequestParameters.cs @@ -0,0 +1,42 @@ +namespace Keycloak.AuthServices.Sdk.Protection.Requests; + +/// +/// Optional parameters for the endpoint. +/// +public class GetResourcesRequestParameters +{ + /// + /// Query resources given a name or pattern. + /// + public string? Name { get; set; } + + /// + /// By default, the filter will match any resource with the given pattern. + /// Set this to true to restrict the query to only return resources with an exact match. + /// + /// + /// https://github.com/keycloak/keycloak-documentation/blob/main/authorization_services/topics/service-protection-resources-api-papi.adoc#querying-resources + /// https://www.keycloak.org/docs/latest/authorization_services/index.html#_service_protection_resources_api + /// + public bool? ExactName { get; set; } + + /// + /// Query resources that match a URI pattern. + /// + public string? Uri { get; set; } + + /// + /// Query resources given an owner. + /// + public string? Owner { get; set; } + + /// + /// Query resources of a specific type. + /// + public string? ResourceType { get; set; } + + /// + /// Query resources with a specific scope. + /// + public string? Scope { get; set; } +} diff --git a/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs b/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs index 48f393ca..54b2dc72 100644 --- a/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs +++ b/src/Keycloak.AuthServices.Sdk/ServiceCollectionExtensions.cs @@ -2,12 +2,13 @@ namespace Keycloak.AuthServices.Sdk; using Keycloak.AuthServices.Common; using Keycloak.AuthServices.Sdk.Admin; +using Keycloak.AuthServices.Sdk.Protection; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; /// -/// Adds HTTP Client SDK +/// Adds HTTP Client SDKs /// public static class ServiceCollectionExtensions { @@ -65,9 +66,7 @@ public static IHttpClientBuilder AddKeycloakAdminHttpClient( services.AddTransient(sp => sp.GetRequiredService()); services.AddTransient(sp => sp.GetRequiredService()); services.AddTransient(sp => sp.GetRequiredService()); - services.AddTransient(sp => - sp.GetRequiredService() - ); + return services .AddHttpClient( "keycloak_admin_api", @@ -88,27 +87,122 @@ public static IHttpClientBuilder AddKeycloakAdminHttpClient( /// TBD: /// /// - /// + /// /// /// v public static IHttpClientBuilder AddKeycloakAdminHttpClient( this IServiceCollection services, - KeycloakAdminClientOptions keycloakAdminClientOptions, + KeycloakAdminClientOptions keycloakOptions, Action? configureClient = default ) { void configureKeycloakOptions(KeycloakAdminClientOptions options) { - options.Realm = keycloakAdminClientOptions.Realm; - options.AuthServerUrl = keycloakAdminClientOptions.AuthServerUrl; - options.Resource = keycloakAdminClientOptions.Resource; + options.Realm = keycloakOptions.Realm; + options.AuthServerUrl = keycloakOptions.AuthServerUrl; + options.Resource = keycloakOptions.Resource; + options.Credentials = keycloakOptions.Credentials; // redundant - options.SslRequired = keycloakAdminClientOptions.SslRequired; - options.VerifyTokenAudience = keycloakAdminClientOptions.VerifyTokenAudience; - options.Credentials = keycloakAdminClientOptions.Credentials; - options.TokenClockSkew = keycloakAdminClientOptions.TokenClockSkew; + options.SslRequired = keycloakOptions.SslRequired; + options.VerifyTokenAudience = keycloakOptions.VerifyTokenAudience; + options.TokenClockSkew = keycloakOptions.TokenClockSkew; } return services.AddKeycloakAdminHttpClient(configureKeycloakOptions, configureClient); } + + /// + /// TBD: + /// + /// + /// + /// + /// + /// + public static IHttpClientBuilder AddKeycloakProtectionHttpClient( + this IServiceCollection services, + IConfiguration configuration, + Action? configureClient = default, + string keycloakClientSectionName = KeycloakProtectionClientOptions.Section + ) => + services.AddKeycloakProtectionHttpClient( + options => configuration.BindKeycloakOptions(options, keycloakClientSectionName), + configureClient + ); + + /// + /// TBD: + /// + /// + /// + /// + /// + public static IHttpClientBuilder AddKeycloakProtectionHttpClient( + this IServiceCollection services, + IConfigurationSection configurationSection, + Action? configureClient = default + ) => + services.AddKeycloakProtectionHttpClient( + options => configurationSection.BindKeycloakOptions(options), + configureClient + ); + + /// + /// TBD: + /// + /// + /// + /// + /// v + public static IHttpClientBuilder AddKeycloakProtectionHttpClient( + this IServiceCollection services, + Action configureKeycloakOptions, + Action? configureClient = default + ) + { + services.Configure(configureKeycloakOptions); + + return services + .AddHttpClient( + "keycloak_protection_api", + (sp, http) => + { + var keycloakOptions = sp.GetRequiredService< + IOptions + >(); + + http.BaseAddress = new Uri(keycloakOptions.Value.KeycloakUrlRealm); + configureClient?.Invoke(http); + } + ) + .AddTypedClient(); + } + + /// + /// TBD: + /// + /// + /// + /// + /// v + public static IHttpClientBuilder AddKeycloakProtectionHttpClient( + this IServiceCollection services, + KeycloakProtectionClientOptions keycloakOptions, + Action? configureClient = default + ) + { + void configureKeycloakOptions(KeycloakProtectionClientOptions options) + { + options.Realm = keycloakOptions.Realm; + options.AuthServerUrl = keycloakOptions.AuthServerUrl; + options.Resource = keycloakOptions.Resource; + options.Credentials = keycloakOptions.Credentials; + // redundant + options.SslRequired = keycloakOptions.SslRequired; + options.VerifyTokenAudience = keycloakOptions.VerifyTokenAudience; + options.TokenClockSkew = keycloakOptions.TokenClockSkew; + } + + return services.AddKeycloakProtectionHttpClient(configureKeycloakOptions, configureClient); + } } diff --git a/tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs b/tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs index 19b69934..eaa33130 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs +++ b/tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs @@ -50,7 +50,7 @@ public async Task RequireProtectedResource_DefaultResource_Verified() services .AddAuthorizationServer(context.Configuration) - .AddStandardResilienceHandler(); // an example of how to extend IKeycloakProtectionClient by adding Polly + .AddStandardResilienceHandler(); // an example of how to extend based on IHttpClientBuilder by adding Polly #endregion RequireProtectedResource_DefaultResource_Verified services.PostConfigure(options => diff --git a/tests/Keycloak.AuthServices.IntegrationTests/Protection/KeycloakProtectionClientTests.cs b/tests/Keycloak.AuthServices.IntegrationTests/Protection/KeycloakProtectionClientTests.cs new file mode 100644 index 00000000..62963b7a --- /dev/null +++ b/tests/Keycloak.AuthServices.IntegrationTests/Protection/KeycloakProtectionClientTests.cs @@ -0,0 +1,67 @@ +namespace Keycloak.AuthServices.IntegrationTests; + +using Keycloak.AuthServices.Common; +using Keycloak.AuthServices.Sdk; +using Keycloak.AuthServices.Sdk.Protection; +using Microsoft.Extensions.DependencyInjection; +using static Keycloak.AuthServices.IntegrationTests.Utils; + +public class KeycloakProtectionClientTests(ITestOutputHelper testOutputHelper) + : AuthenticationScenarioNoKeycloak() +{ + private static readonly string AppSettings = "appsettings.json"; + + [Fact] + public async Task GetResourcesIdsAsync_Success() + { + var (services, _) = ProtectionHttpClientSetup(AppSettings, testOutputHelper); + + #region GetResourcesIdsAsync_Success + + var sp = services.BuildServiceProvider(); + + var client = sp.GetRequiredService(); + + var resources = await client.GetResourcesIdsAsync("Test"); + + resources.Should().NotBeNull(); + #endregion GetResourcesIdsAsync_Success + } + + [Fact] + public async Task GetResourcesAsync_Success() + { + var (services, configuration) = KeycloakSetup(AppSettings, testOutputHelper); + var tokenClientName = "keycloak_protection_api_token"; + + // NOTE: the code is verbose for demonstration/docs + + #region GetResourcesAsync_Success + var keycloakOptions = configuration.GetKeycloakOptions()!; + + services.AddDistributedMemoryCache(); + services + .AddClientCredentialsTokenManagement() + .AddClient( + tokenClientName, + client => + { + client.ClientId = keycloakOptions.Resource; + client.ClientSecret = keycloakOptions.Credentials.Secret; + client.TokenEndpoint = keycloakOptions.KeycloakTokenEndpoint; + } + ); + + services + .AddKeycloakProtectionHttpClient(configuration) + .AddClientCredentialsTokenHandler(tokenClientName); + + var sp = services.BuildServiceProvider(); + var client = sp.GetRequiredService(); + + var resources = await client.GetResourcesAsync("Test"); + #endregion GetResourcesAsync_Success + + resources.Should().NotBeNull(); + } +} diff --git a/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs b/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs index 2ffb6c42..a84f1b93 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs +++ b/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs @@ -114,14 +114,46 @@ ITestOutputHelper testOutputHelper return (services, configuration); } + public static (IServiceCollection services, IConfiguration configuration1) ProtectionHttpClientSetup( + string fileName, + ITestOutputHelper testOutputHelper + ) + { + var (services, configuration) = KeycloakSetup(fileName, testOutputHelper); + + var tokenClientName = "keycloak_protection_api_token"; + var keycloakOptions = configuration.GetKeycloakOptions()!; + + services.AddDistributedMemoryCache(); + services + .AddClientCredentialsTokenManagement() + .AddClient(tokenClientName, client => BindKeycloak(client, keycloakOptions)); + + services + .AddKeycloakProtectionHttpClient(configuration) + .AddClientCredentialsTokenHandler(tokenClientName); + + return (services, configuration); + } + + private static void BindKeycloak( + Duende.AccessTokenManagement.ClientCredentialsClient client, + KeycloakAdminClientOptions keycloaktOptions + ) + { + client.ClientId = keycloaktOptions.Resource; + client.ClientSecret = keycloaktOptions.Credentials.Secret; + client.TokenEndpoint = keycloaktOptions.KeycloakTokenEndpoint; + } + private static void BindKeycloak( Duende.AccessTokenManagement.ClientCredentialsClient client, - KeycloakAdminClientOptions adminClientOptions + KeycloakProtectionClientOptions keycloaktOptions ) { - client.ClientId = adminClientOptions.Resource; - client.ClientSecret = adminClientOptions.Credentials.Secret; - client.TokenEndpoint = adminClientOptions.KeycloakTokenEndpoint; + client.ClientId = keycloaktOptions.Resource; + client.ClientSecret = keycloaktOptions.Credentials.Secret; + client.TokenEndpoint = keycloaktOptions.KeycloakTokenEndpoint; } public static KeycloakAuthenticationOptions ReadKeycloakAuthenticationOptions(string fileName) From d5135c08995609c2e8228fda15320535852290f1 Mon Sep 17 00:00:00 2001 From: nikiforovall Date: Sat, 4 May 2024 13:27:39 +0300 Subject: [PATCH 6/6] docs: Add IAuthorizationServerClient docs --- KeycloakAuthorizationServicesDotNet.sln | 7 -- docs/.vitepress/config.mts | 14 ++- docs/authorization/resources-api.md | 31 +++++++ .../resources-client-reference.md | 3 + docs/authorization/resources-client.md | 93 +++++++++++++++++++ docs/authorization/resources.md | 24 ----- samples/AuthGettingStarted/Program.cs | 2 +- .../Commands/CreateWorkspaceCommand.cs | 30 +++--- .../Controllers/KeycloakAdminController.cs | 34 ++++--- .../AuthorizationGettingStarted/Program.cs | 13 +-- .../ServiceCollectionExtensions.Auth.cs | 52 +++++++---- .../ServiceCollectionExtensions.Logging.cs | 8 +- .../DependsOnNuGetSource.csproj | 14 --- samples/DependsOnNuGetSource/Program.cs | 33 ------- .../Properties/launchSettings.json | 13 --- .../appsettings.Development.json | 8 -- samples/DependsOnNuGetSource/appsettings.json | 9 -- samples/DependsOnNuGetSource/assets/.env | 3 - samples/DependsOnNuGetSource/assets/run.http | 6 -- samples/DependsOnNuGetSource/keycloak.json | 12 --- .../AccessTokenPropagationExtensions.cs | 2 +- .../AccessTokenPropagationHandler.cs | 4 +- .../AuthorizationServerClient.cs | 4 +- ... => KeycloakAuthorizationServerOptions.cs} | 2 +- .../PoliciesBuilderExtensions.cs | 4 + .../ServiceCollectionExtensions.cs | 38 +++++--- .../Utils.cs | 26 +++--- 27 files changed, 265 insertions(+), 224 deletions(-) create mode 100644 docs/authorization/resources-api.md create mode 100644 docs/authorization/resources-client-reference.md create mode 100644 docs/authorization/resources-client.md delete mode 100644 samples/DependsOnNuGetSource/DependsOnNuGetSource.csproj delete mode 100644 samples/DependsOnNuGetSource/Program.cs delete mode 100644 samples/DependsOnNuGetSource/Properties/launchSettings.json delete mode 100644 samples/DependsOnNuGetSource/appsettings.Development.json delete mode 100644 samples/DependsOnNuGetSource/appsettings.json delete mode 100644 samples/DependsOnNuGetSource/assets/.env delete mode 100644 samples/DependsOnNuGetSource/assets/run.http delete mode 100644 samples/DependsOnNuGetSource/keycloak.json rename src/Keycloak.AuthServices.Authorization/AuthorizationServer/{KeycloakAuthorizationServerClientOptions.cs => KeycloakAuthorizationServerOptions.cs} (93%) diff --git a/KeycloakAuthorizationServicesDotNet.sln b/KeycloakAuthorizationServicesDotNet.sln index 41266c5e..43f04b1a 100644 --- a/KeycloakAuthorizationServicesDotNet.sln +++ b/KeycloakAuthorizationServicesDotNet.sln @@ -58,8 +58,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Keycloak.AuthServices.Authe EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Keycloak.AuthServices.Common.Tests", "tests\Keycloak.AuthServices.Common.Tests\Keycloak.AuthServices.Common.Tests.csproj", "{8F9E1322-568B-4F02-A1DD-4222C8457A42}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependsOnNuGetSource", "samples\DependsOnNuGetSource\DependsOnNuGetSource.csproj", "{82DB0FDE-316D-4741-B335-8B7B36DBE962}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AuthorizationGettingStarted", "samples\AuthorizationGettingStarted\AuthorizationGettingStarted.csproj", "{D64B4098-165B-48AA-BE07-B9E9963E0CB5}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GettingStarted", "samples\GettingStarted\GettingStarted.csproj", "{671BA3B1-DBF2-4161-97B5-433B91A3730E}" @@ -124,10 +122,6 @@ Global {8F9E1322-568B-4F02-A1DD-4222C8457A42}.Debug|Any CPU.Build.0 = Debug|Any CPU {8F9E1322-568B-4F02-A1DD-4222C8457A42}.Release|Any CPU.ActiveCfg = Release|Any CPU {8F9E1322-568B-4F02-A1DD-4222C8457A42}.Release|Any CPU.Build.0 = Release|Any CPU - {82DB0FDE-316D-4741-B335-8B7B36DBE962}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {82DB0FDE-316D-4741-B335-8B7B36DBE962}.Debug|Any CPU.Build.0 = Debug|Any CPU - {82DB0FDE-316D-4741-B335-8B7B36DBE962}.Release|Any CPU.ActiveCfg = Release|Any CPU - {82DB0FDE-316D-4741-B335-8B7B36DBE962}.Release|Any CPU.Build.0 = Release|Any CPU {D64B4098-165B-48AA-BE07-B9E9963E0CB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D64B4098-165B-48AA-BE07-B9E9963E0CB5}.Debug|Any CPU.Build.0 = Debug|Any CPU {D64B4098-165B-48AA-BE07-B9E9963E0CB5}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -166,7 +160,6 @@ Global {A85B6B1E-2030-47BE-9B9F-F645B08E501D} = {96857509-627A-4FD2-AC82-34387619A7B1} {FE34728A-25AA-44E1-A3A6-AB500307C406} = {96857509-627A-4FD2-AC82-34387619A7B1} {8F9E1322-568B-4F02-A1DD-4222C8457A42} = {96857509-627A-4FD2-AC82-34387619A7B1} - {82DB0FDE-316D-4741-B335-8B7B36DBE962} = {AEBE10B1-96B1-4060-B8C1-1F9BFA7A586C} {D64B4098-165B-48AA-BE07-B9E9963E0CB5} = {AEBE10B1-96B1-4060-B8C1-1F9BFA7A586C} {671BA3B1-DBF2-4161-97B5-433B91A3730E} = {AEBE10B1-96B1-4060-B8C1-1F9BFA7A586C} {7499F9F0-1132-46B4-AAA2-D60D9F113293} = {96857509-627A-4FD2-AC82-34387619A7B1} diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 50f4e7eb..e6d01213 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -43,8 +43,18 @@ export default withMermaid({ text: 'Authorization', collapsed: false, items: [ - { text: 'Authorization Server', link: '/authorization/authorization-server' }, - { text: 'Protected Resources ✨', link: '/authorization/resources' }, + { + text: 'Authorization Server', link: '/authorization/authorization-server' + }, + { + text: 'Protected Resources ✨', link: '/authorization/resources', + collapsed: true, + items: [ + { text: 'ASP.NET Core Integration', link: '/authorization/resources-api' }, + { text: 'Use HTTP Client', link: '/authorization/resources-client' }, + { text: 'Client API Reference', link: '/authorization/resources-client-reference' }, + ] + }, ] }, { diff --git a/docs/authorization/resources-api.md b/docs/authorization/resources-api.md new file mode 100644 index 00000000..2f4ff8bd --- /dev/null +++ b/docs/authorization/resources-api.md @@ -0,0 +1,31 @@ +# Protect ASP.NET Core + +ASP.NET Core allows you to built policies based on [AuthorizationBuilder](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authorization.authorizationbuilder). `Keycloak.AuthServices.Authorization` adds [AuthorizationPolicyBuilder](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authorization.authorizationpolicybuilder) extension methods to work with protected resources and configure your polices. + +<<< @/../src/Keycloak.AuthServices.Authorization/PoliciesBuilderExtensions.cs#RequireProtectedResource + + + +## Add to your code + +Here is how to use to use protected resource authorization. + +<<< @/../tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs#RequireProtectedResource_Scopes_Verified{7,12-15,18 cs:line-numbers} + +Here are the assertions from the integration test for this scenario: + +<<< @/../tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs#RequireProtectedResource_Scopes_Verified_Assertion + +Source code of integration test: [tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs](https://github.com/NikiforovAll/keycloak-authorization-services-dotnet/blob/main/tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs) + +## Validate Multiple Scopes + +You can specify multiple scopes to validate against and control comparison by using `ScopesValidationMode`. + +Here is an example for `ScopesValidationMode.AllOf`: + +<<< @/../tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs#RequireProtectedResource_MultipleScopesAllOf_Verified + +Here is an example for `ScopesValidationMode.AnyOf`: + +<<< @/../tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs#RequireProtectedResource_MultipleScopesAnyOf_Verified diff --git a/docs/authorization/resources-client-reference.md b/docs/authorization/resources-client-reference.md new file mode 100644 index 00000000..cdde4cb8 --- /dev/null +++ b/docs/authorization/resources-client-reference.md @@ -0,0 +1,3 @@ +# IAuthorizationServerClient + +<<< @/../src/Keycloak.AuthServices.Authorization/AuthorizationServer/IAuthorizationServerClient.cs diff --git a/docs/authorization/resources-client.md b/docs/authorization/resources-client.md new file mode 100644 index 00000000..a0dc1233 --- /dev/null +++ b/docs/authorization/resources-client.md @@ -0,0 +1,93 @@ +# Use IAuthorizationServerClient + +You can use `IAuthorizationServerClient` directly in your code, but beware that it depends on `IHttpContextAccessor`. + +> [!TIP] +> In situations when you want to authorize access in your code I advise you to use [IAuthorizationService](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authorization.iauthorizationservice) and not `IAuthorizationServerClient` directly. In this case, you still need to define policies and use them by name like this: +> +> ```csharp +> var result = await authorizationService.AuthorizeAsync(claimsPrincipal, policyName); +> ``` + +If you need more control and for some reason you don't want to use *Policies* and [IAuthorizationService](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authorization.iauthorizationservice) you can use `IAuthorizationServerClient` directly. + +## Add to your code + +`AddAuthorizationServer` adds typed HTTP client - `IAuthorizationServerClient` + +```csharp +public static IHttpClientBuilder AddAuthorizationServer( + this IServiceCollection services, + Action configureKeycloakOptions, + Action? configureClient = default +) +``` + +Here is an example of a endpoint: + +```csharp + +app.MapGet("RunAuthorizationServerClient", RunAuthorizationServerClient); + +async Task RunAuthorizationServerClient( + [FromQuery] string resource, + [FromQuery] string scopes, + IAuthorizationServerClient client +) +{ + var success = await client.VerifyAccessToResource(resource, scopes); + + return success ? TypedResults.Ok() : TypedResults.Forbid(); +} +``` + +## Use outside `IHttpContextAccessor` + +There is a way to add just HTTP Client without all other Authorization Server capabilities provided by library. + +## Add to your code + +`AddAuthorizationServerClient` adds typed HTTP client - `IAuthorizationServerClient` + +```csharp +public static IHttpClientBuilder AddAuthorizationServerClient( + this IServiceCollection services, + Action? configureClient = default +) +``` + +Use it: + +```csharp +var client = sp.GetRequiredService(); + +bool success = await client.VerifyAccessToResource( + "my-workspace", + "workspace:read,workspace:delete" +); +``` + +> [!WARNING] +> `AddAuthorizationServerClient` doesn't add header propagation because it relies on `IHttpContextAccessor`, so you will need to provide access token using your custom implementation of [DelegatingHandler](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.delegatinghandler) + +Something like: + +```csharp +public class TokenDelegatingHandler : DelegatingHandler +{ + private readonly ITokenProvider tokenProvider; + + public TokenDelegatingHandler(ITokenProvider tokenProvider) => + this.tokenProvider = tokenProvider; + + protected override async Task SendAsync( + HttpRequestMessage request, + CancellationToken cancellationToken + ) + { + string token = await tokenProvider.GetTokenAsync(); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); + return await base.SendAsync(request, cancellationToken); + } +} +``` diff --git a/docs/authorization/resources.md b/docs/authorization/resources.md index d5726bbd..175a5fe7 100644 --- a/docs/authorization/resources.md +++ b/docs/authorization/resources.md @@ -65,27 +65,3 @@ The *Keycloak Authorization Server* evaluates these policies whenever a user att ![evaluate-permissions-for-admin](https://media.githubusercontent.com/media/NikiforovAll/keycloak-authorization-services-dotnet/main/docs/public/assets/evaluate-permissions-for-admin.png) - -## Add to your code - -Here is how to use to use protected resource authorization. - -<<< @/../tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs#RequireProtectedResource_Scopes_Verified - -Here are the assertions from the integration test for this scenario: - -<<< @/../tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs#RequireProtectedResource_Scopes_Verified_Assertion - -Source code of integration test: [tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs](https://github.com/NikiforovAll/keycloak-authorization-services-dotnet/blob/main/tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs) - -## Validate Multiple Scopes - -You can specify multiple scopes to validate against and control comparison by using `ScopesValidationMode`. - -Here is an example for `ScopesValidationMode.AllOf`: - -<<< @/../tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs#RequireProtectedResource_MultipleScopesAllOf_Verified - -Here is an example for `ScopesValidationMode.AnyOf`: - -<<< @/../tests/Keycloak.AuthServices.IntegrationTests/AuthorizationServerPolicyTests.cs#RequireProtectedResource_MultipleScopesAnyOf_Verified diff --git a/samples/AuthGettingStarted/Program.cs b/samples/AuthGettingStarted/Program.cs index ec23dd7a..35fe9479 100644 --- a/samples/AuthGettingStarted/Program.cs +++ b/samples/AuthGettingStarted/Program.cs @@ -30,7 +30,7 @@ } ) ) - .AddKeycloakAuthorization() + .AddKeycloakAuthorization(configuration) .AddAuthorizationServer(configuration); services.AddKeycloakAdminHttpClient(configuration); diff --git a/samples/AuthorizationAndCleanArchitecture/Application/Commands/CreateWorkspaceCommand.cs b/samples/AuthorizationAndCleanArchitecture/Application/Commands/CreateWorkspaceCommand.cs index f9cf4a39..4c2a98ac 100644 --- a/samples/AuthorizationAndCleanArchitecture/Application/Commands/CreateWorkspaceCommand.cs +++ b/samples/AuthorizationAndCleanArchitecture/Application/Commands/CreateWorkspaceCommand.cs @@ -5,8 +5,8 @@ namespace Api.Application.Commands; using Authorization; using Authorization.Abstractions; using Data; -using Keycloak.AuthServices.Sdk.Admin.Models; using Keycloak.AuthServices.Sdk.Protection; +using Keycloak.AuthServices.Sdk.Protection.Models; using MediatR; [AuthorizeProtectedResource("workspaces", "workspaces:create")] @@ -21,16 +21,17 @@ public class CreateWorkspaceCommandHandler : IRequestHandler GetRealms() - { - return this.Ok(await this.keycloakRealmClient.GetRealmAsync(DefaultRealm)); - } + public async Task GetRealms() => + this.Ok(await this.keycloakRealmClient.GetRealmAsync(DefaultRealm)); [HttpGet("resources")] - public async Task GetResources() - { - return this.Ok(await this.protectedResourceClient.GetResources(DefaultRealm)); - } + public async Task GetResources() => + this.Ok(await this.protectionClient.GetResourcesAsync(DefaultRealm)); [HttpGet("resources/{id}")] - public async Task GetResource(string id) - { - return this.Ok(await this.protectedResourceClient.GetResource(DefaultRealm, id)); - } + public async Task GetResource(string id) => + this.Ok(await this.protectionClient.GetResourceAsync(DefaultRealm, id)); [HttpPost("resources")] public async Task CreateResource() { - var resource = new Resource($"workspaces/{Guid.NewGuid()}", new[] { "workspaces:read", "workspaces:delete" }) + var resource = new Resource( + $"workspaces/{Guid.NewGuid()}", + new[] { "workspaces:read", "workspaces:delete" } + ) { Attributes = { ["test"] = "Owner, Operations" }, Type = "urn:workspace-authz:resource:workspaces", }; - return this.Ok(await this.protectedResourceClient.CreateResource(DefaultRealm, resource)); + return this.Ok(await this.protectionClient.CreateResourceAsync(DefaultRealm, resource)); } } diff --git a/samples/AuthorizationGettingStarted/Program.cs b/samples/AuthorizationGettingStarted/Program.cs index 778fb08a..13db001a 100644 --- a/samples/AuthorizationGettingStarted/Program.cs +++ b/samples/AuthorizationGettingStarted/Program.cs @@ -10,9 +10,7 @@ builder.AddSerilog(); -services - .AddApplicationSwagger(configuration) - .AddAuth(configuration); +services.AddApplicationSwagger(configuration).AddAuth(configuration); services.Configure(opts => { @@ -22,8 +20,7 @@ var app = builder.Build(); -app - .UseHttpsRedirection() +app.UseHttpsRedirection() .UseApplicationSwagger(configuration) .UseAuthentication() .UseAuthorization(); @@ -33,12 +30,10 @@ .RequireAuthorization(RequireAspNetCoreRole); // login with requireed realm role evaluated from corresponding claim -app.MapGet("/endpoint2", (ClaimsPrincipal user) => user) - .RequireAuthorization(RequireRealmRole); +app.MapGet("/endpoint2", (ClaimsPrincipal user) => user).RequireAuthorization(RequireRealmRole); // login with requireed client role evaluated from corresponding claim -app.MapGet("/endpoint3", (ClaimsPrincipal user) => user) - .RequireAuthorization(RequireClientRole); +app.MapGet("/endpoint3", (ClaimsPrincipal user) => user).RequireAuthorization(RequireClientRole); // login based on remotely executed policy // authorization is performed by Keycloak (Authorization Server) diff --git a/samples/AuthorizationGettingStarted/ServiceCollectionExtensions.Auth.cs b/samples/AuthorizationGettingStarted/ServiceCollectionExtensions.Auth.cs index 2be7a2c2..18c7e0bb 100644 --- a/samples/AuthorizationGettingStarted/ServiceCollectionExtensions.Auth.cs +++ b/samples/AuthorizationGettingStarted/ServiceCollectionExtensions.Auth.cs @@ -6,31 +6,41 @@ namespace Microsoft.Extensions.DependencyInjection; public static partial class ServiceCollectionExtensions { - public static IServiceCollection AddAuth(this IServiceCollection services, IConfiguration configuration) + public static IServiceCollection AddAuth( + this IServiceCollection services, + IConfiguration configuration + ) { services.AddKeycloakWebApiAuthentication(configuration); - services.AddAuthorization(options => - { - options.AddPolicy( - Policies.RequireAspNetCoreRole, - builder => builder.RequireRole(Roles.AspNetCoreRole)); + services + .AddAuthorization(options => + { + options.AddPolicy( + Policies.RequireAspNetCoreRole, + builder => builder.RequireRole(Roles.AspNetCoreRole) + ); - options.AddPolicy( - Policies.RequireRealmRole, - builder => builder.RequireRealmRoles(Roles.RealmRole)); + options.AddPolicy( + Policies.RequireRealmRole, + builder => builder.RequireRealmRoles(Roles.RealmRole) + ); - options.AddPolicy( - Policies.RequireClientRole, - builder => builder.RequireResourceRoles(Roles.ClientRole)); + options.AddPolicy( + Policies.RequireClientRole, + builder => builder.RequireResourceRoles(Roles.ClientRole) + ); - options.AddPolicy( - Policies.RequireToBeInKeycloakGroupAsReader, - builder => builder - .RequireAuthenticatedUser() - .RequireProtectedResource("workspace", "workspaces:read")); - - }).AddKeycloakAuthorization().AddAuthorizationServer(configuration); + options.AddPolicy( + Policies.RequireToBeInKeycloakGroupAsReader, + builder => + builder + .RequireAuthenticatedUser() + .RequireProtectedResource("workspace", "workspaces:read") + ); + }) + .AddKeycloakAuthorization(configuration) + .AddAuthorizationServer(configuration); return services; } @@ -55,6 +65,8 @@ public static class Policies public const string RequireClientRole = nameof(RequireClientRole); - public const string RequireToBeInKeycloakGroupAsReader = nameof(RequireToBeInKeycloakGroupAsReader); + public const string RequireToBeInKeycloakGroupAsReader = nameof( + RequireToBeInKeycloakGroupAsReader + ); } } diff --git a/samples/AuthorizationGettingStarted/ServiceCollectionExtensions.Logging.cs b/samples/AuthorizationGettingStarted/ServiceCollectionExtensions.Logging.cs index 624fa1ff..3dfd5c9e 100644 --- a/samples/AuthorizationGettingStarted/ServiceCollectionExtensions.Logging.cs +++ b/samples/AuthorizationGettingStarted/ServiceCollectionExtensions.Logging.cs @@ -10,13 +10,17 @@ public static WebApplicationBuilder AddSerilog(this WebApplicationBuilder builde { Log.Logger = new LoggerConfiguration() .MinimumLevel.Information() - .MinimumLevel.Override("Microsoft.AspNetCore.DataProtection.KeyManagement", LogEventLevel.Warning) + .MinimumLevel.Override( + "Microsoft.AspNetCore.DataProtection.KeyManagement", + LogEventLevel.Warning + ) .MinimumLevel.Override("Microsoft.AspNetCore.Authorization", LogEventLevel.Verbose) .MinimumLevel.Override("System.Net.Http", LogEventLevel.Debug) .MinimumLevel.Override("Keycloak.AuthServices", LogEventLevel.Verbose) .WriteTo.SpectreConsole( "{SourceContext}{NewLine}{Timestamp:HH:mm:ss} [{Level:u4}] {Message:lj}{NewLine}{Exception}", - LogEventLevel.Verbose) + LogEventLevel.Verbose + ) .CreateLogger(); builder.Host.UseSerilog(); diff --git a/samples/DependsOnNuGetSource/DependsOnNuGetSource.csproj b/samples/DependsOnNuGetSource/DependsOnNuGetSource.csproj deleted file mode 100644 index d55400f6..00000000 --- a/samples/DependsOnNuGetSource/DependsOnNuGetSource.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - false - false - - - - - - - - - diff --git a/samples/DependsOnNuGetSource/Program.cs b/samples/DependsOnNuGetSource/Program.cs deleted file mode 100644 index 8fc5c1a4..00000000 --- a/samples/DependsOnNuGetSource/Program.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Keycloak.AuthServices.Authentication; -using Keycloak.AuthServices.Authorization; - -var builder = WebApplication.CreateBuilder(args); - -var host = builder.Host; -var configuration = builder.Configuration; -var services = builder.Services; - -host.ConfigureKeycloakConfigurationSource(); -// conventional registration from keycloak.json -services.AddKeycloakAuthentication(configuration); - -services.AddAuthorization(options => - { - options.AddPolicy("RequireWorkspaces", builder => - { - builder.RequireProtectedResource("workspaces", "workspaces:read") - .RequireRealmRoles("User") - .RequireResourceRoles("Admin"); - }); - }) - .AddKeycloakAuthorization(configuration); - -var app = builder.Build(); - -app.UseAuthentication() - .UseAuthorization(); - -app.MapGet("/workspaces", () => "[]") - .RequireAuthorization("RequireWorkspaces"); - -app.Run(); diff --git a/samples/DependsOnNuGetSource/Properties/launchSettings.json b/samples/DependsOnNuGetSource/Properties/launchSettings.json deleted file mode 100644 index 8f57998c..00000000 --- a/samples/DependsOnNuGetSource/Properties/launchSettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "profiles": { - "MinimalApiDependsOnNuGetSource": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "applicationUrl": "https://localhost:5001;http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} diff --git a/samples/DependsOnNuGetSource/appsettings.Development.json b/samples/DependsOnNuGetSource/appsettings.Development.json deleted file mode 100644 index 34f00ef1..00000000 --- a/samples/DependsOnNuGetSource/appsettings.Development.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Debug", - "Microsoft.AspNetCore": "Information" - } - } -} diff --git a/samples/DependsOnNuGetSource/appsettings.json b/samples/DependsOnNuGetSource/appsettings.json deleted file mode 100644 index 10f68b8c..00000000 --- a/samples/DependsOnNuGetSource/appsettings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*" -} diff --git a/samples/DependsOnNuGetSource/assets/.env b/samples/DependsOnNuGetSource/assets/.env deleted file mode 100644 index 985ec46c..00000000 --- a/samples/DependsOnNuGetSource/assets/.env +++ /dev/null @@ -1,3 +0,0 @@ -BASE_URL=https://localhost:5001 -KEYCLOAK_URL=http://localhost:8088 -ACCESS_TOKEN= diff --git a/samples/DependsOnNuGetSource/assets/run.http b/samples/DependsOnNuGetSource/assets/run.http deleted file mode 100644 index 8b80a244..00000000 --- a/samples/DependsOnNuGetSource/assets/run.http +++ /dev/null @@ -1,6 +0,0 @@ -@baseUrl={{$dotenv BASE_URL}} -@accessToken={{$dotenv ACCESS_TOKEN}} -### - -GET {{baseUrl}}/workspaces -authorization: bearer {{accessToken}} diff --git a/samples/DependsOnNuGetSource/keycloak.json b/samples/DependsOnNuGetSource/keycloak.json deleted file mode 100644 index 301155ad..00000000 --- a/samples/DependsOnNuGetSource/keycloak.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "realm": "authz", - "auth-server-url": "http://localhost:8088/auth/", - "ssl-required": "none", - "resource": "workspace-authz", - "verify-token-audience": true, - "credentials": { - "secret": "bdfcee34-64e0-4516-b20d-a25ed2b66346" - }, - "confidential-port": 0, - "policy-enforcer": {} -} diff --git a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AccessTokenPropagationExtensions.cs b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AccessTokenPropagationExtensions.cs index 860f9d75..9355a8bb 100644 --- a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AccessTokenPropagationExtensions.cs +++ b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AccessTokenPropagationExtensions.cs @@ -20,7 +20,7 @@ public static IHttpClientBuilder AddHeaderPropagation(this IHttpClientBuilder bu (sp) => { var contextAccessor = sp.GetRequiredService(); - var options = sp.GetRequiredService>(); + var options = sp.GetRequiredService>(); return new AccessTokenPropagationHandler(contextAccessor, options); } diff --git a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AccessTokenPropagationHandler.cs b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AccessTokenPropagationHandler.cs index d6e651a9..067dea2e 100644 --- a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AccessTokenPropagationHandler.cs +++ b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AccessTokenPropagationHandler.cs @@ -12,7 +12,7 @@ public class AccessTokenPropagationHandler : DelegatingHandler { private readonly IHttpContextAccessor contextAccessor; - private readonly KeycloakAuthorizationServerClientOptions options; + private readonly KeycloakAuthorizationServerOptions options; /// /// Initializes a new instance of the class. @@ -21,7 +21,7 @@ public class AccessTokenPropagationHandler : DelegatingHandler /// The Keycloak client options. public AccessTokenPropagationHandler( IHttpContextAccessor contextAccessor, - IOptions options + IOptions options ) { this.contextAccessor = contextAccessor; diff --git a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AuthorizationServerClient.cs b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AuthorizationServerClient.cs index 7bb357e0..72565a21 100644 --- a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AuthorizationServerClient.cs +++ b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/AuthorizationServerClient.cs @@ -10,7 +10,7 @@ namespace Keycloak.AuthServices.Authorization.AuthorizationServer; public class AuthorizationServerClient : IAuthorizationServerClient { private readonly HttpClient httpClient; - private readonly IOptions options; + private readonly IOptions options; private readonly ILogger logger; /// @@ -21,7 +21,7 @@ public class AuthorizationServerClient : IAuthorizationServerClient /// public AuthorizationServerClient( HttpClient httpClient, - IOptions clientOptions, + IOptions clientOptions, ILogger logger ) { diff --git a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/KeycloakAuthorizationServerClientOptions.cs b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/KeycloakAuthorizationServerOptions.cs similarity index 93% rename from src/Keycloak.AuthServices.Authorization/AuthorizationServer/KeycloakAuthorizationServerClientOptions.cs rename to src/Keycloak.AuthServices.Authorization/AuthorizationServer/KeycloakAuthorizationServerOptions.cs index b0d5ee3e..2b8cf513 100644 --- a/src/Keycloak.AuthServices.Authorization/AuthorizationServer/KeycloakAuthorizationServerClientOptions.cs +++ b/src/Keycloak.AuthServices.Authorization/AuthorizationServer/KeycloakAuthorizationServerOptions.cs @@ -5,7 +5,7 @@ namespace Keycloak.AuthServices.Authorization.AuthorizationServer; /// /// Defines a set of options used to perform Authorization Server calls /// -public sealed class KeycloakAuthorizationServerClientOptions : KeycloakInstallationOptions +public sealed class KeycloakAuthorizationServerOptions : KeycloakInstallationOptions { /// /// Default section name. diff --git a/src/Keycloak.AuthServices.Authorization/PoliciesBuilderExtensions.cs b/src/Keycloak.AuthServices.Authorization/PoliciesBuilderExtensions.cs index 10b9c78f..7b3a32fd 100644 --- a/src/Keycloak.AuthServices.Authorization/PoliciesBuilderExtensions.cs +++ b/src/Keycloak.AuthServices.Authorization/PoliciesBuilderExtensions.cs @@ -54,6 +54,7 @@ params string[] roles .RequireClaim(KeycloakConstants.RealmAccessClaimType) .AddRequirements(new RealmAccessRequirement(roles)); + #region RequireProtectedResource /// /// Adds protected resource requirement to builder. Makes outgoing HTTP requests to Authorization Server. /// @@ -66,7 +67,9 @@ public static AuthorizationPolicyBuilder RequireProtectedResource( string resource, string scope ) => builder.AddRequirements(new DecisionRequirement(resource, scope)); + #endregion RequireProtectedResource + #region RequireProtectedResourceScopes /// /// Adds protected resource requirement to builder. Makes outgoing HTTP requests to Authorization Server. /// @@ -87,4 +90,5 @@ public static AuthorizationPolicyBuilder RequireProtectedResource( ScopesValidationMode = scopesValidationMode } ); + #endregion RequireProtectedResourceScopes } diff --git a/src/Keycloak.AuthServices.Authorization/ServiceCollectionExtensions.cs b/src/Keycloak.AuthServices.Authorization/ServiceCollectionExtensions.cs index 9263b9b0..d8fdbe0f 100644 --- a/src/Keycloak.AuthServices.Authorization/ServiceCollectionExtensions.cs +++ b/src/Keycloak.AuthServices.Authorization/ServiceCollectionExtensions.cs @@ -92,7 +92,7 @@ public static IHttpClientBuilder AddAuthorizationServer( this IServiceCollection services, IConfiguration configuration, Action? configureClient = default, - string configSectionName = KeycloakAuthorizationServerClientOptions.Section + string configSectionName = KeycloakAuthorizationServerOptions.Section ) => services.AddAuthorizationServer( configuration.GetSection(configSectionName), @@ -117,7 +117,7 @@ public static IHttpClientBuilder AddAuthorizationServer( ); /// - /// Adds Keycloak client and auto header propagation + /// Adds Keycloak client, and auto header propagation /// /// /// @@ -125,11 +125,10 @@ public static IHttpClientBuilder AddAuthorizationServer( /// public static IHttpClientBuilder AddAuthorizationServer( this IServiceCollection services, - Action configureKeycloakOptions, + Action configureKeycloakOptions, Action? configureClient = default ) { - configureKeycloakOptions ??= _ => { }; services.Configure(configureKeycloakOptions); services.AddHttpContextAccessor(); @@ -137,7 +136,8 @@ public static IHttpClientBuilder AddAuthorizationServer( services.AddSingleton(); // (!) resolved locally, will not work with PostConfigure and IOptions pattern - var keycloakOptions = new KeycloakAuthorizationServerClientOptions(); + configureKeycloakOptions ??= _ => { }; + var keycloakOptions = new KeycloakAuthorizationServerOptions(); configureKeycloakOptions.Invoke(keycloakOptions); if (keycloakOptions.UseProtectedResourcePolicyProvider) @@ -145,20 +145,34 @@ public static IHttpClientBuilder AddAuthorizationServer( services.AddSingleton(); } - return services + return services.AddAuthorizationServerClient(configureClient).AddHeaderPropagation(); + } + + /// + /// Adds Keycloak client + /// + /// + /// + /// + public static IHttpClientBuilder AddAuthorizationServerClient( + this IServiceCollection services, + Action? configureClient = default + ) => + services .AddHttpClient() .ConfigureHttpClient( (serviceProvider, client) => { var keycloakOptions = serviceProvider - .GetRequiredService>() - .Value; + .GetService>() + ?.Value; - client.BaseAddress = new Uri(keycloakOptions.KeycloakUrlRealm); + if (!string.IsNullOrWhiteSpace(keycloakOptions?.KeycloakUrlRealm)) + { + client.BaseAddress = new Uri(keycloakOptions.KeycloakUrlRealm); + } configureClient?.Invoke(client); } - ) - .AddHeaderPropagation(); - } + ); } diff --git a/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs b/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs index a84f1b93..14d7d8a7 100644 --- a/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs +++ b/tests/Keycloak.AuthServices.IntegrationTests/Utils.cs @@ -92,7 +92,7 @@ ITestOutputHelper testOutputHelper return (services, configuration); } - public static (IServiceCollection services, IConfiguration configuration1) AdminHttpClientSetup( + public static (IServiceCollection services, IConfiguration configuration) AdminHttpClientSetup( string fileName, ITestOutputHelper testOutputHelper ) @@ -114,10 +114,10 @@ ITestOutputHelper testOutputHelper return (services, configuration); } - public static (IServiceCollection services, IConfiguration configuration1) ProtectionHttpClientSetup( - string fileName, - ITestOutputHelper testOutputHelper - ) + public static ( + IServiceCollection services, + IConfiguration configuration + ) ProtectionHttpClientSetup(string fileName, ITestOutputHelper testOutputHelper) { var (services, configuration) = KeycloakSetup(fileName, testOutputHelper); @@ -138,22 +138,22 @@ ITestOutputHelper testOutputHelper private static void BindKeycloak( Duende.AccessTokenManagement.ClientCredentialsClient client, - KeycloakAdminClientOptions keycloaktOptions + KeycloakAdminClientOptions keycloakOptions ) { - client.ClientId = keycloaktOptions.Resource; - client.ClientSecret = keycloaktOptions.Credentials.Secret; - client.TokenEndpoint = keycloaktOptions.KeycloakTokenEndpoint; + client.ClientId = keycloakOptions.Resource; + client.ClientSecret = keycloakOptions.Credentials.Secret; + client.TokenEndpoint = keycloakOptions.KeycloakTokenEndpoint; } private static void BindKeycloak( Duende.AccessTokenManagement.ClientCredentialsClient client, - KeycloakProtectionClientOptions keycloaktOptions + KeycloakProtectionClientOptions keycloakOptions ) { - client.ClientId = keycloaktOptions.Resource; - client.ClientSecret = keycloaktOptions.Credentials.Secret; - client.TokenEndpoint = keycloaktOptions.KeycloakTokenEndpoint; + client.ClientId = keycloakOptions.Resource; + client.ClientSecret = keycloakOptions.Credentials.Secret; + client.TokenEndpoint = keycloakOptions.KeycloakTokenEndpoint; } public static KeycloakAuthenticationOptions ReadKeycloakAuthenticationOptions(string fileName)