< Summary

Information
Class: Elsa.ExternalAuthentication.Endpoints.Connections.ConnectionResponse
Assembly: Elsa.ExternalAuthentication
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.ExternalAuthentication/Endpoints/Connections/ConnectionManagementModels.cs
Line coverage
100%
Covered lines: 90
Uncovered lines: 0
Coverable lines: 90
Total lines: 224
Line coverage: 100%
Branch coverage
84%
Covered branches: 22
Total branches: 26
Branch coverage: 84.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Id()100%11100%
get_Key()100%11100%
get_Source()100%11100%
get_Scope()100%11100%
get_AdapterType()100%11100%
get_CallbackUri()100%11100%
get_PreviewCallbackUri()100%11100%
get_AdapterSettingsVersion()100%11100%
get_AdapterSettings()100%11100%
get_SecretBindings()100%11100%
get_DisplayName()100%11100%
get_IconId()100%11100%
get_Order()100%11100%
get_IsPreferred()100%11100%
get_OverridesConfigurationConnection()100%11100%
get_CanCreateOverride()100%11100%
get_CanPromoteToConfigurationOverride()100%11100%
get_EnabledIntent()100%11100%
get_EffectivelyEnabled()100%11100%
get_Validity()100%11100%
get_Shadowed()100%11100%
get_ShadowedBy()100%11100%
get_Shadows()100%11100%
get_Archived()100%11100%
get_UnlinkedPolicy()100%11100%
get_PermissionGrantSources()100%11100%
get_ClaimProjection()100%11100%
get_UpstreamLogoutMode()100%11100%
get_Revision()100%11100%
get_MaterialRevision()100%11100%
get_LatestObservation()100%11100%
FromAsync()81.81%2222100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.ExternalAuthentication/Endpoints/Connections/ConnectionManagementModels.cs

#LineLine coverage
 1using System.Text.Json;
 2using System.Text.Json.Serialization;
 3using Elsa.ExternalAuthentication.Contracts;
 4using Elsa.ExternalAuthentication.Models;
 5using Elsa.ExternalAuthentication.Services;
 6
 7namespace Elsa.ExternalAuthentication.Endpoints.Connections;
 8
 9internal sealed class ConnectionScopeRequest
 10{
 11    public string? Kind { get; set; }
 12    public string? TenantId { get; set; }
 13}
 14
 15internal sealed class ConnectionRequest
 16{
 17    public string? Key { get; set; }
 18    public ConnectionScopeRequest? Scope { get; set; }
 19    public string? AdapterType { get; set; }
 20    public int AdapterSettingsVersion { get; set; }
 21    public JsonElement AdapterSettings { get; set; }
 22    // Accepted only to return a precise error for clients attempting to mutate
 23    // secret references through the general connection document.
 24    public Dictionary<string, SecretBinding>? SecretBindings { get; set; }
 25    public string? DisplayName { get; set; }
 26    public string? IconId { get; set; }
 27    public int Order { get; set; }
 28    public bool IsPreferred { get; set; }
 29    public bool OverridesConfigurationConnection { get; set; }
 30    public PolicySelection? UnlinkedPolicy { get; set; }
 31    public List<GrantSourceSelection>? PermissionGrantSources { get; set; }
 32    public ClaimProjectionRequest? ClaimProjection { get; set; }
 33    [JsonConverter(typeof(UpstreamLogoutModeJsonConverter))]
 34    public UpstreamLogoutMode UpstreamLogoutMode { get; set; }
 35    public bool ConfirmUnsafeSettings { get; set; }
 36    public bool ConfirmFinalLoginPathOverride { get; set; }
 37
 38    public bool HasOnlyHostScope() => Scope is null ||
 39        (string.IsNullOrWhiteSpace(Scope.TenantId) && (string.IsNullOrWhiteSpace(Scope.Kind) || string.Equals(Scope.Kind
 40
 41    public IdentityProviderConnection ToConnection() => new()
 42    {
 43        TenantId = ConnectionScope.HostTenantId,
 44        Key = Key ?? string.Empty,
 45        AdapterType = AdapterType ?? string.Empty,
 46        AdapterSettingsVersion = AdapterSettingsVersion,
 47        AdapterSettings = AdapterSettings.ValueKind == JsonValueKind.Undefined ? default : AdapterSettings.Clone(),
 48        SecretBindings = new Dictionary<string, SecretBinding>(StringComparer.Ordinal),
 49        DisplayName = DisplayName ?? string.Empty,
 50        IconId = IconId,
 51        DisplayOrder = Order,
 52        IsPreferred = IsPreferred,
 53        OverridesConfigurationConnection = OverridesConfigurationConnection,
 54        UnlinkedPolicy = UnlinkedPolicy,
 55        PermissionGrantSources = PermissionGrantSources?.Select(x => new GrantSourceSelection(x.Type, x.SettingsVersion,
 56        ClaimProjection = ClaimProjection?.ToProjection() ?? Elsa.ExternalAuthentication.Models.ClaimProjection.Empty,
 57        UpstreamLogoutMode = UpstreamLogoutMode
 58    };
 59}
 60
 61internal sealed class ClaimProjectionRequest
 62{
 63    public ICollection<string>? AllowedClaimTypes { get; set; }
 64    public ICollection<string>? RedactedClaimTypes { get; set; }
 65    public int MaximumClaimCount { get; set; }
 66    public int MaximumValueLength { get; set; }
 67    public int MaximumTotalBytes { get; set; }
 68
 69    public ClaimProjection ToProjection() => new(
 70        new HashSet<string>(AllowedClaimTypes ?? [], StringComparer.Ordinal),
 71        new HashSet<string>(RedactedClaimTypes ?? [], StringComparer.Ordinal),
 72        MaximumClaimCount,
 73        MaximumValueLength,
 74        MaximumTotalBytes);
 75}
 76
 77internal sealed record ConnectionSecretBindingResponse(string Ownership, string? ResolverType, string? Reference, bool I
 78
 79internal sealed class ManagedSecretBindingRequest
 80{
 81    public string? ResolverType { get; set; }
 82    public string? Value { get; set; }
 83}
 84
 85internal sealed record ConnectionScopeResponse(string Kind, string TenantId);
 86
 87internal sealed class ConnectionResponse
 88{
 6689    public string Id { get; init; } = null!;
 6690    public string Key { get; init; } = null!;
 6691    public string Source { get; init; } = null!;
 6692    public ConnectionScopeResponse Scope { get; init; } = null!;
 6693    public string AdapterType { get; init; } = null!;
 6694    public Uri? CallbackUri { get; init; }
 6695    public Uri? PreviewCallbackUri { get; init; }
 6696    public int AdapterSettingsVersion { get; init; }
 6697    public JsonElement AdapterSettings { get; init; }
 6698    public IReadOnlyDictionary<string, ConnectionSecretBindingResponse> SecretBindings { get; init; } = null!;
 6699    public string DisplayName { get; init; } = null!;
 66100    public string? IconId { get; init; }
 66101    public int Order { get; init; }
 66102    public bool IsPreferred { get; init; }
 66103    public bool OverridesConfigurationConnection { get; init; }
 66104    public bool CanCreateOverride { get; init; }
 66105    public bool CanPromoteToConfigurationOverride { get; init; }
 66106    public bool EnabledIntent { get; init; }
 66107    public bool EffectivelyEnabled { get; init; }
 66108    public string Validity { get; init; } = null!;
 66109    public bool Shadowed { get; init; }
 66110    public ConnectionReferenceResponse? ShadowedBy { get; init; }
 99111    public IReadOnlyCollection<ConnectionReferenceResponse> Shadows { get; init; } = [];
 66112    public bool Archived { get; init; }
 66113    public PolicySelection? UnlinkedPolicy { get; init; }
 99114    public IReadOnlyCollection<GrantSourceSelection> PermissionGrantSources { get; init; } = [];
 99115    public ClaimProjection ClaimProjection { get; init; } = ClaimProjection.Empty;
 116    [JsonConverter(typeof(UpstreamLogoutModeJsonConverter))]
 66117    public UpstreamLogoutMode UpstreamLogoutMode { get; init; }
 66118    public long Revision { get; init; }
 66119    public string MaterialRevision { get; init; } = null!;
 66120    public ConnectionObservationResponse? LatestObservation { get; init; }
 121
 122    public static async ValueTask<ConnectionResponse> FromAsync(EffectiveIdentityProviderConnection effective, Services.
 123    {
 33124        var states = await management.GetSecretBindingStatesAsync(effective.Connection, cancellationToken);
 33125        var adapterSettings = effective.Connection.AdapterSettings.ValueKind == JsonValueKind.Undefined
 33126            ? default
 33127            : adapters.TryGet(effective.Connection.AdapterType, out var adapter)
 33128                ? AdapterSettingsSecretFieldGuard.RedactDeclaredSecrets(effective.Connection.AdapterSettings, adapter.De
 33129                : JsonSerializer.SerializeToElement(new Dictionary<string, object?>());
 33130        return new ConnectionResponse
 33131        {
 33132            Id = effective.Connection.Id,
 33133            Key = effective.Connection.Key,
 33134            Source = effective.Ownership == ConnectionSourceOwnership.Configuration ? "configuration" : "database",
 33135            Scope = new ConnectionScopeResponse(effective.Scope.Kind switch { ConnectionScopeKind.Host => "host", Connec
 33136            AdapterType = effective.Connection.AdapterType,
 33137            CallbackUri = management.GetProviderCallbackUri(effective.Connection),
 33138            PreviewCallbackUri = management.GetProviderPreviewCallbackUri(effective.Connection),
 33139            AdapterSettingsVersion = effective.Connection.AdapterSettingsVersion,
 33140            AdapterSettings = adapterSettings,
 5141            SecretBindings = effective.Connection.SecretBindings.ToDictionary(x => x.Key, x =>
 33142            {
 5143                states.TryGetValue(x.Key, out var state);
 5144                var presentation = management.PresentSecretBinding(x.Value, state);
 5145                return new ConnectionSecretBindingResponse(
 5146                    presentation.Ownership,
 5147                    x.Value.Ownership == SecretBindingOwnership.External ? x.Value.ResolverType : null,
 5148                    x.Value.Ownership == SecretBindingOwnership.External ? x.Value.Reference : null,
 5149                    presentation.IsConfigured,
 5150                    presentation.IsResolvable);
 33151            }, StringComparer.Ordinal),
 33152            DisplayName = effective.Connection.DisplayName,
 33153            IconId = effective.Connection.IconId,
 33154            Order = effective.Connection.DisplayOrder,
 33155            IsPreferred = effective.Connection.IsPreferred,
 33156            OverridesConfigurationConnection = effective.Connection.OverridesConfigurationConnection,
 33157            CanCreateOverride = effective.Ownership == ConnectionSourceOwnership.Configuration && management.CanCreateCo
 33158            CanPromoteToConfigurationOverride = management.CanPromoteToConfigurationOverride(effective),
 33159            EnabledIntent = effective.Connection.IsEnabled,
 33160            EffectivelyEnabled = effective.Connection.IsEnabled && !effective.Connection.ArchivedAt.HasValue && !effecti
 33161            Validity = effective.Validity.ToString().ToLowerInvariant(),
 33162            Shadowed = effective.IsShadowed,
 33163            ShadowedBy = effective.ShadowedBy is null ? null : ConnectionReferenceResponse.From(effective.ShadowedBy),
 33164            Shadows = effective.Shadows.Select(ConnectionReferenceResponse.From).ToArray(),
 33165            Archived = effective.Connection.ArchivedAt.HasValue,
 33166            UnlinkedPolicy = effective.Connection.UnlinkedPolicy,
 33167            PermissionGrantSources = effective.Connection.PermissionGrantSources.ToArray(),
 33168            ClaimProjection = effective.Connection.ClaimProjection,
 33169            UpstreamLogoutMode = effective.Connection.UpstreamLogoutMode,
 33170            Revision = effective.Connection.Revision,
 33171            MaterialRevision = effective.Connection.MaterialRevision,
 33172            LatestObservation = observation is null
 33173                ? null
 33174                : new ConnectionObservationResponse(
 33175                    observation.Status.ToString().ToLowerInvariant(),
 33176                    observation.ObservedAt,
 33177                    observation.TestedMaterialRevision,
 33178                    !string.Equals(observation.TestedMaterialRevision, effective.Connection.MaterialRevision, StringComp
 33179                    observation.Category,
 33180                    observation.Summary)
 33181        };
 33182    }
 183
 184}
 185
 186internal sealed class UpstreamLogoutModeJsonConverter : JsonConverter<UpstreamLogoutMode>
 187{
 188    public override UpstreamLogoutMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options
 189    {
 190        if (reader.TokenType != JsonTokenType.String)
 191            throw new JsonException("The upstream logout mode must be a string.");
 192
 193        return reader.GetString()?.ToLowerInvariant() switch
 194        {
 195            "disabled" => UpstreamLogoutMode.Disabled,
 196            "userchoice" or "user-choice" or "user_choice" => UpstreamLogoutMode.UserChoice,
 197            "always" => UpstreamLogoutMode.Always,
 198            _ => throw new JsonException("The upstream logout mode is not supported.")
 199        };
 200    }
 201
 202    public override void Write(Utf8JsonWriter writer, UpstreamLogoutMode value, JsonSerializerOptions options) =>
 203        writer.WriteStringValue(value switch
 204        {
 205            UpstreamLogoutMode.Disabled => "disabled",
 206            UpstreamLogoutMode.UserChoice => "user-choice",
 207            UpstreamLogoutMode.Always => "always",
 208            _ => throw new JsonException("The upstream logout mode is not supported.")
 209        });
 210}
 211
 212internal sealed record ConnectionReferenceResponse(string Id, string DisplayName, string Source)
 213{
 214    public static ConnectionReferenceResponse From(IdentityProviderConnectionReference reference) =>
 215        new(
 216            reference.Id,
 217            reference.DisplayName,
 218            reference.Ownership == ConnectionSourceOwnership.Configuration ? "configuration" : "database");
 219}
 220
 221internal sealed record ConnectionObservationResponse(string Status, DateTimeOffset ObservedAt, string TestedMaterialRevi
 222internal sealed record ConnectionValidationResponse(bool Valid, IReadOnlyCollection<ConnectionValidationError> Errors, I
 223internal sealed record ConnectionListResponse(IReadOnlyCollection<ConnectionResponse> Items, string? NextCursor);
 224internal sealed record ManagementErrorResponse(string Error, string Message, object? Details, string CorrelationId);