From f1e2a092f916c41d1949bf36efa16a90abda664d Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sat, 25 Jul 2026 14:07:06 +0200 Subject: [PATCH] Fix external authentication setting field identifiers --- .../Services/ExtensionDescriptorValidator.cs | 6 +++++- .../Extensibility/ExtensionConformanceTests.cs | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/modules/Elsa.ExternalAuthentication/Services/ExtensionDescriptorValidator.cs b/src/modules/Elsa.ExternalAuthentication/Services/ExtensionDescriptorValidator.cs index 43bc37e1a..524730683 100644 --- a/src/modules/Elsa.ExternalAuthentication/Services/ExtensionDescriptorValidator.cs +++ b/src/modules/Elsa.ExternalAuthentication/Services/ExtensionDescriptorValidator.cs @@ -10,6 +10,7 @@ namespace Elsa.ExternalAuthentication.Services; public sealed class ExtensionDescriptorValidator { private static readonly Regex IdentifierPattern = new("^[a-z][a-z0-9]*(?:[-.][a-z0-9]+)*$", RegexOptions.CultureInvariant); + private static readonly Regex SettingFieldNamePattern = new("^[a-z][A-Za-z0-9]*$", RegexOptions.CultureInvariant); private static readonly HashSet SupportedValueTypes = new(StringComparer.Ordinal) { "string", "secret", "boolean", "integer", "number", "uri", "string-array", "json" @@ -79,7 +80,7 @@ public sealed class ExtensionDescriptorValidator var names = new HashSet(StringComparer.Ordinal); foreach (var field in fields) { - if (!IsIdentifier(field.Name)) + if (!IsSettingFieldName(field.Name)) failures.Add($"Extension '{extensionType}' has invalid field name '{field.Name}'."); else if (!names.Add(field.Name)) failures.Add($"Extension '{extensionType}' defines field '{field.Name}' more than once."); @@ -123,4 +124,7 @@ public sealed class ExtensionDescriptorValidator private static bool IsIdentifier(string? value) => !string.IsNullOrWhiteSpace(value) && IdentifierPattern.IsMatch(value); + + private static bool IsSettingFieldName(string? value) => + !string.IsNullOrWhiteSpace(value) && SettingFieldNamePattern.IsMatch(value); } diff --git a/test/unit/Elsa.ExternalAuthentication.UnitTests/Extensibility/ExtensionConformanceTests.cs b/test/unit/Elsa.ExternalAuthentication.UnitTests/Extensibility/ExtensionConformanceTests.cs index 2830742dd..4a53a3345 100644 --- a/test/unit/Elsa.ExternalAuthentication.UnitTests/Extensibility/ExtensionConformanceTests.cs +++ b/test/unit/Elsa.ExternalAuthentication.UnitTests/Extensibility/ExtensionConformanceTests.cs @@ -2,6 +2,7 @@ using System.Text.Json; using Elsa.ExternalAuthentication.Contracts; using Elsa.ExternalAuthentication.Models; using Elsa.ExternalAuthentication.Options; +using Elsa.ExternalAuthentication.Policies; using Elsa.ExternalAuthentication.Services; namespace Elsa.ExternalAuthentication.UnitTests.Extensibility; @@ -80,6 +81,23 @@ public class ExtensionConformanceTests Assert.Contains("custom-editor", exception.Message); } + [Fact] + public void DescriptorValidatorAcceptsLowerCamelCaseSettingFieldNames() + { + var descriptor = new ExtensionDescriptorValidator().Validate(new CreateUserUnlinkedIdentityPolicy()); + + Assert.Contains(descriptor.Fields, field => field.Name == "defaultRoleIds"); + } + + [Fact] + public void DescriptorValidatorKeepsExtensionTypesLowercaseAndStable() + { + var exception = Assert.Throws(() => + new ExtensionDescriptorValidator().Validate(new ConformanceAdapter("InvalidType"))); + + Assert.Contains("stable identifier", exception.Message); + } + [Fact] public async Task SettingsMigrationRunsAdapterOwnedForwardStepsAndRejectsUnsupportedVersions() {