Skip to content

Commit

Permalink
simplify logic
Browse files Browse the repository at this point in the history
  • Loading branch information
ntruchsess committed Nov 28, 2024
1 parent ed0b2ff commit 7396156
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 109 deletions.
27 changes: 9 additions & 18 deletions src/keycloak/Keycloak.Seeding/BusinessLogic/SeedDataHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class SeedDataHandler : ISeedDataHandler
private KeycloakRealm? _keycloakRealm;
private IReadOnlyDictionary<string, string>? _idOfClients;
private SeederConfigurationModel? _defaultConfiguration;
private Dictionary<string, (bool Create, bool Update, bool Delete)>? _flatConfiguration;
private IReadOnlyDictionary<ConfigurationKey, bool>? _flatConfiguration;

public async Task Import(KeycloakRealmSettings realmSettings, CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -165,22 +165,13 @@ public IEnumerable<AuthenticationExecutionModel> GetAuthenticationExecutions(str
public AuthenticatorConfigModel GetAuthenticatorConfig(string? alias) =>
_keycloakRealm?.AuthenticatorConfig?.SingleOrDefault(x => x.Alias == (alias ?? throw new ConflictException("alias is null"))) ?? throw new ConflictException($"authenticatorConfig {alias} does not exist");

public KeycloakSeederConfigModel GetSpecificConfiguration(ConfigurationKey configKey)
{
var config = _defaultConfiguration ?? throw new ConflictException("configuration must not be null");
config.SeederConfigurations.TryGetValue(configKey.ToString().ToLower(), out var specificConfiguration);
return new KeycloakSeederConfigModel(config, specificConfiguration);
}
public KeycloakSeederConfigModel GetSpecificConfiguration(ConfigurationKey configKey) =>
new KeycloakSeederConfigModel(
_defaultConfiguration ?? throw new ConflictException("configuration must not be null"),
_defaultConfiguration.SeederConfigurations?.TryGetValue(configKey.ToString(), out var specificConfiguration) ?? false ? specificConfiguration : null);

public bool IsModificationAllowed(ConfigurationKey configKey)
{
var flatConfig = _flatConfiguration ?? throw new ConflictException("configuration must not be null");
var config = _defaultConfiguration ?? throw new ConflictException("configuration must not be null");
if (flatConfig.TryGetValue(configKey.ToString().ToLower(), out var result))
{
return result.Create || result.Update || result.Delete;
}

return config.Create || config.Update || config.Delete;
}
public bool IsModificationAllowed(ConfigurationKey configKey) =>
(_flatConfiguration ?? throw new ConflictException("configuration must not be null")).TryGetValue(configKey, out var result)
? result
: (_defaultConfiguration ?? throw new ConflictException("configuration must not be null")).Create || _defaultConfiguration.Update || _defaultConfiguration.Delete;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,74 +18,42 @@
********************************************************************************/

using Org.Eclipse.TractusX.Portal.Backend.Keycloak.Seeding.Models;
using System.Collections.Immutable;

namespace Org.Eclipse.TractusX.Portal.Backend.Keycloak.Seeding.Extensions;

public static class KeycloakRealmSettingsExtensions
{
public static Dictionary<string, (bool Create, bool Update, bool Delete)> GetFlatDictionary(this KeycloakRealmSettings realmSettings)
{
var result = new Dictionary<string, (bool Create, bool Update, bool Delete, int Depth)>();

GetFlatDictionary(realmSettings.SeederConfigurations, 0, result);

return result.Select(x => new KeyValuePair<string, ValueTuple<bool, bool, bool>>(x.Key.ToLower(), new ValueTuple<bool, bool, bool>(x.Value.Create, x.Value.Update, x.Value.Delete))).ToDictionary();
}

private static (bool Create, bool Update, bool Delete) GetFlatDictionary(
IEnumerable<SeederConfiguration>? configurations,
int depth,
IDictionary<string, (bool Create, bool Update, bool Delete, int Depth)> result)
{
if (configurations == null)
{
return (false, false, false);
}

var parentCreate = false;
var parentUpdate = false;
var parentDelete = false;

foreach (var config in configurations)
{
// Process child configurations first
var childPermissions = GetFlatDictionary(config.SeederConfigurations, depth + 1, result);

// Combine child permissions with current configuration's permissions
var create = config.Create || childPermissions.Create;
var update = config.Update || childPermissions.Update;
var delete = config.Delete || childPermissions.Delete;

// If the key doesn't exist or the current depth is deeper, update the result
if (!result.TryGetValue(config.Key.ToLower(), out var value) || depth > value.Depth)
{
result[config.Key.ToLower()] = (create, update, delete, depth);
}

// Aggregate permissions for the parent
parentCreate |= create;
parentUpdate |= update;
parentDelete |= delete;
}

return (parentCreate, parentUpdate, parentDelete);
}
public static IReadOnlyDictionary<ConfigurationKey, bool>? GetFlatDictionary(this KeycloakRealmSettings realmSettings) =>
realmSettings.SeederConfigurations?
.Join(
Enum.GetValues<ConfigurationKey>(),
config => config.Key,
key => key.ToString(),
(SeederConfiguration config, ConfigurationKey key) => KeyValuePair.Create(key, GetFlat(config)),
StringComparer.OrdinalIgnoreCase
).ToImmutableDictionary();

private static bool GetFlat(SeederConfiguration config) =>
config.Create || config.Update || config.Delete || (config.SeederConfigurations != null && config.SeederConfigurations.Any(GetFlat));

public static SeederConfigurationModel GetConfigurationDictionaries(this KeycloakRealmSettings realmSettings) =>
new(
realmSettings.Create,
realmSettings.Update,
realmSettings.Delete,
realmSettings.SeederConfigurations?.ToDictionary(sc =>
sc.Key.ToLower(),
ConvertSeederConfigToSeederConfigurationModel) ?? new Dictionary<string, SeederConfigurationModel>());
realmSettings.SeederConfigurations?.ToImmutableDictionary(sc =>
sc.Key,
ConvertSeederConfigToSeederConfigurationModel,
StringComparer.OrdinalIgnoreCase));

private static SeederConfigurationModel ConvertSeederConfigToSeederConfigurationModel(this SeederConfiguration seederConfig) =>
new(
seederConfig.Create,
seederConfig.Update,
seederConfig.Delete,
seederConfig.SeederConfigurations?.ToDictionary(sc =>
seederConfig.SeederConfigurations?.ToImmutableDictionary(sc =>
sc.Key.ToLower(),
ConvertSeederConfigToSeederConfigurationModel) ?? new Dictionary<string, SeederConfigurationModel>());
ConvertSeederConfigToSeederConfigurationModel,
StringComparer.OrdinalIgnoreCase));
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public static bool ModificationAllowed(this KeycloakSeederConfigModel config, Mo
}

// If we have a configuration for a specific entry return its value
if (specificConfig?.SeederConfigurations.TryGetValue(entityKey.ToLower(), out var specificEntry) == true)
if (specificConfig?.SeederConfigurations?.TryGetValue(entityKey, out var specificEntry) ?? false)
{
return specificEntry.ModifyAllowed(modificationType);
}
Expand All @@ -51,15 +51,14 @@ public static bool ModificationAllowed(this KeycloakSeederConfigModel config, st
{
// Check if the specific configuration contains the entity key
// e.g. for the users configuration check for a specific user configuration
if (config.SpecificConfiguration?.SeederConfigurations.TryGetValue(containingEntityKey.ToLower(), out var containingEntityKeyConfiguration) == true)
if (config.SpecificConfiguration?.SeederConfigurations?.TryGetValue(containingEntityKey, out var containingEntityKeyConfiguration) ?? false)
{
// check if the specific entity configuration has a configuration for the section
// e.g. for the specific user configuration is there a section for federated identities
if (!containingEntityKeyConfiguration.SeederConfigurations.TryGetValue(configKey.ToString().ToLower(), out var containingEntityTypeConfig))
if (!(containingEntityKeyConfiguration.SeederConfigurations?.TryGetValue(configKey.ToString(), out var containingEntityTypeConfig) ?? false))
{
config.DefaultSettings.SeederConfigurations.TryGetValue(configKey.ToString().ToLower(), out var specificConfig);
var configModel = config with { SpecificConfiguration = specificConfig };
return configModel.ModificationAllowed(modificationType, entityKey);
return (config with { SpecificConfiguration = config.DefaultSettings.SeederConfigurations?.TryGetValue(configKey.ToString(), out var specificConfig) ?? false ? specificConfig : null })
.ModificationAllowed(modificationType, entityKey);
}

// if the entity key isn't set check the configuration for the type
Expand All @@ -69,14 +68,13 @@ public static bool ModificationAllowed(this KeycloakSeederConfigModel config, st
}

// If we have a configuration for a specific entry return its value otherwise take the section configuration
containingEntityTypeConfig.SeederConfigurations.TryGetValue(entityKey.ToLower(), out var entity);
return entity?.ModifyAllowed(modificationType) ?? containingEntityTypeConfig.ModifyAllowed(modificationType);
return (containingEntityTypeConfig.SeederConfigurations?.TryGetValue(entityKey, out var entity) ?? false ? entity?.ModifyAllowed(modificationType) : null)
?? containingEntityTypeConfig.ModifyAllowed(modificationType);
}

// if no configuration isn't set check the top level configuration
config.DefaultSettings.SeederConfigurations.TryGetValue(configKey.ToString().ToLower(), out var topLevelSpecificConfig);
var topLevelConfig = config with { SpecificConfiguration = topLevelSpecificConfig };
return topLevelConfig.ModificationAllowed(modificationType, entityKey);
return (config with { SpecificConfiguration = config.DefaultSettings.SeederConfigurations?.TryGetValue(configKey.ToString(), out var topLevelSpecificConfig) ?? false ? topLevelSpecificConfig : null })
.ModificationAllowed(modificationType, entityKey);
}

private static bool ModifyAllowed(this SeederConfigurationModel configuration, ModificationType modificationType) =>
Expand Down
2 changes: 2 additions & 0 deletions src/keycloak/Keycloak.Seeding/Models/SeederConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
********************************************************************************/

using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Validation;
using System.ComponentModel.DataAnnotations;

namespace Org.Eclipse.TractusX.Portal.Backend.Keycloak.Seeding.Models;

public class SeederConfiguration
{
[Required(AllowEmptyStrings = false)]
public string Key { get; set; } = null!;
public bool Create { get; set; }
public bool Update { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ public record SeederConfigurationModel(
bool Create,
bool Update,
bool Delete,
IDictionary<string, SeederConfigurationModel> SeederConfigurations
IReadOnlyDictionary<string, SeederConfigurationModel>? SeederConfigurations
);
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
/********************************************************************************
* Copyright (c) 2024 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

using Org.Eclipse.TractusX.Portal.Backend.Keycloak.Seeding.Extensions;
using Org.Eclipse.TractusX.Portal.Backend.Keycloak.Seeding.Models;

Expand All @@ -17,8 +36,29 @@ public void GetFlatDictionary_WithInDepthConfiguration_DeeperOneIsTaken()
Create = false,
Update = false,
Delete = false,
SeederConfigurations = new List<SeederConfiguration>
{
SeederConfigurations =
[
new()
{
Key = "roles",
Create = true,
Update = false,
Delete = false
},
new()
{
Key = "localiZations",
Create = false,
Update = true,
Delete = false
},
new()
{
Key = "UserProfile",
Create = false,
Update = false,
Delete = true
},
new()
{
Key = "FederatedIdentities",
Expand All @@ -30,48 +70,67 @@ public void GetFlatDictionary_WithInDepthConfiguration_DeeperOneIsTaken()
{
Key = "Users",
Create = false,
Update = true,
Delete = true,
SeederConfigurations = new List<SeederConfiguration>
{
Update = false,
Delete = false,
SeederConfigurations =
[
new()
{
Key = "testUser",
Key = "testUser1",
Create = false,
Update = true,
Update = false,
Delete = false
},
new()
{
Key = "testUser2",
Create = false,
Update = false,
Delete = false,
SeederConfigurations = new List<SeederConfiguration>
{
SeederConfigurations =
[
new()
{
Key = "FederatedIdentities",
Create = true,
Update = true,
Delete = false
}
}
]
}
}
]
},
new()
{
Key = "Clients",
Create = false,
Update = false,
Delete = false
Delete = false,
SeederConfigurations =
[
new()
{
Key = "testClient",
Create = false,
Update = false,
Delete = false
}
]
}
}
]
};

// Act
var result = realmSettings.GetFlatDictionary();

// Assert
result.Should().HaveCount(4).And.Satisfy(
x => x.Key == "federatedidentities" && x.Value.Create && x.Value.Update && !x.Value.Delete,
x => x.Key == "testuser" && x.Value.Create && x.Value.Update && !x.Value.Delete,
x => x.Key == "users" && x.Value.Create && x.Value.Update && x.Value.Delete,
x => x.Key == "clients" && !x.Value.Create && !x.Value.Update && !x.Value.Delete
result.Should().HaveCount(6).And.Satisfy(
x => x.Key == ConfigurationKey.Roles && x.Value,
x => x.Key == ConfigurationKey.Localizations && x.Value,
x => x.Key == ConfigurationKey.UserProfile && x.Value,
x => x.Key == ConfigurationKey.FederatedIdentities && !x.Value,
x => x.Key == ConfigurationKey.Users && x.Value,
x => x.Key == ConfigurationKey.Clients && !x.Value
);
}

Expand All @@ -84,26 +143,26 @@ public void GetConfigurationDictionaries_WithNestedConfigurations_ReturnsExpecte
Create = true,
Update = false,
Delete = true,
SeederConfigurations = new List<SeederConfiguration>
{
SeederConfigurations =
[
new()
{
Key = "Users",
Create = true,
Update = false,
Delete = true,
SeederConfigurations = new List<SeederConfiguration>
{
SeederConfigurations =
[
new()
{
Key = "testUser",
Create = false,
Update = true,
Delete = false
}
}
]
}
}
]
};

// Act
Expand All @@ -114,10 +173,12 @@ public void GetConfigurationDictionaries_WithNestedConfigurations_ReturnsExpecte
result.Update.Should().BeFalse();
result.Delete.Should().BeTrue();
result.SeederConfigurations.Should().ContainSingle().And.Satisfy(
x => x.Key == "users" && x.Value.Create && !x.Value.Update && x.Value.Delete &&
x.Value.SeederConfigurations.Count == 1 && x.Value.SeederConfigurations.ContainsKey("testuser") &&
x => x.Key == "Users" && x.Value.Create && !x.Value.Update && x.Value.Delete &&
x.Value.SeederConfigurations != null && x.Value.SeederConfigurations.Count == 1 && x.Value.SeederConfigurations.ContainsKey("testUser") &&
!x.Value.SeederConfigurations.Single().Value.Create &&
x.Value.SeederConfigurations.Single().Value.Update &&
!x.Value.SeederConfigurations.Single().Value.Delete);
result.SeederConfigurations!.TryGetValue("users", out var _).Should().BeTrue();
result.SeederConfigurations["uSeRs"].SeederConfigurations!.TryGetValue("TESTUSER", out var _).Should().BeTrue();
}
}
Loading

0 comments on commit 7396156

Please sign in to comment.