Skip to content

Commit

Permalink
feat(keycloak): add seeding data to configuration (#910)
Browse files Browse the repository at this point in the history
* feature(keycloak): add seeding data to configuration
* adjust handling of null dictionary-values in seeder-settings
update framework version
* allow update of existing urls and secret in client
* remove redundant keycloak notfound error-logging
* remove ExcludedUserAttributes, add startup-validation
* add credentials to user
* allow override of identityproviders urls on update
* create users by partial-import, fix client protocol-mappers
* add seeding of protocolmappers and clientscopes
* create clients via partialimport
* allow import of multiple files per realm, fix client-scope updates
* add error 500 messages, fix client protocolmapper creation
* fix keycloak-calls uppercase dictionary keys
* fix keycloak api-calls json-serialisation null-value handling
* update framework version
* Update src/keycloak/Keycloak.Seeding/appsettings.json
---------
Refs: #438
Co-authored-by: Evelyn Gurschler <[email protected]>
  • Loading branch information
ntruchsess authored Sep 26, 2024
1 parent 5f8b7fe commit f8c155c
Show file tree
Hide file tree
Showing 67 changed files with 2,505 additions and 457 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,8 @@ public async Task<CompanyUsersBpnDetails> AddOwnCompanyUsersBusinessPartnerNumbe
}
return acc;
},
acc => (acc.SuccessfulBpns.ToImmutable(), acc.UnsuccessfulBpns.ToImmutable())
acc => (acc.SuccessfulBpns.ToImmutable(), acc.UnsuccessfulBpns.ToImmutable()),
cancellationToken
).ConfigureAwait(ConfigureAwaitOptions.None);

if (successfulBpns.Count != 0)
Expand Down
62 changes: 57 additions & 5 deletions src/framework/Framework.Async/AsyncAggregateExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,61 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Framework.Async;

public static class AsyncAggregateExtensions
{
public static Task<TResult> AggregateAwait<TSource, TAccumulate, TResult>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, Task<TAccumulate>> accumulate, Func<TAccumulate, TResult> select) =>
source.Aggregate(
Task.FromResult(seed),
async (accTask, item) => await accumulate(await accTask.ConfigureAwait(ConfigureAwaitOptions.None), item).ConfigureAwait(ConfigureAwaitOptions.None),
async (accTask) => select(await accTask.ConfigureAwait(ConfigureAwaitOptions.None)));
public static Task<TAccumulate> AggregateAwait<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, CancellationToken, Task<TAccumulate>> accumulate, CancellationToken cancellationToken = default)
{
using var enumerator = source.GetEnumerator();
return AggregateAwait(enumerator, seed, accumulate, cancellationToken);
}

public static Task<TAccumulate> AggregateAwait<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, Task<TAccumulate>> accumulate, CancellationToken cancellationToken = default)
{
using var enumerator = source.GetEnumerator();
return AggregateAwait(enumerator, seed, accumulate, cancellationToken);
}

public static async Task<TResult> AggregateAwait<TSource, TAccumulate, TResult>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, CancellationToken, Task<TAccumulate>> accumulate, Func<TAccumulate, TResult> result, CancellationToken cancellationToken = default) =>
result(await AggregateAwait(source, seed, accumulate, cancellationToken));

public static async Task<TResult> AggregateAwait<TSource, TAccumulate, TResult>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, Task<TAccumulate>> accumulate, Func<TAccumulate, TResult> result, CancellationToken cancellationToken = default) =>
result(await AggregateAwait(source, seed, accumulate, cancellationToken));

public static Task<TSource> AggregateAwait<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, CancellationToken, Task<TSource>> accumulate, CancellationToken cancellationToken = default)
{
using var enumerator = source.GetEnumerator();
if (!enumerator.MoveNext())
throw new InvalidOperationException("source must not be empty");

return AggregateAwait(enumerator, enumerator.Current, accumulate, cancellationToken);
}

public static Task<TSource> AggregateAwait<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, Task<TSource>> accumulate, CancellationToken cancellationToken = default)
{
using var enumerator = source.GetEnumerator();
if (!enumerator.MoveNext())
throw new InvalidOperationException("source must not be empty");

return AggregateAwait(enumerator, enumerator.Current, accumulate, cancellationToken);
}

private static async Task<TAccumulate> AggregateAwait<TSource, TAccumulate>(IEnumerator<TSource> enumerator, TAccumulate seed, Func<TAccumulate, TSource, CancellationToken, Task<TAccumulate>> accumulate, CancellationToken cancellationToken)
{
var accumulator = seed;
while (enumerator.MoveNext())
{
cancellationToken.ThrowIfCancellationRequested();
accumulator = await accumulate(accumulator, enumerator.Current, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None);
}
return accumulator;
}

private static async Task<TAccumulate> AggregateAwait<TSource, TAccumulate>(IEnumerator<TSource> enumerator, TAccumulate seed, Func<TAccumulate, TSource, Task<TAccumulate>> accumulate, CancellationToken cancellationToken)
{
var accumulator = seed;
while (enumerator.MoveNext())
{
cancellationToken.ThrowIfCancellationRequested();
accumulator = await accumulate(accumulator, enumerator.Current).ConfigureAwait(ConfigureAwaitOptions.None);
}
return accumulator;
}
}
2 changes: 1 addition & 1 deletion src/framework/Framework.Async/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<Project>
<PropertyGroup>
<VersionPrefix>2.8.0</VersionPrefix>
<VersionPrefix>2.9.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
</Project>
2 changes: 1 addition & 1 deletion src/framework/Framework.Cors/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<Project>
<PropertyGroup>
<VersionPrefix>2.8.0</VersionPrefix>
<VersionPrefix>2.9.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
</Project>
2 changes: 1 addition & 1 deletion src/framework/Framework.DBAccess/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<Project>
<PropertyGroup>
<VersionPrefix>2.8.0</VersionPrefix>
<VersionPrefix>2.9.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<Project>
<PropertyGroup>
<VersionPrefix>2.8.0</VersionPrefix>
<VersionPrefix>2.9.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<Project>
<PropertyGroup>
<VersionPrefix>2.8.0</VersionPrefix>
<VersionPrefix>2.9.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<Project>
<PropertyGroup>
<VersionPrefix>2.8.0</VersionPrefix>
<VersionPrefix>2.9.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<Project>
<PropertyGroup>
<VersionPrefix>2.8.0</VersionPrefix>
<VersionPrefix>2.9.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<Project>
<PropertyGroup>
<VersionPrefix>2.8.0</VersionPrefix>
<VersionPrefix>2.9.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<Project>
<PropertyGroup>
<VersionPrefix>2.8.0</VersionPrefix>
<VersionPrefix>2.9.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
</Project>
2 changes: 1 addition & 1 deletion src/framework/Framework.IO/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<Project>
<PropertyGroup>
<VersionPrefix>2.8.0</VersionPrefix>
<VersionPrefix>2.9.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
</Project>
2 changes: 1 addition & 1 deletion src/framework/Framework.Linq/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<Project>
<PropertyGroup>
<VersionPrefix>2.8.0</VersionPrefix>
<VersionPrefix>2.9.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
</Project>
13 changes: 13 additions & 0 deletions src/framework/Framework.Linq/IfAnyExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,17 @@ public static bool IfAny<T, R>(this IEnumerable<T> source, Func<IEnumerable<T>,
returnValue = default;
return false;
}

public static async ValueTask<bool> IfAnyAwait<T>(this IEnumerable<T> source, Func<IEnumerable<T>, Task> process)
{
var enumerator = source.GetEnumerator();

if (enumerator.MoveNext())
{
await process(new IfAnyEnumerable<T>(source, enumerator)).ConfigureAwait(ConfigureAwaitOptions.None);
return true;
}

return false;
}
}
22 changes: 18 additions & 4 deletions src/framework/Framework.Linq/NullOrSequenceEqualExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Framework.Linq;

public static class NullOrSequenceEqualExtensions
{
public static bool NullOrContentEqual<T>(this IEnumerable<T>? items, IEnumerable<T>? others, IEqualityComparer<T>? comparer = null) where T : IComparable =>
public static bool NullOrContentEqual<T>(this IEnumerable<T>? items, IEnumerable<T>? others, IEqualityComparer<T>? comparer = null) where T : IComparable? =>
items == null && others == null ||
items != null && others != null &&
items.OrderBy(x => x).SequenceEqual(others.OrderBy(x => x), comparer);
items.Order().SequenceEqual(others.Order(), comparer);

public static bool NullOrContentEqual<K, V>(this IEnumerable<KeyValuePair<K, V>>? items, IEnumerable<KeyValuePair<K, V>>? others, IEqualityComparer<KeyValuePair<K, V>>? comparer = null) where K : IComparable where V : IComparable =>
public static bool NullOrContentEqual<K, V>(this IEnumerable<KeyValuePair<K, V>>? items, IEnumerable<KeyValuePair<K, V>>? others, IEqualityComparer<KeyValuePair<K, V>>? comparer = null) where K : IComparable where V : IComparable? =>
items == null && others == null ||
items != null && others != null &&
items.OrderBy(x => x.Key).SequenceEqual(others.OrderBy(x => x.Key), comparer);
Expand All @@ -37,6 +37,11 @@ public static bool NullOrContentEqual<K, V>(this IEnumerable<KeyValuePair<K, IEn
items == null && others == null ||
items != null && others != null &&
items.OrderBy(x => x.Key).SequenceEqual(others.OrderBy(x => x.Key), comparer ?? new EnumerableValueKeyValuePairEqualityComparer<K, V>());

public static bool NullOrNullableContentEqual<K, V>(this IEnumerable<KeyValuePair<K, IEnumerable<V>?>>? items, IEnumerable<KeyValuePair<K, IEnumerable<V>?>>? others, IEqualityComparer<KeyValuePair<K, IEnumerable<V>?>>? comparer = null) where V : IComparable =>
items == null && others == null ||
items != null && others != null &&
items.OrderBy(x => x.Key).SequenceEqual(others.OrderBy(x => x.Key), comparer ?? new NullableEnumerableValueKeyValuePairEqualityComparer<K, V>());
}

public class KeyValuePairEqualityComparer<K, V> : IEqualityComparer<KeyValuePair<K, V>> where K : IComparable where V : IComparable
Expand All @@ -50,7 +55,16 @@ public class EnumerableValueKeyValuePairEqualityComparer<K, V> : IEqualityCompar
{
public bool Equals(KeyValuePair<K, IEnumerable<V>> source, KeyValuePair<K, IEnumerable<V>> other) =>
Equals(source.Key, other.Key) &&
source.Value.NullOrContentEqual(other.Value);
source.Value.Order().SequenceEqual(other.Value.Order());

public int GetHashCode([DisallowNull] KeyValuePair<K, IEnumerable<V>> obj) => throw new NotImplementedException();
}

public class NullableEnumerableValueKeyValuePairEqualityComparer<K, V> : IEqualityComparer<KeyValuePair<K, IEnumerable<V>?>> where V : IComparable
{
public bool Equals(KeyValuePair<K, IEnumerable<V>?> source, KeyValuePair<K, IEnumerable<V>?> other) =>
Equals(source.Key, other.Key) &&
source.Value.NullOrContentEqual(other.Value);

public int GetHashCode([DisallowNull] KeyValuePair<K, IEnumerable<V>?> obj) => throw new NotImplementedException();
}
6 changes: 6 additions & 0 deletions src/framework/Framework.Linq/NullableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,10 @@ public static class NullableExtensions
{
public static bool IsNullOrEmpty<T>(this IEnumerable<T>? collection) =>
collection == null || !collection.Any();

public static IEnumerable<KeyValuePair<TKey, TValue>> FilterNotNullValues<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue?>> source) where TValue : notnull =>
source.Where(x => x.Value is not null).Cast<KeyValuePair<TKey, TValue>>();

public static IEnumerable<T> FilterNotNull<T>(this IEnumerable<T?> source) where T : notnull =>
source.Where(x => x is not null).Cast<T>();
}
2 changes: 1 addition & 1 deletion src/framework/Framework.Logging/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<Project>
<PropertyGroup>
<VersionPrefix>2.8.0</VersionPrefix>
<VersionPrefix>2.9.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
</Project>
2 changes: 1 addition & 1 deletion src/framework/Framework.Models/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<Project>
<PropertyGroup>
<VersionPrefix>2.8.0</VersionPrefix>
<VersionPrefix>2.9.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
</Project>
2 changes: 1 addition & 1 deletion src/framework/Framework.Seeding/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<Project>
<PropertyGroup>
<VersionPrefix>2.8.0</VersionPrefix>
<VersionPrefix>2.9.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
</Project>
2 changes: 1 addition & 1 deletion src/framework/Framework.Swagger/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<Project>
<PropertyGroup>
<VersionPrefix>2.8.0</VersionPrefix>
<VersionPrefix>2.9.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
</Project>
2 changes: 1 addition & 1 deletion src/framework/Framework.Token/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<Project>
<PropertyGroup>
<VersionPrefix>2.8.0</VersionPrefix>
<VersionPrefix>2.9.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
</Project>
2 changes: 1 addition & 1 deletion src/framework/Framework.Web/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<Project>
<PropertyGroup>
<VersionPrefix>2.8.0</VersionPrefix>
<VersionPrefix>2.9.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
</Project>
31 changes: 20 additions & 11 deletions src/keycloak/Keycloak.ErrorHandling/FlurlErrorHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,45 +21,54 @@
using Microsoft.Extensions.Logging;
using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling;
using System.Net;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Org.Eclipse.TractusX.Portal.Backend.Keycloak.ErrorHandling;

public static class FlurlErrorHandler
{
public static void ConfigureErrorHandler(ILogger logger, bool isDevelopment)
public static void ConfigureErrorHandler(ILogger logger)
{
FlurlHttp.Configure(settings => settings.OnError = (call) =>
{
var message = $"{call.HttpResponseMessage?.ReasonPhrase ?? "ReasonPhrase is null"}: {call.HttpRequestMessage.RequestUri}";
if (isDevelopment)
if (logger.IsEnabled(LogLevel.Debug))
{
LogDevelopmentError(logger, call);
LogDebug(logger, call);
}
else
{
logger.LogError(call.Exception, "{Message}", message);
}
if (call.HttpResponseMessage != null)
{
var errorContent = JsonSerializer.Deserialize<KeycloakErrorResponse>(call.HttpResponseMessage.Content.ReadAsStream())?.ErrorMessage;
if (!string.IsNullOrWhiteSpace(errorContent))
{
message = errorContent;
}
throw call.HttpResponseMessage.StatusCode switch
{
HttpStatusCode.NotFound => new KeycloakEntityNotFoundException(message, call.Exception),
HttpStatusCode.Conflict => new KeycloakEntityConflictException(message, call.Exception),
HttpStatusCode.BadRequest => new ArgumentException(message, call.Exception),
HttpStatusCode.BadRequest => new KeycloakNoSuccessException(message, call.Exception),
_ => new ServiceException(message, call.Exception, call.HttpResponseMessage.StatusCode),
};
}
throw new ServiceException(message, call.Exception);
});
}

private static void LogDevelopmentError(ILogger logger, FlurlCall call)
private static void LogDebug(ILogger logger, FlurlCall call)
{
var request = call.HttpRequestMessage == null ? "" : $"{call.HttpRequestMessage.Method} {call.HttpRequestMessage.RequestUri} HTTP/{call.HttpRequestMessage.Version}\n{call.HttpRequestMessage.Headers}\n";
var requestBody = call.RequestBody == null ? "\n" : call.RequestBody + "\n\n";
var response = call.HttpResponseMessage == null ? "" : call.HttpResponseMessage.ReasonPhrase + "\n";
var responseContent = call.HttpResponseMessage?.Content == null ? "" : call.HttpResponseMessage.Content.ReadAsStringAsync().Result + "\n";
logger.LogError(call.Exception, "{Request}{Body}{Response}{Content}", request, requestBody, response, responseContent);
logger.LogDebug(call.Exception, "{Request}{Body}{Response}{Content}", request, requestBody, response, responseContent);
}

public class KeycloakErrorResponse
{
[JsonPropertyName("errorMessage")]
public string? ErrorMessage { get; set; }
}
}
Loading

0 comments on commit f8c155c

Please sign in to comment.