Skip to content

Commit

Permalink
Refactored the use of ClaimsLite and ClaimsRecord into a single type
Browse files Browse the repository at this point in the history
  • Loading branch information
Erwinvandervalk committed Feb 4, 2025
1 parent 68ef1f8 commit d9b5605
Show file tree
Hide file tree
Showing 17 changed files with 192 additions and 46 deletions.
94 changes: 94 additions & 0 deletions .github/workflows/ignore-this-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# This was generated by tool. Edits will be overwritten.

name: ignore-this/release
on:
workflow_dispatch:
inputs:
version:
description: 'Version in format X.Y.Z or X.Y.Z-preview.'
type: string
required: true
default: '0.0.0'
env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
jobs:
tag:
name: Tag and Pack
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
defaults:
run:
shell: bash
working-directory: ignore-this
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Dotnet
uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25
with:
dotnet-version: |-
6.0.x
8.0.x
9.0.x
- name: Git tag
run: |-
git config --global user.email "[email protected]"
git config --global user.name "Duende Software GitHub Bot"
git tag -a it-${{ github.event.inputs.version }} -m "Release v${{ github.event.inputs.version }}"
git push origin it-${{ github.event.inputs.version }}
- name: Pack IgnoreThis
run: dotnet pack -c Release src/IgnoreThis -o artifacts
- name: Tool restore
run: dotnet tool restore
- name: Sign packages
run: |-
for file in artifacts/*.nupkg; do
dotnet NuGetKeyVaultSignTool sign "$file" --file-digest sha256 --timestamp-rfc3161 http://timestamp.digicert.com --azure-key-vault-url https://duendecodesigninghsm.vault.azure.net/ --azure-key-vault-client-id 18e3de68-2556-4345-8076-a46fad79e474 --azure-key-vault-tenant-id ed3089f0-5401-4758-90eb-066124e2d907 --azure-key-vault-client-secret ${{ secrets.SignClientSecret }} --azure-key-vault-certificate NuGetPackageSigning
done
- name: Push packages to MyGet
if: github.ref == 'refs/heads/main'
run: dotnet nuget push artifacts/*.nupkg --source https://www.myget.org/F/duende_identityserver/api/v2/package --api-key ${{ secrets.MYGET }} --skip-duplicate
- name: Push packages to GitHub
if: github.ref == 'refs/heads/main'
run: dotnet nuget push artifacts/*.nupkg --source https://nuget.pkg.github.com/DuendeSoftware/index.json --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Artifacts
if: github.ref == 'refs/heads/main'
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882
with:
name: artifacts
path: ignore-this/artifacts/*.nupkg
overwrite: true
retention-days: 15
publish:
name: Publish to nuget.org
needs:
- tag
runs-on: ubuntu-latest
environment:
name: nuget.org
steps:
- uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16
with:
name: artifacts
path: artifacts
- name: Setup Dotnet
uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25
with:
dotnet-version: |-
6.0.x
8.0.x
9.0.x
- name: List files
run: tree
shell: bash
- name: Push packages to nuget.org
if: github.ref == 'refs/heads/main'
run: dotnet nuget push artifacts/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_ORG_API_KEY }} --skip-duplicate
2 changes: 1 addition & 1 deletion bff/samples/Apis/Api.DPoP/Api.DPoP.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFrameworks>net9.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion bff/samples/Bff/Bff.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFrameworks>net9.0</TargetFrameworks>
<RootNamespace>Bff</RootNamespace>
<Nullable>enable</Nullable>
</PropertyGroup>
Expand Down
14 changes: 14 additions & 0 deletions bff/samples/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<!-- This line is needed because:
Aspire needs a single target framework
(until this is resolved: https://github.com/dotnet/aspire/issues/2962)
Strangely enough, <TargetFrameworks>net9.0</TargetFrameworks> in the csproj file is not enough.
But, to use a Directory.Packages.props where we check for $(TargetFramework) == 'net9.0',
we need to set the TargetFramework to 'net9.0' here.
-->
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<Import Project="../../samples.props" />
</Project>
2 changes: 1 addition & 1 deletion bff/samples/Hosts.Tests/Hosts.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFrameworks>net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
Expand Down
41 changes: 41 additions & 0 deletions bff/src/Duende.Bff.Blazor.Client/ClaimRecord.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Text.Json.Serialization;

namespace Duende.Bff;

/// <summary>
/// Serialization friendly claim.
///
/// Note, this is a copy of the ClaimRecord class from Duende.Bff, but since we can't create a reference to it, we need to copy it here.
/// We also can't link to it (as we do with the extensions) because we had to make it public.
/// </summary>
internal class ClaimRecord()
{
/// <summary>
/// Serialization friendly claim
/// </summary>
/// <param name="type">The type</param>
/// <param name="value">The Value</param>
internal ClaimRecord(string type, object value) : this()
{
Type = type;
Value = value;
}

/// <summary>
/// The type
/// </summary>
[JsonPropertyName("type")]
public string Type { get; init; } = default!;

/// <summary>
/// The value
/// </summary>
[JsonPropertyName("value")]
public object Value { get; init; } = default!;

/// <summary>
/// The value type
/// </summary>
[JsonPropertyName("valueType")]
public string? ValueType { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
</PropertyGroup>

<ItemGroup>
<Compile Include="..\Duende.Bff\Shared\ClaimLite.cs" Link="Shared\ClaimLite.cs" />
<Compile Include="..\Duende.Bff\Shared\ClaimsLiteExtensions.cs" Link="Shared\ClaimsLiteExtensions.cs" />
<Compile Include="..\Duende.Bff\Shared\ClaimsPrincipalLite.cs" Link="Shared\ClaimsPrincipalLite.cs" />
<Compile Include="..\Duende.Bff\Shared\ClaimRecordExtensions.cs" Link="Shared\ClaimRecordExtensions.cs" />
<Compile Include="..\Duende.Bff\Shared\ClaimsPrincipalRecord.cs" Link="Shared\ClaimsPrincipalRecord.cs" />
</ItemGroup>

<ItemGroup>
Expand Down
3 changes: 0 additions & 3 deletions bff/src/Duende.Bff.Blazor.Client/Internals/GetUserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,6 @@ public async ValueTask<ClaimsPrincipal> GetUserAsync(bool useCache = true)
return _cachedUser;
}

// TODO - Consider using ClaimLite instead here
record ClaimRecord(string Type, object Value);

internal async Task<ClaimsPrincipal> FetchUser()
{
try
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal class PersistentUserService(PersistentComponentState state, ILogger<Per
/// <inheritdoc />
public ClaimsPrincipal GetPersistedUser()
{
if (!state.TryTakeFromJson<ClaimsPrincipalLite>(nameof(ClaimsPrincipalLite), out var lite) || lite is null)
if (!state.TryTakeFromJson<ClaimsPrincipalRecord>(nameof(ClaimsPrincipalRecord), out var lite) || lite is null)
{
logger.LogDebug("Failed to load persisted user.");
return new ClaimsPrincipal(new ClaimsIdentity());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,14 @@ private async Task OnPersistingAsync()
var authenticationState = await _authenticationStateTask;

var claims = authenticationState.User.Claims
.Select(c => new ClaimLite
.Select(c => new ClaimRecord
{
Type = c.Type,
Value = c.Value?.ToString() ?? string.Empty,
ValueType = c.ValueType == ClaimValueTypes.String ? null : c.ValueType
}).ToArray();

var principal = new ClaimsPrincipalLite
var principal = new ClaimsPrincipalRecord
{
AuthenticationType = authenticationState.User.Identity!.AuthenticationType,
NameClaimType = authenticationState.User.Identities.First().NameClaimType,
Expand All @@ -90,7 +90,7 @@ private async Task OnPersistingAsync()

_logger.LogDebug("Persisting Authentication State");

_state.PersistAsJson(nameof(ClaimsPrincipalLite), principal);
_state.PersistAsJson(nameof(ClaimsPrincipalRecord), principal);
}


Expand Down
16 changes: 1 addition & 15 deletions bff/src/Duende.Bff/EndpointServices/User/DefaultUserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
Expand Down Expand Up @@ -134,20 +135,5 @@ protected virtual Task<IEnumerable<ClaimRecord>> GetManagementClaimsAsync(HttpCo

return Task.FromResult((IEnumerable<ClaimRecord>)claims);
}

/// <summary>
/// Serialization-friendly claim
/// </summary>
/// <param name="Type"></param>
/// <param name="Value"></param>
protected record ClaimRecord(string Type, object Value)
{
/// <summary></summary>
[JsonPropertyName("type")]
public string Type { get; init; } = Type;

/// <summary></summary>
[JsonPropertyName("value")]
public object Value { get; init; } = Value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ internal class AuthenticationTicketLite
/// <summary>
/// The user
/// </summary>
public ClaimsPrincipalLite User { get; set; } = default!;
public ClaimsPrincipalRecord User { get; set; } = default!;

/// <summary>
/// The items
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,41 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.

using System.Text.Json.Serialization;

namespace Duende.Bff;

/// <summary>
/// Serialization friendly claim
/// </summary>
internal class ClaimLite
public class ClaimRecord()
{
/// <summary>
///
/// </summary>
/// <param name="type"></param>
/// <param name="value"></param>
public ClaimRecord(string type, object value) : this()
{
Type = type;
Value = value;
}

/// <summary>
/// The type
/// </summary>
[JsonPropertyName("type")]
public string Type { get; init; } = default!;

/// <summary>
/// The value
/// </summary>
public string Value { get; init; } = default!;
[JsonPropertyName("value")]
public object Value { get; init; } = default!;

/// <summary>
/// The value type
/// </summary>
[JsonPropertyName("valueType")]
public string? ValueType { get; init; }
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.

using System.Linq;
using System.Security.Claims;

namespace Duende.Bff;

internal static class ClaimsLiteExtensions
internal static class ClaimRecordExtensions
{
/// <summary>
/// Converts a ClaimsPrincipalLite to ClaimsPrincipal
/// </summary>
public static ClaimsPrincipal ToClaimsPrincipal(this ClaimsPrincipalLite principal)
public static ClaimsPrincipal ToClaimsPrincipal(this ClaimsPrincipalRecord principal)
{
var claims = principal.Claims.Select(x => new Claim(x.Type, x.Value, x.ValueType ?? ClaimValueTypes.String))
var claims = principal.Claims.Select(x => new Claim(x.Type, x.Value.ToString() ?? string.Empty, x.ValueType ?? ClaimValueTypes.String))
.ToArray();
var id = new ClaimsIdentity(claims, principal.AuthenticationType, principal.NameClaimType,
principal.RoleClaimType);
Expand All @@ -24,17 +23,17 @@ public static ClaimsPrincipal ToClaimsPrincipal(this ClaimsPrincipalLite princip
/// <summary>
/// Converts a ClaimsPrincipal to ClaimsPrincipalLite
/// </summary>
public static ClaimsPrincipalLite ToClaimsPrincipalLite(this ClaimsPrincipal principal)
public static ClaimsPrincipalRecord ToClaimsPrincipalLite(this ClaimsPrincipal principal)
{
var claims = principal.Claims.Select(
x => new ClaimLite
x => new ClaimRecord
{
Type = x.Type,
Value = x.Value,
ValueType = x.ValueType == ClaimValueTypes.String ? null : x.ValueType
}).ToArray();

return new ClaimsPrincipalLite
return new ClaimsPrincipalRecord
{
AuthenticationType = principal.Identity!.AuthenticationType,
NameClaimType = principal.Identities.First().NameClaimType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Duende.Bff;
/// <summary>
/// Serialization friendly ClaimsPrincipal
/// </summary>
internal class ClaimsPrincipalLite
internal class ClaimsPrincipalRecord
{
/// <summary>
/// The authentication type
Expand All @@ -26,5 +26,5 @@ internal class ClaimsPrincipalLite
/// <summary>
/// The claims
/// </summary>
public ClaimLite[] Claims { get; init; } = default!;
public ClaimRecord[] Claims { get; init; } = default!;
}
2 changes: 1 addition & 1 deletion bff/test/Duende.Bff.Tests/TestHosts/ApiHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ private void Configure(IApplicationBuilder app)
Path: context.Request.Path.Value ?? "/",
Sub: context.User.FindFirst("sub")?.Value,
ClientId: context.User.FindFirst("client_id")?.Value,
Claims: context.User.Claims.Select(x => new ClaimRecord(x.Type, x.Value)).ToArray())
Claims: context.User.Claims.Select(x => new TestFramework.ClaimRecord(x.Type, x.Value)).ToArray())
{
Body = body,
RequestHeaders = requestHeaders
Expand Down
Loading

0 comments on commit d9b5605

Please sign in to comment.