| | | 1 | | using System.Text.Json; |
| | | 2 | | using Elsa.ExternalAuthentication.Contracts; |
| | | 3 | | using Elsa.ExternalAuthentication.Models; |
| | | 4 | | using Elsa.ExternalAuthentication.Options; |
| | | 5 | | using Elsa.ExternalAuthentication.Services; |
| | | 6 | | using Microsoft.Extensions.Options; |
| | | 7 | | |
| | | 8 | | namespace Elsa.ExternalAuthentication.Providers; |
| | | 9 | | |
| | | 10 | | /// <summary> |
| | | 11 | | /// Supplies deployment-owned connections from the current External Authentication options. |
| | | 12 | | /// </summary> |
| | 4 | 13 | | public sealed class ConfigurationIdentityProviderConnectionSource( |
| | 4 | 14 | | IOptionsMonitor<ExternalAuthenticationOptions> options, |
| | 4 | 15 | | ConnectionRevisionCalculator revisionCalculator, |
| | 4 | 16 | | IExternalAuthenticationAdapterRegistry adapters) : IIdentityProviderConnectionSource |
| | | 17 | | { |
| | | 18 | | public const string SourceName = "configuration"; |
| | | 19 | | |
| | 1 | 20 | | public string Name => SourceName; |
| | 1 | 21 | | public ConnectionSourceOwnership Ownership => ConnectionSourceOwnership.Configuration; |
| | | 22 | | |
| | | 23 | | public ValueTask<ConnectionSourceSnapshot> GetSnapshotAsync(ConnectionScope scope, CancellationToken cancellationTok |
| | | 24 | | { |
| | 4 | 25 | | cancellationToken.ThrowIfCancellationRequested(); |
| | | 26 | | |
| | 4 | 27 | | var connections = (options.CurrentValue.ConfigurationConnections ?? []) |
| | 6 | 28 | | .Where(connection => IsInScope(connection.TenantId, scope)) |
| | 4 | 29 | | .Select(connection => Materialize(connection, scope)) |
| | 0 | 30 | | .OrderBy(connection => ConnectionRevisionCalculator.NormalizeKey(connection.Key), StringComparer.Ordinal) |
| | 0 | 31 | | .ThenBy(connection => connection.Id, StringComparer.Ordinal) |
| | 4 | 32 | | .ToArray(); |
| | | 33 | | |
| | 3 | 34 | | return ValueTask.FromResult(new ConnectionSourceSnapshot(scope, revisionCalculator.CalculateSourceVersion(scope, |
| | | 35 | | } |
| | | 36 | | |
| | | 37 | | private IdentityProviderConnection Materialize(IdentityProviderConnection configuredConnection, ConnectionScope scop |
| | | 38 | | { |
| | 4 | 39 | | if (adapters.TryGet(configuredConnection.AdapterType, out var adapter)) |
| | 1 | 40 | | AdapterSettingsSecretFieldGuard.ThrowIfContainsDeclaredSecret(configuredConnection.AdapterSettings, adapter. |
| | | 41 | | |
| | 3 | 42 | | var connection = new IdentityProviderConnection |
| | 3 | 43 | | { |
| | 3 | 44 | | Id = string.IsNullOrWhiteSpace(configuredConnection.Id) |
| | 3 | 45 | | ? ConnectionRevisionCalculator.CalculateConfigurationConnectionId(scope, configuredConnection.Key) |
| | 3 | 46 | | : configuredConnection.Id, |
| | 3 | 47 | | TenantId = scope.TenantId, |
| | 3 | 48 | | Key = ConnectionRevisionCalculator.NormalizeKey(configuredConnection.Key), |
| | 3 | 49 | | AdapterType = configuredConnection.AdapterType, |
| | 3 | 50 | | AdapterSettingsVersion = configuredConnection.AdapterSettingsVersion, |
| | 3 | 51 | | AdapterSettings = CloneJson(configuredConnection.AdapterSettings), |
| | 0 | 52 | | SecretBindings = configuredConnection.SecretBindings?.ToDictionary(x => x.Key, x => x.Value, StringComparer. |
| | 3 | 53 | | DisplayName = configuredConnection.DisplayName, |
| | 3 | 54 | | IconId = configuredConnection.IconId, |
| | 3 | 55 | | DisplayOrder = configuredConnection.DisplayOrder, |
| | 3 | 56 | | IsPreferred = configuredConnection.IsPreferred, |
| | 3 | 57 | | IsEnabled = configuredConnection.IsEnabled, |
| | 3 | 58 | | OverridesConfigurationConnection = false, |
| | 3 | 59 | | ArchivedAt = configuredConnection.ArchivedAt, |
| | 3 | 60 | | UnlinkedPolicy = configuredConnection.UnlinkedPolicy is { } policy ? new PolicySelection(policy.Type, policy |
| | 3 | 61 | | PermissionGrantSources = (configuredConnection.PermissionGrantSources ?? []) |
| | 0 | 62 | | .Select(x => new GrantSourceSelection(x.Type, x.SettingsVersion, CloneJson(x.Settings), x.Order)) |
| | 3 | 63 | | .ToArray(), |
| | 3 | 64 | | ClaimProjection = CloneClaimProjection(configuredConnection.ClaimProjection), |
| | 3 | 65 | | UpstreamLogoutMode = configuredConnection.UpstreamLogoutMode, |
| | 3 | 66 | | Revision = 1, |
| | 3 | 67 | | CreatedAt = DateTimeOffset.UnixEpoch, |
| | 3 | 68 | | UpdatedAt = DateTimeOffset.UnixEpoch |
| | 3 | 69 | | }; |
| | | 70 | | |
| | 3 | 71 | | connection.MaterialRevision = revisionCalculator.CalculateMaterialRevision(connection); |
| | 3 | 72 | | return connection; |
| | | 73 | | } |
| | | 74 | | |
| | | 75 | | private static bool IsInScope(string? tenantId, ConnectionScope scope) => |
| | 6 | 76 | | scope == ConnectionScope.Host && (string.IsNullOrWhiteSpace(tenantId) || string.Equals(tenantId, ConnectionScope |
| | | 77 | | |
| | | 78 | | private static ClaimProjection CloneClaimProjection(ClaimProjection? projection) |
| | | 79 | | { |
| | 3 | 80 | | projection ??= ClaimProjection.Empty; |
| | 3 | 81 | | return new ClaimProjection( |
| | 3 | 82 | | new HashSet<string>(projection.AllowedClaimTypes ?? new HashSet<string>(), StringComparer.Ordinal), |
| | 3 | 83 | | new HashSet<string>(projection.RedactedClaimTypes ?? new HashSet<string>(), StringComparer.Ordinal), |
| | 3 | 84 | | projection.MaximumClaimCount, |
| | 3 | 85 | | projection.MaximumValueLength, |
| | 3 | 86 | | projection.MaximumTotalBytes); |
| | | 87 | | } |
| | | 88 | | |
| | 3 | 89 | | private static JsonElement CloneJson(JsonElement value) => value.ValueKind == JsonValueKind.Undefined ? default : va |
| | | 90 | | } |