Merge pull request 'Happy flow for creating realms works' (#1) from minimal-realm-create into main
Reviewed-on: #1
This commit is contained in:
commit
09480eb1e4
25 changed files with 437 additions and 12 deletions
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.2" />
|
||||
<PackageReference Include="Serilog" Version="4.3.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
|
||||
<PackageReference Include="Serilog.Expressions" Version="5.0.0" />
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using IdentityShroud.Api;
|
||||
using IdentityShroud.Core;
|
||||
using IdentityShroud.Core.Contracts;
|
||||
using IdentityShroud.Core.Security;
|
||||
using Serilog;
|
||||
using Serilog.Formatting.Json;
|
||||
|
||||
|
|
@ -30,6 +32,7 @@ void ConfigureBuilder(WebApplicationBuilder builder)
|
|||
services.AddOpenApi();
|
||||
services.AddScoped<Db>();
|
||||
services.AddOptions<DbConfiguration>().Bind(configuration.GetSection("db"));
|
||||
services.AddSingleton<ISecretProvider, ConfigurationSecretProvider>();
|
||||
|
||||
builder.Host.UseSerilog((context, services, configuration) => configuration
|
||||
.Enrich.FromLogContext()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
using IdentityShroud.Core.Messages;
|
||||
using IdentityShroud.Core.Messages.Realm;
|
||||
using IdentityShroud.Core.Services;
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace IdentityShroud.Api;
|
||||
|
||||
|
|
@ -10,6 +13,9 @@ public static class RealmController
|
|||
var realm = app.MapGroup("/realms/{slug}");
|
||||
|
||||
realm.MapGet("", GetRoot);
|
||||
realm.MapPost("", (RealmCreateRequest request, [FromServices] RealmService service) =>
|
||||
service.Create(request))
|
||||
.WithName("Create Realm");
|
||||
realm.MapGet(".well-known/openid-configuration", GetOpenIdConfiguration);
|
||||
|
||||
var openidConnect = realm.MapGroup("openid-connect");
|
||||
|
|
|
|||
65
IdentityShroud.Core.Tests/Asserts/ResultAssert.cs
Normal file
65
IdentityShroud.Core.Tests/Asserts/ResultAssert.cs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
using FluentResults;
|
||||
|
||||
namespace IdentityShroud.Core.Tests;
|
||||
|
||||
public static class ResultAssert
|
||||
{
|
||||
public static void Success(Result result)
|
||||
{
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
var errors = string.Join("\n", result.Errors.Select(e => "\t" + e.Message));
|
||||
Assert.True(result.IsSuccess, $"ResultAssert.Success: failed, got errors:\n{errors}");
|
||||
}
|
||||
}
|
||||
|
||||
public static T Success<T>(Result<T> result)
|
||||
{
|
||||
Success(result.ToResult());
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
public static void Failed(Result result, Predicate<IError>? filter = null)
|
||||
{
|
||||
if (!result.IsFailed)
|
||||
{
|
||||
Assert.Fail("ResultAssert.Failed: failed, unexpected success result");
|
||||
}
|
||||
|
||||
if (filter is not null)
|
||||
Assert.Contains(result.Errors, filter);
|
||||
}
|
||||
|
||||
public static void Failed<T>(Result<T> result, Predicate<IError>? filter = null)
|
||||
{
|
||||
Failed(result.ToResult(), filter);
|
||||
}
|
||||
|
||||
public static void FailedWith<TError>(Result result) where TError : IError
|
||||
{
|
||||
if (!result.IsFailed)
|
||||
{
|
||||
Assert.Fail("ResultAssert.Failed: failed, unexpected success result");
|
||||
}
|
||||
|
||||
if (!result.Errors.Any(e => e is TError))
|
||||
{
|
||||
string typeName = typeof(TError).Name;
|
||||
Assert.Fail($"ResultAssert.Failed: failed, no error of the type {typeName} found");
|
||||
}
|
||||
}
|
||||
|
||||
public static void FailedWith<T, TError>(Result<T> result) where TError : IError
|
||||
{
|
||||
if (!result.IsFailed)
|
||||
{
|
||||
Assert.Fail("ResultAssert.Failed: failed, unexpected success result");
|
||||
}
|
||||
|
||||
if (!result.Errors.Any(e => e is TError))
|
||||
{
|
||||
string typeName = typeof(TError).Name;
|
||||
Assert.Fail($"ResultAssert.Failed: failed, no error of the type {typeName} found");
|
||||
}
|
||||
}
|
||||
}
|
||||
70
IdentityShroud.Core.Tests/Fixtures/DbFixture.cs
Normal file
70
IdentityShroud.Core.Tests/Fixtures/DbFixture.cs
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
using DotNet.Testcontainers.Containers;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Npgsql;
|
||||
using Testcontainers.PostgreSql;
|
||||
|
||||
namespace IdentityShroud.Core.Tests.Fixtures;
|
||||
|
||||
public class DbFixture : IAsyncLifetime
|
||||
{
|
||||
private readonly IContainer _postgresqlServer;
|
||||
|
||||
private string ConnectionString =>
|
||||
$"Host={_postgresqlServer.Hostname};" +
|
||||
$"Port={DbPort};" +
|
||||
$"Username={Username};Password={Password}";
|
||||
|
||||
private string Username => "postgres";
|
||||
private string Password => "password";
|
||||
private string DbHostname => _postgresqlServer.Hostname;
|
||||
private int DbPort => _postgresqlServer.GetMappedPublicPort(PostgreSqlBuilder.PostgreSqlPort);
|
||||
|
||||
public Db CreateDbContext(string dbName)
|
||||
{
|
||||
var db = new Db(Options.Create<DbConfiguration>(new()
|
||||
{
|
||||
ConnectionString = ConnectionString + ";Database=" + dbName,
|
||||
LogSensitiveData = false,
|
||||
}), new NullLoggerFactory());
|
||||
return db;
|
||||
}
|
||||
|
||||
public DbFixture()
|
||||
{
|
||||
_postgresqlServer = new PostgreSqlBuilder("postgres:18.1")
|
||||
.WithName("KMS-Test-Infra-" + Guid.NewGuid().ToString("D"))
|
||||
.WithPassword(Password)
|
||||
.Build();
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await _postgresqlServer.StopAsync();
|
||||
}
|
||||
|
||||
public async ValueTask InitializeAsync()
|
||||
{
|
||||
await _postgresqlServer.StartAsync();
|
||||
}
|
||||
|
||||
public NpgsqlConnection GetConnection(string dbname)
|
||||
{
|
||||
string connString = ConnectionString
|
||||
+ $";Database={dbname}";
|
||||
var connection = new NpgsqlConnection(connString);
|
||||
connection.Open();
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
[CollectionDefinition("PostgresqlFixtureCollection", DisableParallelization = false)]
|
||||
public class PostgresqlFactoryCollection : ICollectionFixture<PostgresqlFixture>
|
||||
{
|
||||
// This class has no code, and is never created. Its purpose is simply
|
||||
// to be the place to apply [CollectionDefinition] and all the
|
||||
// ICollectionFixture<> interfaces.
|
||||
}
|
||||
*/
|
||||
|
|
@ -12,12 +12,16 @@
|
|||
<PackageReference Include="jose-jwt" Version="5.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="10.0.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"/>
|
||||
<PackageReference Include="xunit" Version="2.9.3"/>
|
||||
<PackageReference Include="NSubstitute" Version="5.3.0" />
|
||||
<PackageReference Include="Testcontainers" Version="4.10.0" />
|
||||
<PackageReference Include="Testcontainers.PostgreSql" Version="4.10.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4"/>
|
||||
<PackageReference Include="xunit.v3" Version="3.2.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit"/>
|
||||
<Using Include="NSubstitute"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=asserts/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
51
IdentityShroud.Core.Tests/Model/RealmTests.cs
Normal file
51
IdentityShroud.Core.Tests/Model/RealmTests.cs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
using IdentityShroud.Core.Contracts;
|
||||
using IdentityShroud.Core.Model;
|
||||
|
||||
namespace IdentityShroud.Core.Tests.Model;
|
||||
|
||||
public class RealmTests
|
||||
{
|
||||
[Fact]
|
||||
public void SetNewKey()
|
||||
{
|
||||
byte[] privateKey = [5, 6, 7, 8];
|
||||
byte[] encryptedPrivateKey = [1, 2, 3, 4];
|
||||
|
||||
var encryptionService = Substitute.For<IEncryptionService>();
|
||||
encryptionService
|
||||
.Encrypt(Arg.Any<byte[]>())
|
||||
.Returns(x => encryptedPrivateKey);
|
||||
|
||||
Realm realm = new();
|
||||
realm.SetPrivateKey(encryptionService, privateKey);
|
||||
|
||||
// should be able to return original without calling decrypt
|
||||
Assert.Equal(privateKey, realm.GetPrivateKey(encryptionService));
|
||||
Assert.Equal(encryptedPrivateKey, realm.PrivateKeyEncrypted);
|
||||
|
||||
encryptionService.Received(1).Encrypt(privateKey);
|
||||
encryptionService.DidNotReceive().Decrypt(Arg.Any<byte[]>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDecryptedKey()
|
||||
{
|
||||
byte[] privateKey = [5, 6, 7, 8];
|
||||
byte[] encryptedPrivateKey = [1, 2, 3, 4];
|
||||
|
||||
var encryptionService = Substitute.For<IEncryptionService>();
|
||||
encryptionService
|
||||
.Decrypt(encryptedPrivateKey)
|
||||
.Returns(x => privateKey);
|
||||
|
||||
Realm realm = new();
|
||||
realm.PrivateKeyEncrypted = encryptedPrivateKey;
|
||||
|
||||
// should be able to return original without calling decrypt
|
||||
Assert.Equal(privateKey, realm.GetPrivateKey(encryptionService));
|
||||
Assert.Equal(encryptedPrivateKey, realm.PrivateKeyEncrypted);
|
||||
|
||||
encryptionService.Received(1).Decrypt(encryptedPrivateKey);
|
||||
}
|
||||
|
||||
}
|
||||
22
IdentityShroud.Core.Tests/Services/EncryptionServiceTests.cs
Normal file
22
IdentityShroud.Core.Tests/Services/EncryptionServiceTests.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
using System.Security.Cryptography;
|
||||
using IdentityShroud.Core.Services;
|
||||
|
||||
namespace IdentityShroud.Core.Tests.Services;
|
||||
|
||||
public class EncryptionServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public void RoundtripWorks()
|
||||
{
|
||||
// setup
|
||||
string key = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));
|
||||
EncryptionService sut = new(key);
|
||||
byte[] input = RandomNumberGenerator.GetBytes(16);
|
||||
|
||||
// act
|
||||
var cipher = sut.Encrypt(input);
|
||||
var result = sut.Decrypt(cipher);
|
||||
|
||||
Assert.Equal(input, result);
|
||||
}
|
||||
}
|
||||
52
IdentityShroud.Core.Tests/Services/RealmServiceTests.cs
Normal file
52
IdentityShroud.Core.Tests/Services/RealmServiceTests.cs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
using FluentResults;
|
||||
using IdentityShroud.Core.Services;
|
||||
using IdentityShroud.Core.Tests.Fixtures;
|
||||
using IdentityShroud.Core.Tests.Substitutes;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace IdentityShroud.Core.Tests.Services;
|
||||
|
||||
public class RealmServiceTests : IClassFixture<DbFixture>
|
||||
{
|
||||
private readonly Db _db;
|
||||
|
||||
public RealmServiceTests(DbFixture dbFixture)
|
||||
{
|
||||
_db = dbFixture.CreateDbContext("realmservice");
|
||||
|
||||
if (!_db.Database.EnsureCreated())
|
||||
TruncateTables();
|
||||
}
|
||||
|
||||
private void TruncateTables()
|
||||
{
|
||||
_db.Database.ExecuteSqlRaw("TRUNCATE realm CASCADE;");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("a7c2a39c-3ed9-4790-826e-43bb2e5e480c")]
|
||||
public async Task Create(string? idString)
|
||||
{
|
||||
Guid? realmId = null;
|
||||
if (idString is not null)
|
||||
realmId = new(idString);
|
||||
|
||||
var encryptionService = EncryptionServiceSubstitute.CreatePassthrough();
|
||||
RealmService sut = new(_db, encryptionService);
|
||||
|
||||
var response = await sut.Create(
|
||||
new(realmId, "slug", "New realm"),
|
||||
TestContext.Current.CancellationToken);
|
||||
|
||||
RealmCreateResponse val = ResultAssert.Success(response);
|
||||
if (realmId.HasValue)
|
||||
Assert.Equal(realmId, val.Realm.Id);
|
||||
else
|
||||
Assert.NotEqual(Guid.Empty, val.Realm.Id);
|
||||
|
||||
Assert.Equal("slug", val.Realm.Slug);
|
||||
Assert.Equal("New realm", val.Realm.Name);
|
||||
Assert.NotEmpty(val.Realm.PrivateKeyEncrypted);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
using IdentityShroud.Core.Contracts;
|
||||
|
||||
namespace IdentityShroud.Core.Tests.Substitutes;
|
||||
|
||||
public static class EncryptionServiceSubstitute
|
||||
{
|
||||
public static IEncryptionService CreatePassthrough()
|
||||
{
|
||||
var encryptionService = Substitute.For<IEncryptionService>();
|
||||
encryptionService
|
||||
.Encrypt(Arg.Any<byte[]>())
|
||||
.Returns(x => x.ArgAt<byte[]>(0));
|
||||
encryptionService
|
||||
.Decrypt(Arg.Any<byte[]>())
|
||||
.Returns(x => x.ArgAt<byte[]>(0));
|
||||
return encryptionService;
|
||||
}
|
||||
}
|
||||
7
IdentityShroud.Core/Contracts/IEncryptionService.cs
Normal file
7
IdentityShroud.Core/Contracts/IEncryptionService.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
namespace IdentityShroud.Core.Contracts;
|
||||
|
||||
public interface IEncryptionService
|
||||
{
|
||||
byte[] Encrypt(byte[] plain);
|
||||
byte[] Decrypt(byte[] cipher);
|
||||
}
|
||||
|
|
@ -2,5 +2,5 @@ namespace IdentityShroud.Core.Contracts;
|
|||
|
||||
public interface ISecretProvider
|
||||
{
|
||||
Task<string> GetSecretAsync(string name);
|
||||
string GetSecretAsync(string name);
|
||||
}
|
||||
|
|
|
|||
3
IdentityShroud.Core/DTO/Realm/RealmCreateRequest.cs
Normal file
3
IdentityShroud.Core/DTO/Realm/RealmCreateRequest.cs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
namespace IdentityShroud.Core.Messages.Realm;
|
||||
|
||||
public record RealmCreateRequest(Guid? Id, string Slug, string Description);
|
||||
|
|
@ -8,9 +8,16 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="10.0.1" />
|
||||
<PackageReference Include="FluentResults" Version="4.0.0" />
|
||||
<PackageReference Include="FluentValidation" Version="12.1.1" />
|
||||
<PackageReference Include="jose-jwt" Version="5.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.2" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="FluentResults" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.AspNetCore.WebUtilities">
|
||||
|
|
|
|||
|
|
@ -1,10 +1,45 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using IdentityShroud.Core.Contracts;
|
||||
|
||||
namespace IdentityShroud.Core.Model;
|
||||
|
||||
[Table("realm")]
|
||||
public class Realm
|
||||
{
|
||||
private byte[] _privateKeyDecrypted = [];
|
||||
|
||||
public Guid Id { get; set; }
|
||||
/// <summary>
|
||||
/// Note this is part of the url we should encourage users to keep it short but we do not want to limit them too much
|
||||
/// </summary>
|
||||
[MaxLength(40)]
|
||||
public string Slug { get; set; } = "";
|
||||
public string Description { get; set; } = "";
|
||||
public List<Client> Clients { get; set; } = [];
|
||||
public byte[] PrivateKey { get; set; }
|
||||
|
||||
[MaxLength(128)]
|
||||
public string Name { get; set; } = "";
|
||||
public List<Client> Clients { get; init; } = [];
|
||||
|
||||
public byte[] PrivateKeyEncrypted
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
_privateKeyDecrypted = [];
|
||||
}
|
||||
} = [];
|
||||
|
||||
public byte[] GetPrivateKey(IEncryptionService encryptionService)
|
||||
{
|
||||
if (_privateKeyDecrypted.Length == 0 && PrivateKeyEncrypted.Length > 0)
|
||||
_privateKeyDecrypted = encryptionService.Decrypt(PrivateKeyEncrypted);
|
||||
return _privateKeyDecrypted;
|
||||
}
|
||||
|
||||
public void SetPrivateKey(IEncryptionService encryptionService, byte[] privateKey)
|
||||
{
|
||||
PrivateKeyEncrypted = encryptionService.Encrypt(privateKey);
|
||||
_privateKeyDecrypted = privateKey;
|
||||
}
|
||||
}
|
||||
17
IdentityShroud.Core/Security/ConfigurationSecretProvider.cs
Normal file
17
IdentityShroud.Core/Security/ConfigurationSecretProvider.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using IdentityShroud.Core.Contracts;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace IdentityShroud.Core.Security;
|
||||
|
||||
/// <summary>
|
||||
/// Secret provider that retrieves secrets from configuration.
|
||||
/// </summary>
|
||||
public class ConfigurationSecretProvider(IConfiguration configuration) : ISecretProvider
|
||||
{
|
||||
private readonly IConfigurationSection secrets = configuration.GetSection("secrets");
|
||||
|
||||
public string GetSecretAsync(string name)
|
||||
{
|
||||
return secrets.GetValue<string>(name) ?? "";
|
||||
}
|
||||
}
|
||||
7
IdentityShroud.Core/Security/RsaHelper.cs
Normal file
7
IdentityShroud.Core/Security/RsaHelper.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
using System.Security.Cryptography;
|
||||
|
||||
namespace IdentityShroud.Core.Security;
|
||||
|
||||
public static class RsaHelper
|
||||
{
|
||||
}
|
||||
23
IdentityShroud.Core/Services/MasterEncryptionService.cs
Normal file
23
IdentityShroud.Core/Services/MasterEncryptionService.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
using IdentityShroud.Core.Contracts;
|
||||
using IdentityShroud.Core.Security;
|
||||
|
||||
namespace IdentityShroud.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="encryptionKey">Encryption key as base64, must be 32 bytes</param>
|
||||
public class EncryptionService(string keyBase64) : IEncryptionService
|
||||
{
|
||||
private readonly byte[] encryptionKey = Convert.FromBase64String(keyBase64);
|
||||
|
||||
public byte[] Encrypt(byte[] plain)
|
||||
{
|
||||
return AesGcmHelper.EncryptAesGcm(plain, encryptionKey);
|
||||
}
|
||||
|
||||
public byte[] Decrypt(byte[] cipher)
|
||||
{
|
||||
return AesGcmHelper.DecryptAesGcm(cipher, encryptionKey);
|
||||
}
|
||||
}
|
||||
31
IdentityShroud.Core/Services/RealmService.cs
Normal file
31
IdentityShroud.Core/Services/RealmService.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
using System.Security.Cryptography;
|
||||
using IdentityShroud.Core.Contracts;
|
||||
using IdentityShroud.Core.Messages.Realm;
|
||||
using IdentityShroud.Core.Model;
|
||||
|
||||
namespace IdentityShroud.Core.Services;
|
||||
|
||||
public record RealmCreateResponse(Realm Realm);
|
||||
|
||||
public class RealmService(
|
||||
Db db,
|
||||
IEncryptionService encryptionService)
|
||||
{
|
||||
public async Task<Result<RealmCreateResponse>> Create(RealmCreateRequest request, CancellationToken ct = default)
|
||||
{
|
||||
Realm realm = new()
|
||||
{
|
||||
Id = request.Id ?? Guid.CreateVersion7(),
|
||||
Slug = request.Slug,
|
||||
Name = request.Description,
|
||||
};
|
||||
|
||||
using RSA rsa = RSA.Create(2048);
|
||||
realm.SetPrivateKey(encryptionService, rsa.ExportPkcs8PrivateKey());
|
||||
|
||||
db.Add(realm);
|
||||
await db.SaveChangesAsync(ct);
|
||||
|
||||
return new RealmCreateResponse(realm);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,15 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAesGcm_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F26fbd7ed219da834e9eaf78ad486d552132eb3c92bbfccff8c27249cdf5f6722_003FAesGcm_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACallInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F402b2077f38742cb9b381ab9e79e493229c00_003F81_003F75c3679f_003FCallInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AHealthCheckEndpointRouteBuilderExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6d0f079e13da4e98881aa3e6e169c6d34f08_003F0e_003Fc2b30661_003FHealthCheckEndpointRouteBuilderExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AKeySizes_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fe6cebf5d2d92b49eb99f568415b3cd457a252cacf81d426ca4f3e94ff429daf7_003FKeySizes_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANamingConventionsExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Feacd26cff49d864d97bf44d3424fd383a26620b1d0c43fb1d6f115da85c655_003FNamingConventionsExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOkOfT_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fe2a19de442f561af862af2dcad0852b7e62707a5cf194d266d1656f92bbb6d2_003FOkOfT_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APostgreSqlBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fcdd0beaf7beaf8366c0862f34fe40da30911084d957625ab31577851ee8cae7_003FPostgreSqlBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATypedResults_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fcea118513a410f660e578fe32bed95cf86457dd135e4b4632ca91eb4f7b_003FTypedResults_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/Environment/Hierarchy/Build/BuildTool/DotNetCliExePath/@EntryValue">/home/eelke/.dotnet/dotnet</s:String>
|
||||
<s:String x:Key="/Default/Environment/Hierarchy/Build/BuildTool/CustomBuildToolPath/@EntryValue">/home/eelke/.dotnet/sdk/10.0.102/MSBuild.dll</s:String>
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=7283fa6f_002Dab5a_002D49e4_002Db89b_002D4cdb1b0ba2b3/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="DecodeTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<TestAncestor>
|
||||
<TestId>xUnit::DC887623-8680-4D3B-B23A-D54F7DA91891::net10.0::IdentityShroud.Core.Tests.UnitTest1.DecodeTest</TestId>
|
||||
<TestId>xUnit::DC887623-8680-4D3B-B23A-D54F7DA91891::net10.0::IdentityShroud.Core.Tests.UnitTest1.CreateTest</TestId>
|
||||
<TestId>xUnit::DC887623-8680-4D3B-B23A-D54F7DA91891::net10.0::IdentityShroud.Core.Tests.Security.AesGcmHelperTests.EncryptDecryptCycleWorks</TestId>
|
||||
</TestAncestor>
|
||||
</SessionState></s:String></wpf:ResourceDictionary>
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=7d190ab0_002D4f9d_002D4f9f_002Dad83_002Da57b539f3bbd/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<Solution />
|
||||
</SessionState></s:String>
|
||||
</wpf:ResourceDictionary>
|
||||
Loading…
Add table
Add a link
Reference in a new issue