Skip to content

Commit

Permalink
feat: Add IKeycloakPolicyClient (#84)
Browse files Browse the repository at this point in the history
  • Loading branch information
NikiforovAll authored May 4, 2024
1 parent 117af72 commit cf22078
Show file tree
Hide file tree
Showing 33 changed files with 1,421 additions and 380 deletions.
2 changes: 2 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ dotnet_diagnostic.IDE0045.severity = suggestion
dotnet_style_prefer_conditional_expression_over_return = false:suggestion
dotnet_diagnostic.IDE0046.severity = suggestion
dotnet_style_prefer_compound_assignment = true:warning

dotnet_diagnostic.CA1848.severity = suggestion
# Null-checking preferences
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#null-checking-preferences
dotnet_style_coalesce_expression = true:warning
Expand Down
4 changes: 4 additions & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ export default withMermaid({
{ text: 'Introduction', link: '/protection-api/protection-api' },
{
text: 'Protection API Reference', link: '/protection-api/protection-api-reference',
items: [
{ text: 'Protected Resource Client', link: '/protection-api/protected-resource-client' },
{ text: 'Policy Client', link: '/protection-api/policy-client' },
]
},
]
},
Expand Down
3 changes: 3 additions & 0 deletions docs/protection-api/policy-client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# IKeycloakPolicyClient

<<< @/../src/Keycloak.AuthServices.Sdk/Protection/IKeycloakPolicyClient.cs
3 changes: 3 additions & 0 deletions docs/protection-api/protected-resource-client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# IKeycloakProtectedResourceClient

<<< @/../src/Keycloak.AuthServices.Sdk/Protection/IKeycloakProtectedResourceClient.cs
5 changes: 4 additions & 1 deletion samples/AuthGettingStarted/ApplicationLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ public static class ApplicationLogger
{
public static void ConfigureLogger(this ConfigureHostBuilder host)
{
#pragma warning disable CA1305 // Specify IFormatProvider
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo.Console(
outputTemplate: "[{Level:u4}] |{SourceContext,30}({EventId})| {Message:lj}{NewLine}{Exception}",
restrictedToMinimumLevel: LogEventLevel.Debug)
restrictedToMinimumLevel: LogEventLevel.Debug
)
.CreateBootstrapLogger();
#pragma warning restore CA1305 // Specify IFormatProvider

host.UseSerilog();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ IConfiguration configuration
Implicit = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri(
$"{options.KeycloakUrlRealm}/protocol/openid-connect/auth"
$"{options!.KeycloakUrlRealm}/protocol/openid-connect/auth"
),
TokenUrl = new Uri(
$"{options.KeycloakUrlRealm}/protocol/openid-connect/token"
Expand All @@ -55,7 +55,7 @@ IConfiguration configuration
{
KeycloakAuthenticationOptions options = new();

configuration.BindKeycloakOptions<KeycloakAuthenticationOptions>(options);
configuration.BindKeycloakOptions(options);

app.UseSwagger();
app.UseSwaggerUI(s => s.OAuthClientId(options.Resource));
Expand Down
2 changes: 1 addition & 1 deletion samples/Blazor/Client/Pages/FetchData.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@page "/fetchdata"
@using Blazor.Shared
@using BlazorSample.Common

@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
Expand Down
1 change: 0 additions & 1 deletion samples/Blazor/Server/Blazor.Server.csproj
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" />
<PackageReference Include="Serilog.AspNetCore" />
Expand Down
41 changes: 22 additions & 19 deletions samples/Blazor/Server/Controllers/WeatherForecastController.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace Blazor.Server.Controllers;
using Blazor.Shared;

using BlazorSample.Common;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

Expand All @@ -10,25 +11,27 @@ public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
"Freezing",
"Bracing",
"Chilly",
"Cool",
"Mild",
"Warm",
"Balmy",
"Hot",
"Sweltering",
"Scorching"
};

private readonly ILogger<WeatherForecastController> _logger;

public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}

[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
public IEnumerable<WeatherForecast> Get() =>
Enumerable
.Range(1, 5)
.Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
2 changes: 2 additions & 0 deletions samples/Blazor/Shared/Blazor.Shared.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

<PropertyGroup>
<IsPackable>false</IsPackable>
<RootNamespace>BlazorSample</RootNamespace>
<NoWarn>CS7022</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions samples/Blazor/Shared/WeatherForecast.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Blazor.Shared;
namespace BlazorSample.Common;

public class WeatherForecast
{
Expand All @@ -8,5 +8,5 @@ public class WeatherForecast

public string? Summary { get; set; }

public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public int TemperatureF => 32 + (int)(this.TemperatureC / 0.5556);
}
2 changes: 1 addition & 1 deletion src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ async Task<UserRepresentation> GetUserAsync(
return await response.GetResponseAsync<UserRepresentation>(cancellationToken) ?? new();
}

// <summary>
/// <summary>
/// Get representation of a user.
/// </summary>
/// <param name="realm">Realm name (not ID).</param>
Expand Down
24 changes: 14 additions & 10 deletions src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
namespace Keycloak.AuthServices.Sdk.Admin;

using System.Globalization;
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;

/// <summary>
/// TBD:
/// Represents a client for interacting with the Keycloak Admin API.
/// </summary>
public partial class KeycloakClient : IKeycloakClient
{
private readonly HttpClient httpClient;

/// <summary>
/// TBD:
/// Initializes a new instance of the <see cref="KeycloakClient"/> class.
/// </summary>
/// <param name="httpClient"></param>
public KeycloakClient(HttpClient httpClient) => this.httpClient = httpClient;
Expand Down Expand Up @@ -204,7 +205,7 @@ public async Task<HttpResponseMessage> ExecuteActionsEmailWithResponseAsync(

if (request.Lifespan.HasValue)
{
queryBuilder.Add("lifespan", request.Lifespan?.ToString()!);
queryBuilder.Add("lifespan", request.Lifespan?.ToString(CultureInfo.InvariantCulture)!);
}

var url = path + queryBuilder.ToQueryString();
Expand Down Expand Up @@ -233,17 +234,17 @@ public async Task<HttpResponseMessage> GetUserGroupsWithResponseAsync(
parameters ??= new();
if (parameters.BriefRepresentation.HasValue)
{
queryBuilder.Add("briefRepresentation", parameters.BriefRepresentation.ToString());
queryBuilder.Add("briefRepresentation", parameters.BriefRepresentation?.ToString()!);
}

if (parameters.First.HasValue)
{
queryBuilder.Add("first", parameters.First.ToString());
queryBuilder.Add("first", parameters.First?.ToString(CultureInfo.InvariantCulture)!);
}

if (parameters.Max.HasValue)
{
queryBuilder.Add("max", parameters.Max.ToString()!);
queryBuilder.Add("max", parameters.Max?.ToString(CultureInfo.InvariantCulture)!);
}

var url = path + queryBuilder.ToQueryString();
Expand Down Expand Up @@ -310,22 +311,25 @@ public async Task<HttpResponseMessage> GetGroupsWithResponseAsync(
parameters ??= new();
if (parameters.BriefRepresentation.HasValue)
{
queryBuilder.Add("briefRepresentation", parameters.BriefRepresentation.ToString());
queryBuilder.Add(
"briefRepresentation",
parameters.BriefRepresentation?.ToString(CultureInfo.InvariantCulture)!
);
}

if (parameters.First.HasValue)
{
queryBuilder.Add("first", parameters.First.ToString());
queryBuilder.Add("first", parameters.First?.ToString(CultureInfo.InvariantCulture)!);
}

if (parameters.Exact.HasValue)
{
queryBuilder.Add("exact", parameters.Exact.ToString());
queryBuilder.Add("exact", parameters.Exact?.ToString(CultureInfo.InvariantCulture)!);
}

if (parameters.Max.HasValue)
{
queryBuilder.Add("max", parameters.Max.ToString()!);
queryBuilder.Add("max", parameters.Max?.ToString(CultureInfo.InvariantCulture)!);
}

if (parameters.Search is not null)
Expand Down
32 changes: 0 additions & 32 deletions src/Keycloak.AuthServices.Sdk/Admin/Models/Contracts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8058,38 +8058,6 @@ public void Dispose()
}
}

[System.CodeDom.Compiler.GeneratedCode(
"NSwag",
"13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))"
)]
public partial class KeycloakHttpClientException : System.Exception
{
public int StatusCode { get; private set; }

public string HttpResponse { get; private set; }

public ErrorResponse Response { get; private set; }

public KeycloakHttpClientException(
string message,
int statusCode,
string httpResponse,
ErrorResponse response,
System.Exception? innerException
)
: base(message + "\n\nStatus: " + statusCode, innerException)
{
StatusCode = statusCode;
HttpResponse = httpResponse;
Response = response;
}

public override string ToString()
{
return string.Format("HTTP Response: \n\n{0}\n\n{1}", HttpResponse, base.ToString());
}
}

#pragma warning restore 108
#pragma warning restore 114
#pragma warning restore 472
Expand Down
6 changes: 3 additions & 3 deletions src/Keycloak.AuthServices.Sdk/ErrorResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ namespace Keycloak.AuthServices.Sdk;
using System.Text.Json.Serialization;

/// <summary>
/// TBD:
/// Represents an error response from Keycloak.
/// </summary>
public sealed record ErrorResponse
{
/// <summary>
/// TBD:
/// Gets or sets the error code.
/// </summary>
[JsonPropertyName("error")]
public string Error { get; init; } = default!;

/// <summary>
/// TBD:
/// Gets or sets the error description.
/// </summary>
[JsonPropertyName("error_description")]
public string ErrorDescription { get; init; } = default!;
Expand Down
22 changes: 10 additions & 12 deletions src/Keycloak.AuthServices.Sdk/HttpResponseExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@ namespace Keycloak.AuthServices.Sdk;

using System.Net.Http.Json;
using System.Text.Json;
using Keycloak.AuthServices.Sdk.Admin.Models;

/// <summary>
/// TBD:
/// Provides extension methods for handling HTTP responses.
/// </summary>
public static class HttpResponseExtensions
{
/// <summary>
/// TBD:
/// Reads the HTTP response content as JSON and deserializes it into the specified type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="response"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <typeparam name="T">The type to deserialize the response content into.</typeparam>
/// <param name="response">The HTTP response message.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The deserialized response content.</returns>
public static async Task<T?> GetResponseAsync<T>(
this HttpResponseMessage response,
CancellationToken cancellationToken = default
Expand All @@ -31,12 +30,11 @@ public static class HttpResponseExtensions
}

/// <summary>
/// TBD:
/// Ensures that the HTTP response is successful, otherwise throws an exception.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="response"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <param name="response">The HTTP response message.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <exception cref="KeycloakHttpClientException">Thrown when the response is not successful.</exception>
public static async Task EnsureResponseAsync(
this HttpResponseMessage response,
CancellationToken cancellationToken = default
Expand Down
Loading

0 comments on commit cf22078

Please sign in to comment.