Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor!: Change organization of AuthServices.Common #63

Merged
merged 4 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
- name: "Install .NET SDK"
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.100
dotnet-version: 8.0.204
- name: "Dotnet Tool Restore"
run: dotnet tool restore
shell: pwsh
Expand Down
9 changes: 5 additions & 4 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ jobs:
strategy:
fail-fast: false
matrix:
language: ["csharp"]
language: ['csharp']

# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
Expand All @@ -38,7 +39,7 @@ jobs:

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
Expand All @@ -49,7 +50,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
uses: github/codeql-action/autobuild@v3

# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
Expand All @@ -63,4 +64,4 @@ jobs:
# make release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v3
12 changes: 12 additions & 0 deletions docs/design-principles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Design V2

## Goals

### Minimum dependencies

Currently, we use Refit for client generation, but it is completely unnecessary because it add dependency on `Refit` and therefore the clients are taking this dependency also. Retrospectively, it was bad decision

### Composable and Open For Extension

* Composable API that allows to configuration Keycloak integration for Web API scenarios.
* It should be possible to configure pretty much every aspect of Keycloak integration, meaning we should avoid static "variables and sealed settings"
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Index
Empty file added docs/introduction.md
Empty file.
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "6.0.100",
"version": "8.0.204",
"rollForward": "latestMajor",
"allowPrerelease": false
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Keycloak.AuthServices.Authorization.Requirements;

using Common;
using Keycloak.AuthServices.Common.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Logging;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Keycloak.AuthServices.Authorization.Requirements;

using Common;
using Keycloak.AuthServices.Common.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Logging;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
namespace Keycloak.AuthServices.Common.Claims;

using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Security.Claims;
using System.Text.Json;
using static Keycloak.AuthServices.Common.KeycloakConstants;

/// <summary>
/// Keycloak resource claims extensions.
/// </summary>
public static class KeycloakClaimsExtensions
{
public const string JsonClaimValueType = "JSON";

Check warning on line 14 in src/Keycloak.AuthServices.Common/Claims/KeycloakResourceClaimsExtensions.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Missing XML comment for publicly visible type or member 'KeycloakClaimsExtensions.JsonClaimValueType'

Check warning on line 14 in src/Keycloak.AuthServices.Common/Claims/KeycloakResourceClaimsExtensions.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Missing XML comment for publicly visible type or member 'KeycloakClaimsExtensions.JsonClaimValueType'

Check warning on line 14 in src/Keycloak.AuthServices.Common/Claims/KeycloakResourceClaimsExtensions.cs

View workflow job for this annotation

GitHub Actions / Build-macOS-latest

Missing XML comment for publicly visible type or member 'KeycloakClaimsExtensions.JsonClaimValueType'

Check warning on line 14 in src/Keycloak.AuthServices.Common/Claims/KeycloakResourceClaimsExtensions.cs

View workflow job for this annotation

GitHub Actions / Build-macOS-latest

Missing XML comment for publicly visible type or member 'KeycloakClaimsExtensions.JsonClaimValueType'

Check warning on line 14 in src/Keycloak.AuthServices.Common/Claims/KeycloakResourceClaimsExtensions.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Missing XML comment for publicly visible type or member 'KeycloakClaimsExtensions.JsonClaimValueType'

Check warning on line 14 in src/Keycloak.AuthServices.Common/Claims/KeycloakResourceClaimsExtensions.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Missing XML comment for publicly visible type or member 'KeycloakClaimsExtensions.JsonClaimValueType'

/// <summary>
/// Tries to get the value of a specific claim from the JWT claims.
/// </summary>
/// <typeparam name="T">The type of the claim value.</typeparam>
/// <param name="claims">The JWT claims.</param>
/// <param name="claimType">The claim type.</param>
/// <param name="claimValueType">The claim value type.</param>
/// <param name="result">The claim value if found, otherwise the default value of type T.</param>
/// <returns>True if the claim value was found, false otherwise.</returns>
public static bool TryGetClaimValue<T>(
this IEnumerable<Claim> claims,
string claimType,
string? claimValueType,
[MaybeNullWhen(false)] out T result
)
{
var claim = claims.SingleOrDefault(x =>
x.Type.Equals(claimType, StringComparison.OrdinalIgnoreCase)
&& x.ValueType.Equals(claimValueType, StringComparison.OrdinalIgnoreCase)
);

if (claim == null || string.IsNullOrEmpty(claim.Value))
{
result = default;

return false;
}

if (typeof(T).IsPrimitive || typeof(T) == typeof(string))
{
result = (T)Convert.ChangeType(claim.Value, typeof(T), CultureInfo.InvariantCulture);
}
else
{
result = JsonSerializer.Deserialize<T>(claim.Value)!;
}

return true;
}

/// <summary>
/// Tries to get the resource collection from the JWT claims.
/// </summary>
/// <param name="claims">The JWT claims.</param>
/// <param name="resourceAccessCollection">The resource access collection if found, otherwise null.</param>
/// <returns>True if the resource collection was found, false otherwise.</returns>
public static bool TryGetResourceCollection(
this IEnumerable<Claim> claims,
[MaybeNullWhen(false)] out ResourceAccessCollection resourceAccessCollection
) =>
claims.TryGetClaimValue(
ResourceAccessClaimType,
JsonClaimValueType,
out resourceAccessCollection
);

/// <summary>
/// Tries to get the realm resource from the JWT claims.
/// </summary>
/// <param name="claims">The JWT claims.</param>
/// <param name="resourcesAccess">The realm resource access if found, otherwise null.</param>
/// <returns>True if the realm resource was found, false otherwise.</returns>
public static bool TryGetRealmResource(
this IEnumerable<Claim> claims,
[MaybeNullWhen(false)] out ResourceAccess resourcesAccess
) => claims.TryGetClaimValue(RealmAccessClaimType, JsonClaimValueType, out resourcesAccess);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Keycloak.AuthServices.Common;
namespace Keycloak.AuthServices.Common.Claims;

using System.Text.Json.Serialization;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,8 @@ private static string NormalizeUrl(string url)
/// <summary>
/// Default binder required to handle kebab-case configuration format used by Keycloak
/// </summary>
#pragma warning disable CA2211 // Non-constant fields should not be visible
public static Action<BinderOptions> KeycloakFormatBinder = options =>
public static Action<BinderOptions> KeycloakFormatBinder => options =>
options.BindNonPublicProperties = true;
#pragma warning restore CA2211 // Non-constant fields should not be visible
}

/// <summary>
Expand Down

This file was deleted.

2 changes: 2 additions & 0 deletions tests/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[*.cs]
dotnet_diagnostic.CA1707.severity = none
3 changes: 2 additions & 1 deletion tests/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
Expand All @@ -24,6 +24,7 @@

<ItemGroup Label="Global Usings">
<Using Include="Xunit" />
<Using Include="FluentAssertions" />
</ItemGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
namespace Keycloak.AuthServices.Common.Tests;

using System.Security.Claims;
using System.Text.Json;
using Keycloak.AuthServices.Common.Claims;

public class KeycloakClaimsExtensionsTests
{
[Fact]
public void TryGetClaimValue_RetrieveStringValue_Success()
{
// Arrange
List<Claim> claims = [new("claimType", "claimValue", ClaimValueTypes.String)];

// Act
var result = claims.TryGetClaimValue<string>(
"claimType",
ClaimValueTypes.String,
out var claimValue
);

// Assert
result.Should().BeTrue();
claimValue.Should().Be("claimValue");
}

[Fact]
public void TryGetClaimValue_RetrieveIntValue_Success()
{
// Arrange
List<Claim> claims = [new Claim("claimType", "10", ClaimValueTypes.Integer)];

// Act
var result = claims.TryGetClaimValue<int>(
"claimType",
ClaimValueTypes.Integer,
out var claimValue
);

// Assert
result.Should().BeTrue();
claimValue.Should().Be(10);
}

[Fact]
public void TryGetResourceCollection_RetrieveResourceCollection_Success()
{
// Arrange
var jsonResourceAccessCollection = JsonSerializer.Serialize(new ResourceAccessCollection());
List<Claim> claims =
[
new Claim(
KeycloakConstants.ResourceAccessClaimType,
jsonResourceAccessCollection,
KeycloakClaimsExtensions.JsonClaimValueType
)
];

// Act
var result = claims.TryGetResourceCollection(out var retrievedResourceAccessCollection);

// Assert
result.Should().BeTrue();
retrievedResourceAccessCollection.Should().NotBeNull();
}

[Fact]
public void TryGetRealmResource_RetrieveRealmResource_Success()
{
// Arrange
var jsonResourceAccess = JsonSerializer.Serialize(new ResourceAccess());
List<Claim> claims =
[
new Claim(
KeycloakConstants.RealmAccessClaimType,
jsonResourceAccess,
KeycloakClaimsExtensions.JsonClaimValueType
)
];

// Act
var result = claims.TryGetRealmResource(out var retrievedResourceAccess);

// Assert
result.Should().BeTrue();
retrievedResourceAccess.Should().NotBeNull();
}

[Fact]
public void TryGetRealmResource_NoRealmResource_Failure()
{
// Arrange
List<Claim> claims = [];

// Act
var result = claims.TryGetRealmResource(out var retrievedResourceAccess);

// Assert
result.Should().BeFalse();
retrievedResourceAccess.Should().BeNull();
}

[Fact]
public void TryGetResourceCollection_NoResourceCollection_Failure()
{
// Arrange
List<Claim> claims = [];

// Act
var result = claims.TryGetResourceCollection(out var retrievedResourceAccessCollection);

// Assert
result.Should().BeFalse();
retrievedResourceAccessCollection.Should().BeNull();
}
}
Loading
Loading