Add unit tests for SBOM ingestion and transformation
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Implement `SbomIngestServiceCollectionExtensionsTests` to verify the SBOM ingestion pipeline exports snapshots correctly. - Create `SbomIngestTransformerTests` to ensure the transformation produces expected nodes and edges, including deduplication of license nodes and normalization of timestamps. - Add `SbomSnapshotExporterTests` to test the export functionality for manifest, adjacency, nodes, and edges. - Introduce `VexOverlayTransformerTests` to validate the transformation of VEX nodes and edges. - Set up project file for the test project with necessary dependencies and configurations. - Include JSON fixture files for testing purposes.
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Authority.Plugin.Ldap.Connections;
|
||||
using StellaOps.Authority.Plugin.Ldap.Credentials;
|
||||
using StellaOps.Authority.Plugin.Ldap.Monitoring;
|
||||
using StellaOps.Authority.Plugin.Ldap.Tests.Fakes;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap.Tests.Credentials;
|
||||
|
||||
public class LdapCredentialStoreTests
|
||||
{
|
||||
private const string PluginName = "corp-ldap";
|
||||
|
||||
[Fact]
|
||||
public async Task VerifyPasswordAsync_UsesUserDnFormatAndBindsSuccessfully()
|
||||
{
|
||||
var options = CreateBaseOptions();
|
||||
options.Connection.UserDnFormat = "uid={username},ou=people,dc=example,dc=internal";
|
||||
options.Connection.BindDn = "cn=service,dc=example,dc=internal";
|
||||
options.Connection.BindPasswordSecret = "service-secret";
|
||||
|
||||
var monitor = new StaticOptionsMonitor(options);
|
||||
var connection = new FakeLdapConnection();
|
||||
var bindCalls = new List<(string Dn, string Password)>();
|
||||
connection.OnBindAsync = (dn, pwd, ct) =>
|
||||
{
|
||||
bindCalls.Add((dn, pwd));
|
||||
return ValueTask.CompletedTask;
|
||||
};
|
||||
|
||||
var store = new LdapCredentialStore(
|
||||
PluginName,
|
||||
monitor,
|
||||
new FakeLdapConnectionFactory(connection),
|
||||
NullLogger<LdapCredentialStore>.Instance,
|
||||
new LdapMetrics(PluginName));
|
||||
|
||||
var result = await store.VerifyPasswordAsync("J.Doe", "Password1!", CancellationToken.None);
|
||||
|
||||
Assert.True(result.Succeeded);
|
||||
Assert.Equal(2, bindCalls.Count);
|
||||
Assert.Equal(options.Connection.BindDn, bindCalls[0].Dn);
|
||||
Assert.Equal("service-secret", bindCalls[0].Password);
|
||||
Assert.Equal("uid=j.doe,ou=people,dc=example,dc=internal", bindCalls[1].Dn);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task VerifyPasswordAsync_SearchesWhenFormatMissing()
|
||||
{
|
||||
var options = CreateBaseOptions();
|
||||
options.Connection.UserDnFormat = null;
|
||||
options.Connection.SearchBase = "ou=people,dc=example,dc=internal";
|
||||
options.Connection.UsernameAttribute = "uid";
|
||||
options.Queries.UserFilter = "(&(objectClass=person)(uid={username}))";
|
||||
|
||||
var monitor = new StaticOptionsMonitor(options);
|
||||
var connection = new FakeLdapConnection();
|
||||
connection.OnFindAsync = (baseDn, filter, attributes, ct) =>
|
||||
{
|
||||
Assert.Equal(options.Connection.SearchBase, baseDn);
|
||||
Assert.Contains("uid=j.doe", filter);
|
||||
var attr = new Dictionary<string, IReadOnlyList<string>>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["displayName"] = new List<string> { "John Doe" }
|
||||
};
|
||||
return ValueTask.FromResult<LdapSearchEntry?>(new LdapSearchEntry("uid=j.doe,ou=people,dc=example,dc=internal", attr));
|
||||
};
|
||||
|
||||
var userBindCount = 0;
|
||||
connection.OnBindAsync = (dn, pwd, ct) =>
|
||||
{
|
||||
userBindCount++;
|
||||
return ValueTask.CompletedTask;
|
||||
};
|
||||
|
||||
var store = new LdapCredentialStore(
|
||||
PluginName,
|
||||
monitor,
|
||||
new FakeLdapConnectionFactory(connection),
|
||||
NullLogger<LdapCredentialStore>.Instance,
|
||||
new LdapMetrics(PluginName));
|
||||
|
||||
var result = await store.VerifyPasswordAsync("J.Doe", "Password1!", CancellationToken.None);
|
||||
|
||||
Assert.True(result.Succeeded);
|
||||
Assert.NotNull(result.User);
|
||||
Assert.Equal("John Doe", result.User!.DisplayName);
|
||||
Assert.Equal(1, userBindCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task VerifyPasswordAsync_RetriesOnTransientFailure()
|
||||
{
|
||||
var options = CreateBaseOptions();
|
||||
options.Connection.UserDnFormat = "uid={username},ou=people,dc=example,dc=internal";
|
||||
|
||||
var monitor = new StaticOptionsMonitor(options);
|
||||
var connection = new FakeLdapConnection();
|
||||
var attempts = 0;
|
||||
connection.OnBindAsync = (dn, pwd, ct) =>
|
||||
{
|
||||
attempts++;
|
||||
if (attempts == 1)
|
||||
{
|
||||
throw new LdapTransientException("network failure");
|
||||
}
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
};
|
||||
|
||||
var store = new LdapCredentialStore(
|
||||
PluginName,
|
||||
monitor,
|
||||
new FakeLdapConnectionFactory(connection),
|
||||
NullLogger<LdapCredentialStore>.Instance,
|
||||
new LdapMetrics(PluginName),
|
||||
delayAsync: (_, _) => Task.CompletedTask);
|
||||
|
||||
var result = await store.VerifyPasswordAsync("jdoe", "Password1!", CancellationToken.None);
|
||||
|
||||
Assert.True(result.Succeeded);
|
||||
Assert.Equal(2, attempts);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task VerifyPasswordAsync_ReturnsFailureOnInvalidCredentials()
|
||||
{
|
||||
var options = CreateBaseOptions();
|
||||
options.Connection.UserDnFormat = "uid={username},ou=people,dc=example,dc=internal";
|
||||
|
||||
var monitor = new StaticOptionsMonitor(options);
|
||||
var connection = new FakeLdapConnection
|
||||
{
|
||||
OnBindAsync = (dn, pwd, ct) => ValueTask.FromException(new LdapAuthenticationException("invalid"))
|
||||
};
|
||||
|
||||
var store = new LdapCredentialStore(
|
||||
PluginName,
|
||||
monitor,
|
||||
new FakeLdapConnectionFactory(connection),
|
||||
NullLogger<LdapCredentialStore>.Instance,
|
||||
new LdapMetrics(PluginName),
|
||||
delayAsync: (_, _) => Task.CompletedTask);
|
||||
|
||||
var result = await store.VerifyPasswordAsync("jdoe", "bad", CancellationToken.None);
|
||||
|
||||
Assert.False(result.Succeeded);
|
||||
Assert.Equal(AuthorityCredentialFailureCode.InvalidCredentials, result.FailureCode);
|
||||
}
|
||||
|
||||
private static LdapPluginOptions CreateBaseOptions()
|
||||
{
|
||||
return new LdapPluginOptions
|
||||
{
|
||||
Connection = new LdapConnectionOptions
|
||||
{
|
||||
Host = "ldaps://ldap.example.internal",
|
||||
Port = 636,
|
||||
BindDn = null,
|
||||
BindPasswordSecret = null,
|
||||
UserDnFormat = "uid={username},ou=people,dc=example,dc=internal"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private sealed class StaticOptionsMonitor : IOptionsMonitor<LdapPluginOptions>
|
||||
{
|
||||
private readonly LdapPluginOptions value;
|
||||
|
||||
public StaticOptionsMonitor(LdapPluginOptions options)
|
||||
{
|
||||
value = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
public LdapPluginOptions CurrentValue => value;
|
||||
|
||||
public LdapPluginOptions Get(string? name) => value;
|
||||
|
||||
public IDisposable? OnChange(Action<LdapPluginOptions, string?> listener) => null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.Authority.Plugin.Ldap.Connections;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap.Tests.Fakes;
|
||||
|
||||
internal sealed class FakeLdapConnectionFactory : ILdapConnectionFactory
|
||||
{
|
||||
public FakeLdapConnectionFactory(FakeLdapConnection connection)
|
||||
{
|
||||
Connection = connection ?? throw new ArgumentNullException(nameof(connection));
|
||||
}
|
||||
|
||||
public FakeLdapConnection Connection { get; }
|
||||
|
||||
public ValueTask<ILdapConnectionHandle> CreateAsync(CancellationToken cancellationToken)
|
||||
=> ValueTask.FromResult<ILdapConnectionHandle>(Connection);
|
||||
}
|
||||
|
||||
internal sealed class FakeLdapConnection : ILdapConnectionHandle
|
||||
{
|
||||
private readonly List<string> operations = new();
|
||||
|
||||
public List<string> Operations => operations;
|
||||
|
||||
public Func<string, string, CancellationToken, ValueTask>? OnBindAsync { get; set; }
|
||||
|
||||
public Func<string, string, IReadOnlyCollection<string>, CancellationToken, ValueTask<LdapSearchEntry?>>? OnFindAsync { get; set; }
|
||||
|
||||
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
|
||||
|
||||
public ValueTask BindAsync(string distinguishedName, string password, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
operations.Add($"bind:{distinguishedName}");
|
||||
return OnBindAsync is null
|
||||
? ValueTask.CompletedTask
|
||||
: OnBindAsync(distinguishedName, password, cancellationToken);
|
||||
}
|
||||
|
||||
public ValueTask<LdapSearchEntry?> FindEntryAsync(string baseDn, string filter, IReadOnlyCollection<string> attributes, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
operations.Add($"search:{baseDn}:{filter}");
|
||||
return OnFindAsync is null
|
||||
? ValueTask.FromResult<LdapSearchEntry?>(null)
|
||||
: OnFindAsync(baseDn, filter, attributes, cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap.Tests;
|
||||
|
||||
public class LdapPluginOptionsTests : IDisposable
|
||||
{
|
||||
private readonly string tempRoot;
|
||||
|
||||
public LdapPluginOptionsTests()
|
||||
{
|
||||
tempRoot = Path.Combine(Path.GetTempPath(), "stellaops-ldap-plugin", Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(tempRoot);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Normalize_ResolvesRelativeClientCertificateAndBundlePaths()
|
||||
{
|
||||
var configPath = Path.Combine(tempRoot, "ldap.yaml");
|
||||
var options = new LdapPluginOptions
|
||||
{
|
||||
Connection = new LdapConnectionOptions
|
||||
{
|
||||
Host = "ldaps://ldap.internal",
|
||||
BindDn = "cn=service,dc=example,dc=internal",
|
||||
BindPasswordSecret = "file:/secrets/ldap-bind.txt",
|
||||
ClientCertificate = new LdapClientCertificateOptions
|
||||
{
|
||||
PfxPath = "../certs/ldap-client.pfx",
|
||||
PasswordSecret = "env:LDAP_CLIENT_PFX"
|
||||
},
|
||||
TrustStore = new LdapTrustStoreOptions
|
||||
{
|
||||
Mode = LdapTrustStoreMode.Bundle,
|
||||
BundlePath = "../trust/ldap-ca.pem"
|
||||
},
|
||||
UserDnFormat = "uid={username},ou=people,dc=example,dc=internal"
|
||||
}
|
||||
};
|
||||
|
||||
options.Normalize(configPath);
|
||||
|
||||
var expectedCert = Path.GetFullPath(Path.Combine(tempRoot, "../certs/ldap-client.pfx"));
|
||||
var expectedBundle = Path.GetFullPath(Path.Combine(tempRoot, "../trust/ldap-ca.pem"));
|
||||
|
||||
Assert.Equal(expectedCert, options.Connection.ClientCertificate!.PfxPath);
|
||||
Assert.Equal(expectedBundle, options.Connection.TrustStore.BundlePath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenHostMissing()
|
||||
{
|
||||
var options = new LdapPluginOptions
|
||||
{
|
||||
Connection = new LdapConnectionOptions
|
||||
{
|
||||
BindDn = "cn=service,dc=example,dc=internal",
|
||||
BindPasswordSecret = "secret",
|
||||
UserDnFormat = "uid={username},ou=people,dc=example,dc=internal"
|
||||
}
|
||||
};
|
||||
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => options.Validate("corp-ldap"));
|
||||
Assert.Contains("connection.host", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenBundleModeWithoutPath()
|
||||
{
|
||||
var options = new LdapPluginOptions
|
||||
{
|
||||
Connection = new LdapConnectionOptions
|
||||
{
|
||||
Host = "ldaps://ldap.internal",
|
||||
BindDn = "cn=service,dc=example,dc=internal",
|
||||
BindPasswordSecret = "secret",
|
||||
TrustStore = new LdapTrustStoreOptions
|
||||
{
|
||||
Mode = LdapTrustStoreMode.Bundle,
|
||||
BundlePath = null
|
||||
},
|
||||
UserDnFormat = "uid={username},ou=people,dc=example,dc=internal"
|
||||
}
|
||||
};
|
||||
|
||||
options.Normalize(Path.Combine(tempRoot, "ldap.yaml"));
|
||||
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => options.Validate("corp-ldap"));
|
||||
Assert.Contains("connection.trustStore.bundlePath", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenClientCertificateIncomplete()
|
||||
{
|
||||
var options = new LdapPluginOptions
|
||||
{
|
||||
Connection = new LdapConnectionOptions
|
||||
{
|
||||
Host = "ldaps://ldap.internal",
|
||||
BindDn = "cn=service,dc=example,dc=internal",
|
||||
BindPasswordSecret = "secret",
|
||||
ClientCertificate = new LdapClientCertificateOptions
|
||||
{
|
||||
PasswordSecret = "env:LDAP_PFX"
|
||||
},
|
||||
UserDnFormat = "uid={username},ou=people,dc=example,dc=internal"
|
||||
}
|
||||
};
|
||||
|
||||
options.Normalize(Path.Combine(tempRoot, "ldap.yaml"));
|
||||
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => options.Validate("corp-ldap"));
|
||||
Assert.Contains("clientCertificate.pfxPath", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenTlsDisabledWithoutEnvToggle()
|
||||
{
|
||||
var options = ValidOptions();
|
||||
options.Security.RequireTls = false;
|
||||
options.Security.AllowInsecureWithEnvToggle = false;
|
||||
|
||||
options.Normalize(Path.Combine(tempRoot, "ldap.yaml"));
|
||||
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => options.Validate("corp-ldap"));
|
||||
Assert.Contains("allowInsecureWithEnvToggle", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_Throws_WhenTlsDisabledWithoutEnvironmentVariable()
|
||||
{
|
||||
var options = ValidOptions();
|
||||
options.Security.RequireTls = false;
|
||||
options.Security.AllowInsecureWithEnvToggle = true;
|
||||
|
||||
options.Normalize(Path.Combine(tempRoot, "ldap.yaml"));
|
||||
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => options.Validate("corp-ldap"));
|
||||
Assert.Contains(LdapSecurityOptions.AllowInsecureEnvironmentVariable, ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_AllowsTlsDisabledWhenEnvToggleSet()
|
||||
{
|
||||
const string envVar = "STELLAOPS_LDAP_ALLOW_INSECURE";
|
||||
var original = Environment.GetEnvironmentVariable(envVar);
|
||||
try
|
||||
{
|
||||
Environment.SetEnvironmentVariable(envVar, "true");
|
||||
|
||||
var options = ValidOptions();
|
||||
options.Security.RequireTls = false;
|
||||
options.Security.AllowInsecureWithEnvToggle = true;
|
||||
|
||||
options.Normalize(Path.Combine(tempRoot, "ldap.yaml"));
|
||||
options.Validate("corp-ldap");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Environment.SetEnvironmentVariable(envVar, original);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Normalize_DeduplicatesCipherSuites()
|
||||
{
|
||||
var options = ValidOptions();
|
||||
options.Security.AllowedCipherSuites = new[] { "TLS_AES_256_GCM_SHA384", " tls_aes_256_gcm_sha384 ", "TLS_AES_128_GCM_SHA256" };
|
||||
|
||||
options.Normalize(Path.Combine(tempRoot, "ldap.yaml"));
|
||||
|
||||
Assert.Collection(
|
||||
options.Security.AllowedCipherSuites,
|
||||
item => Assert.Equal("TLS_AES_256_GCM_SHA384", item),
|
||||
item => Assert.Equal("TLS_AES_128_GCM_SHA256", item));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Registrar_BindsOptionsAndAppliesNormalization()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var pluginName = "corp-ldap";
|
||||
var configPath = Path.Combine(tempRoot, "plugins", "ldap", "corp.yaml");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(configPath)!);
|
||||
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["connection:host"] = "ldaps://ldap.example.internal",
|
||||
["connection:port"] = "389",
|
||||
["connection:bindDn"] = "cn=service,dc=example,dc=internal",
|
||||
["connection:bindPasswordSecret"] = "secret:ldap/service",
|
||||
["connection:clientCertificate:pfxPath"] = "../certs/ldap-client.pfx",
|
||||
["connection:clientCertificate:passwordSecret"] = "secret:ldap/client-pfx",
|
||||
["connection:trustStore:mode"] = "bundle",
|
||||
["connection:trustStore:bundlePath"] = "../trust/ca.pem",
|
||||
["connection:userDnFormat"] = "uid={username},ou=people,dc=example,dc=internal",
|
||||
["security:allowedCipherSuites:0"] = "TLS_AES_256_GCM_SHA384"
|
||||
})
|
||||
.Build();
|
||||
|
||||
var manifest = new AuthorityPluginManifest(
|
||||
pluginName,
|
||||
Type: "ldap",
|
||||
Enabled: true,
|
||||
AssemblyName: null,
|
||||
AssemblyPath: null,
|
||||
Capabilities: Array.Empty<string>(),
|
||||
Metadata: new Dictionary<string, string?>(),
|
||||
ConfigPath: configPath);
|
||||
|
||||
var registrar = new LdapPluginRegistrar();
|
||||
registrar.Register(new AuthorityPluginRegistrationContext(
|
||||
services,
|
||||
new AuthorityPluginContext(manifest, configuration),
|
||||
new ConfigurationBuilder().Build()));
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
var monitor = provider.GetRequiredService<IOptionsMonitor<LdapPluginOptions>>();
|
||||
var options = monitor.Get(pluginName);
|
||||
|
||||
Assert.Equal(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(configPath)!, "../certs/ldap-client.pfx")), options.Connection.ClientCertificate!.PfxPath);
|
||||
Assert.Equal(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(configPath)!, "../trust/ca.pem")), options.Connection.TrustStore.BundlePath);
|
||||
Assert.Equal("TLS_AES_256_GCM_SHA384", Assert.Single(options.Security.AllowedCipherSuites));
|
||||
}
|
||||
|
||||
private static LdapPluginOptions ValidOptions()
|
||||
{
|
||||
return new LdapPluginOptions
|
||||
{
|
||||
Connection = new LdapConnectionOptions
|
||||
{
|
||||
Host = "ldaps://ldap.internal",
|
||||
BindDn = "cn=service,dc=example,dc=internal",
|
||||
BindPasswordSecret = "secret",
|
||||
UserDnFormat = "uid={username},ou=people,dc=example,dc=internal"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(tempRoot))
|
||||
{
|
||||
Directory.Delete(tempRoot, recursive: true);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// swallow cleanup failures in tests
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\\StellaOps.Authority.Plugin.Ldap\\StellaOps.Authority.Plugin.Ldap.csproj" />
|
||||
<ProjectReference Include="..\\StellaOps.Authority.Plugins.Abstractions\\StellaOps.Authority.Plugins.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap.Claims;
|
||||
|
||||
internal sealed class LdapClaimsEnricher : IClaimsEnricher
|
||||
{
|
||||
public ValueTask EnrichAsync(
|
||||
ClaimsIdentity identity,
|
||||
AuthorityClaimsEnrichmentContext context,
|
||||
CancellationToken cancellationToken)
|
||||
=> ValueTask.CompletedTask;
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Authority.Plugin.Ldap.Monitoring;
|
||||
using StellaOps.Authority.Plugin.Ldap.Security;
|
||||
using System.DirectoryServices.Protocols;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap.Connections;
|
||||
|
||||
internal sealed class DirectoryServicesLdapConnectionFactory : ILdapConnectionFactory
|
||||
{
|
||||
private readonly string pluginName;
|
||||
private readonly IOptionsMonitor<LdapPluginOptions> optionsMonitor;
|
||||
private readonly ILogger<DirectoryServicesLdapConnectionFactory> logger;
|
||||
private readonly LdapMetrics metrics;
|
||||
|
||||
public DirectoryServicesLdapConnectionFactory(
|
||||
string pluginName,
|
||||
IOptionsMonitor<LdapPluginOptions> optionsMonitor,
|
||||
ILogger<DirectoryServicesLdapConnectionFactory> logger,
|
||||
LdapMetrics metrics)
|
||||
{
|
||||
this.pluginName = pluginName ?? throw new ArgumentNullException(nameof(pluginName));
|
||||
this.optionsMonitor = optionsMonitor ?? throw new ArgumentNullException(nameof(optionsMonitor));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
this.metrics = metrics ?? throw new ArgumentNullException(nameof(metrics));
|
||||
}
|
||||
|
||||
public ValueTask<ILdapConnectionHandle> CreateAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var options = optionsMonitor.Get(pluginName);
|
||||
var identifier = new LdapDirectoryIdentifier(options.Connection.Host!, options.Connection.Port, fullyQualifiedDnsHostName: false, connectionless: false);
|
||||
var connection = new LdapConnection(identifier)
|
||||
{
|
||||
Timeout = TimeSpan.FromSeconds(10)
|
||||
};
|
||||
|
||||
ConfigureCertificateValidation(connection, options);
|
||||
ConfigureClientCertificate(connection, options);
|
||||
|
||||
if (options.Connection.UseStartTls)
|
||||
{
|
||||
connection.SessionOptions.StartTransportLayerSecurity(null);
|
||||
}
|
||||
else if (options.Connection.Port == 636)
|
||||
{
|
||||
connection.SessionOptions.SecureSocketLayer = true;
|
||||
}
|
||||
|
||||
return ValueTask.FromResult<ILdapConnectionHandle>(new DirectoryServicesLdapConnectionHandle(connection, logger, metrics));
|
||||
}
|
||||
|
||||
private static void ConfigureCertificateValidation(LdapConnection connection, LdapPluginOptions options)
|
||||
{
|
||||
if (!options.Connection.ValidateCertificates)
|
||||
{
|
||||
connection.SessionOptions.VerifyServerCertificate += (_, _) => true;
|
||||
return;
|
||||
}
|
||||
|
||||
X509Certificate2Collection? customRoots = null;
|
||||
if (options.Connection.TrustStore.Mode == LdapTrustStoreMode.Bundle && !string.IsNullOrWhiteSpace(options.Connection.TrustStore.BundlePath))
|
||||
{
|
||||
customRoots = LoadBundle(options.Connection.TrustStore.BundlePath!);
|
||||
}
|
||||
|
||||
connection.SessionOptions.VerifyServerCertificate += (_, certificate) =>
|
||||
{
|
||||
if (certificate is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
using var cert2 = new X509Certificate2(certificate);
|
||||
using var chain = new X509Chain
|
||||
{
|
||||
ChainPolicy =
|
||||
{
|
||||
RevocationMode = X509RevocationMode.NoCheck,
|
||||
VerificationFlags = X509VerificationFlags.NoFlag
|
||||
}
|
||||
};
|
||||
|
||||
if (customRoots is not null)
|
||||
{
|
||||
foreach (var root in customRoots)
|
||||
{
|
||||
chain.ChainPolicy.CustomTrustStore.Add(root);
|
||||
}
|
||||
|
||||
chain.ChainPolicy.VerificationFlags |= X509VerificationFlags.AllowUnknownCertificateAuthority;
|
||||
}
|
||||
|
||||
return chain.Build(cert2);
|
||||
};
|
||||
}
|
||||
|
||||
private static void ConfigureClientCertificate(LdapConnection connection, LdapPluginOptions options)
|
||||
{
|
||||
var clientCertificateOptions = options.Connection.ClientCertificate;
|
||||
if (clientCertificateOptions is null || !clientCertificateOptions.IsConfigured)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(clientCertificateOptions.PfxPath))
|
||||
{
|
||||
throw new InvalidOperationException("Client certificate PFX path must be configured when enabling client certificates.");
|
||||
}
|
||||
|
||||
var password = LdapSecretResolver.Resolve(clientCertificateOptions.PasswordSecret);
|
||||
var certificate = X509CertificateLoader.LoadPkcs12FromFile(
|
||||
clientCertificateOptions.PfxPath,
|
||||
password,
|
||||
X509KeyStorageFlags.EphemeralKeySet);
|
||||
connection.ClientCertificates.Add(certificate);
|
||||
}
|
||||
|
||||
private static X509Certificate2Collection LoadBundle(string path)
|
||||
{
|
||||
var collection = new X509Certificate2Collection();
|
||||
if (path.EndsWith(".pem", StringComparison.OrdinalIgnoreCase) || path.EndsWith(".crt", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
collection.ImportFromPemFile(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
var certificate = X509CertificateLoader.LoadPkcs12FromFile(path, password: null, X509KeyStorageFlags.EphemeralKeySet);
|
||||
collection.Add(certificate);
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class DirectoryServicesLdapConnectionHandle : ILdapConnectionHandle
|
||||
{
|
||||
private readonly LdapConnection connection;
|
||||
private readonly ILogger logger;
|
||||
private readonly LdapMetrics metrics;
|
||||
private const int InvalidCredentialsResultCode = 49;
|
||||
private const int ServerDownResultCode = 81;
|
||||
private const int TimeLimitExceededResultCode = 3;
|
||||
private const int BusyResultCode = 51;
|
||||
private const int UnavailableResultCode = 52;
|
||||
|
||||
public DirectoryServicesLdapConnectionHandle(
|
||||
LdapConnection connection,
|
||||
ILogger logger,
|
||||
LdapMetrics metrics)
|
||||
{
|
||||
this.connection = connection ?? throw new ArgumentNullException(nameof(connection));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
this.metrics = metrics ?? throw new ArgumentNullException(nameof(metrics));
|
||||
}
|
||||
|
||||
public ValueTask DisposeAsync()
|
||||
{
|
||||
connection.Dispose();
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
public ValueTask BindAsync(string distinguishedName, string password, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
metrics.RecordBindAttempt();
|
||||
|
||||
try
|
||||
{
|
||||
connection.Bind(new NetworkCredential(distinguishedName, password));
|
||||
metrics.RecordBindSuccess();
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
catch (LdapException ex) when (IsInvalidCredentials(ex))
|
||||
{
|
||||
metrics.RecordBindFailure();
|
||||
throw new LdapAuthenticationException($"Invalid credentials for '{distinguishedName}'.", ex);
|
||||
}
|
||||
catch (LdapException ex) when (IsTransient(ex))
|
||||
{
|
||||
metrics.RecordBindFailure();
|
||||
throw new LdapTransientException($"Transient bind failure for '{distinguishedName}'.", ex);
|
||||
}
|
||||
catch (LdapException ex)
|
||||
{
|
||||
metrics.RecordBindFailure();
|
||||
throw new LdapOperationException($"LDAP bind failure ({FormatResult(ex.ErrorCode)}).", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public ValueTask<LdapSearchEntry?> FindEntryAsync(string baseDn, string filter, IReadOnlyCollection<string> attributes, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
metrics.RecordSearchAttempt();
|
||||
|
||||
try
|
||||
{
|
||||
var request = new SearchRequest(baseDn, filter, SearchScope.Subtree, attributes?.ToArray());
|
||||
var response = (SearchResponse)connection.SendRequest(request);
|
||||
|
||||
if (response.Entries.Count == 0)
|
||||
{
|
||||
metrics.RecordSearchMiss();
|
||||
return ValueTask.FromResult<LdapSearchEntry?>(null);
|
||||
}
|
||||
|
||||
var entry = response.Entries[0];
|
||||
var attributeDictionary = new Dictionary<string, IReadOnlyList<string>>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (string attributeName in entry.Attributes.AttributeNames)
|
||||
{
|
||||
var attribute = entry.Attributes[attributeName];
|
||||
var values = attribute?.GetValues(typeof(string))?.Cast<string>().ToArray() ?? Array.Empty<string>();
|
||||
attributeDictionary[attributeName] = values;
|
||||
}
|
||||
|
||||
metrics.RecordSearchHit();
|
||||
return ValueTask.FromResult<LdapSearchEntry?>(new LdapSearchEntry(entry.DistinguishedName, attributeDictionary));
|
||||
}
|
||||
catch (LdapException ex) when (IsTransient(ex))
|
||||
{
|
||||
metrics.RecordSearchFailure();
|
||||
throw new LdapTransientException("Transient LDAP search failure.", ex);
|
||||
}
|
||||
catch (LdapException ex)
|
||||
{
|
||||
metrics.RecordSearchFailure();
|
||||
logger.LogWarning(ex, "LDAP search failure ({Result}).", FormatResult(ex.ErrorCode));
|
||||
throw new LdapOperationException($"LDAP search failure ({FormatResult(ex.ErrorCode)}).", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsInvalidCredentials(LdapException ex)
|
||||
=> ex.ErrorCode == InvalidCredentialsResultCode;
|
||||
|
||||
private static bool IsTransient(LdapException ex)
|
||||
=> ex.ErrorCode is ServerDownResultCode
|
||||
or TimeLimitExceededResultCode
|
||||
or BusyResultCode
|
||||
or UnavailableResultCode;
|
||||
|
||||
private static string FormatResult(int errorCode)
|
||||
=> errorCode switch
|
||||
{
|
||||
InvalidCredentialsResultCode => "InvalidCredentials (49)",
|
||||
ServerDownResultCode => "ServerDown (81)",
|
||||
TimeLimitExceededResultCode => "TimeLimitExceeded (3)",
|
||||
BusyResultCode => "Busy (51)",
|
||||
UnavailableResultCode => "Unavailable (52)",
|
||||
_ => errorCode.ToString(CultureInfo.InvariantCulture)
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap.Connections;
|
||||
|
||||
internal interface ILdapConnectionFactory
|
||||
{
|
||||
ValueTask<ILdapConnectionHandle> CreateAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
internal interface ILdapConnectionHandle : IAsyncDisposable
|
||||
{
|
||||
ValueTask BindAsync(string distinguishedName, string password, CancellationToken cancellationToken);
|
||||
|
||||
ValueTask<LdapSearchEntry?> FindEntryAsync(string baseDn, string filter, IReadOnlyCollection<string> attributes, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
internal sealed record LdapSearchEntry(string DistinguishedName, IReadOnlyDictionary<string, IReadOnlyList<string>> Attributes);
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap.Connections;
|
||||
|
||||
internal class LdapAuthenticationException : Exception
|
||||
{
|
||||
public LdapAuthenticationException(string message, Exception? innerException = null)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal class LdapTransientException : Exception
|
||||
{
|
||||
public LdapTransientException(string message, Exception? innerException = null)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal class LdapOperationException : Exception
|
||||
{
|
||||
public LdapOperationException(string message, Exception? innerException = null)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.Plugin.Ldap.Connections;
|
||||
using StellaOps.Authority.Plugin.Ldap.Monitoring;
|
||||
using StellaOps.Authority.Plugin.Ldap.Security;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap.Credentials;
|
||||
|
||||
internal sealed class LdapCredentialStore : IUserCredentialStore
|
||||
{
|
||||
private static readonly TimeSpan BaseDelay = TimeSpan.FromMilliseconds(150);
|
||||
private const int MaxAttempts = 3;
|
||||
|
||||
private readonly string pluginName;
|
||||
private readonly IOptionsMonitor<LdapPluginOptions> optionsMonitor;
|
||||
private readonly ILdapConnectionFactory connectionFactory;
|
||||
private readonly ILogger<LdapCredentialStore> logger;
|
||||
private readonly LdapMetrics metrics;
|
||||
private readonly Func<TimeSpan, CancellationToken, Task> delayAsync;
|
||||
|
||||
public LdapCredentialStore(
|
||||
string pluginName,
|
||||
IOptionsMonitor<LdapPluginOptions> optionsMonitor,
|
||||
ILdapConnectionFactory connectionFactory,
|
||||
ILogger<LdapCredentialStore> logger,
|
||||
LdapMetrics metrics,
|
||||
Func<TimeSpan, CancellationToken, Task>? delayAsync = null)
|
||||
{
|
||||
this.pluginName = pluginName ?? throw new ArgumentNullException(nameof(pluginName));
|
||||
this.optionsMonitor = optionsMonitor ?? throw new ArgumentNullException(nameof(optionsMonitor));
|
||||
this.connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
this.metrics = metrics ?? throw new ArgumentNullException(nameof(metrics));
|
||||
this.delayAsync = delayAsync ?? ((delay, token) => Task.Delay(delay, token));
|
||||
}
|
||||
|
||||
public async ValueTask<AuthorityCredentialVerificationResult> VerifyPasswordAsync(
|
||||
string username,
|
||||
string password,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var auditProperties = new List<AuthEventProperty>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrEmpty(password))
|
||||
{
|
||||
return AuthorityCredentialVerificationResult.Failure(
|
||||
AuthorityCredentialFailureCode.InvalidCredentials,
|
||||
"Invalid credentials.",
|
||||
auditProperties: auditProperties);
|
||||
}
|
||||
|
||||
var normalizedUsername = NormalizeUsername(username);
|
||||
var options = optionsMonitor.Get(pluginName);
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await connectionFactory.CreateAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await EnsureServiceBindAsync(connection, options, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var userEntry = await ResolveUserEntryAsync(
|
||||
connection,
|
||||
options,
|
||||
normalizedUsername,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (userEntry is null)
|
||||
{
|
||||
logger.LogWarning("LDAP plugin {Plugin} could not find user {Username}.", pluginName, normalizedUsername);
|
||||
return AuthorityCredentialVerificationResult.Failure(
|
||||
AuthorityCredentialFailureCode.InvalidCredentials,
|
||||
"Invalid credentials.",
|
||||
auditProperties: auditProperties);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await ExecuteWithRetryAsync<bool>(
|
||||
"user_bind",
|
||||
async ct =>
|
||||
{
|
||||
await connection.BindAsync(userEntry.DistinguishedName, password, ct).ConfigureAwait(false);
|
||||
return true;
|
||||
},
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (LdapAuthenticationException)
|
||||
{
|
||||
logger.LogWarning("LDAP plugin {Plugin} received invalid credentials for {Username}.", pluginName, normalizedUsername);
|
||||
return AuthorityCredentialVerificationResult.Failure(
|
||||
AuthorityCredentialFailureCode.InvalidCredentials,
|
||||
"Invalid credentials.",
|
||||
auditProperties: auditProperties);
|
||||
}
|
||||
|
||||
var descriptor = BuildDescriptor(userEntry, normalizedUsername, passwordRequiresReset: false);
|
||||
return AuthorityCredentialVerificationResult.Success(descriptor, auditProperties: auditProperties);
|
||||
}
|
||||
catch (LdapTransientException ex)
|
||||
{
|
||||
logger.LogWarning(ex, "LDAP plugin {Plugin} experienced transient failure when verifying user {Username}.", pluginName, normalizedUsername);
|
||||
return AuthorityCredentialVerificationResult.Failure(
|
||||
AuthorityCredentialFailureCode.UnknownError,
|
||||
"Authentication service temporarily unavailable.",
|
||||
retryAfter: TimeSpan.FromSeconds(5),
|
||||
auditProperties: auditProperties);
|
||||
}
|
||||
catch (LdapOperationException ex)
|
||||
{
|
||||
logger.LogError(ex, "LDAP plugin {Plugin} failed to verify user {Username} due to an LDAP error.", pluginName, normalizedUsername);
|
||||
return AuthorityCredentialVerificationResult.Failure(
|
||||
AuthorityCredentialFailureCode.UnknownError,
|
||||
"Authentication service error.",
|
||||
auditProperties: auditProperties);
|
||||
}
|
||||
}
|
||||
|
||||
public ValueTask<AuthorityPluginOperationResult<AuthorityUserDescriptor>> UpsertUserAsync(
|
||||
AuthorityUserRegistration registration,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return ValueTask.FromResult(AuthorityPluginOperationResult<AuthorityUserDescriptor>.Failure(
|
||||
"not_supported",
|
||||
"LDAP identity provider does not support provisioning users."));
|
||||
}
|
||||
|
||||
public ValueTask<AuthorityUserDescriptor?> FindBySubjectAsync(string subjectId, CancellationToken cancellationToken)
|
||||
{
|
||||
_ = subjectId;
|
||||
_ = cancellationToken;
|
||||
return ValueTask.FromResult<AuthorityUserDescriptor?>(null);
|
||||
}
|
||||
|
||||
private async Task EnsureServiceBindAsync(ILdapConnectionHandle connection, LdapPluginOptions options, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(options.Connection.BindDn))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var secret = LdapSecretResolver.Resolve(options.Connection.BindPasswordSecret);
|
||||
await ExecuteWithRetryAsync<bool>(
|
||||
"service_bind",
|
||||
async ct =>
|
||||
{
|
||||
await connection.BindAsync(options.Connection.BindDn!, secret, ct).ConfigureAwait(false);
|
||||
return true;
|
||||
},
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<LdapSearchEntry?> ResolveUserEntryAsync(
|
||||
ILdapConnectionHandle connection,
|
||||
LdapPluginOptions options,
|
||||
string normalizedUsername,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(options.Connection.UserDnFormat))
|
||||
{
|
||||
var dn = BuildUserDistinguishedName(options.Connection.UserDnFormat!, normalizedUsername);
|
||||
return new LdapSearchEntry(dn, new Dictionary<string, IReadOnlyList<string>>(StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
var searchBase = options.Connection.SearchBase;
|
||||
var usernameAttribute = options.Connection.UsernameAttribute;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(searchBase) || string.IsNullOrWhiteSpace(usernameAttribute))
|
||||
{
|
||||
logger.LogError(
|
||||
"LDAP plugin {Plugin} missing searchBase/usernameAttribute configuration for user {Username} lookup.",
|
||||
pluginName,
|
||||
normalizedUsername);
|
||||
return null;
|
||||
}
|
||||
|
||||
var filter = BuildUserFilter(options, normalizedUsername);
|
||||
var attributes = options.Queries.Attributes.Length > 0
|
||||
? options.Queries.Attributes
|
||||
: new[] { "displayName", "cn", "mail" };
|
||||
|
||||
return await ExecuteWithRetryAsync(
|
||||
"lookup",
|
||||
ct => connection.FindEntryAsync(searchBase, filter, attributes, ct),
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<T> ExecuteWithRetryAsync<T>(
|
||||
string operation,
|
||||
Func<CancellationToken, ValueTask<T>> action,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var attempt = 0;
|
||||
Exception? lastException = null;
|
||||
|
||||
while (attempt < MaxAttempts)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
return await action(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (LdapTransientException ex)
|
||||
{
|
||||
lastException = ex;
|
||||
attempt++;
|
||||
metrics.RecordRetry();
|
||||
|
||||
if (attempt >= MaxAttempts)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var delay = TimeSpan.FromMilliseconds(BaseDelay.TotalMilliseconds * Math.Pow(2, attempt - 1));
|
||||
logger.LogWarning(ex, "LDAP operation {Operation} transient failure (attempt {Attempt}/{MaxAttempts}).", operation, attempt, MaxAttempts);
|
||||
await delayAsync(delay, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
throw new LdapTransientException($"LDAP operation '{operation}' failed after {MaxAttempts} attempts.", lastException);
|
||||
}
|
||||
|
||||
private static string NormalizeUsername(string username)
|
||||
=> username.Trim().ToLowerInvariant();
|
||||
|
||||
private static string BuildUserDistinguishedName(string template, string username)
|
||||
=> template.Replace("{username}", EscapeDnValue(username), StringComparison.Ordinal);
|
||||
|
||||
private static string EscapeDnValue(string value)
|
||||
{
|
||||
var needsEscape = value.Any(static ch => ch is ',' or '+' or '"' or '\\' or '<' or '>' or ';' or '#' or '=' || char.IsWhiteSpace(ch));
|
||||
if (!needsEscape)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return value.Replace("\\", "\\\\", StringComparison.Ordinal)
|
||||
.Replace(",", "\\,", StringComparison.Ordinal)
|
||||
.Replace("+", "\\+", StringComparison.Ordinal)
|
||||
.Replace("\"", "\\\"", StringComparison.Ordinal)
|
||||
.Replace("<", "\\<", StringComparison.Ordinal)
|
||||
.Replace(">", "\\>", StringComparison.Ordinal)
|
||||
.Replace(";", "\\;", StringComparison.Ordinal)
|
||||
.Replace("#", "\\#", StringComparison.Ordinal)
|
||||
.Replace("=", "\\=", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static string BuildUserFilter(LdapPluginOptions options, string username)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(options.Queries.UserFilter))
|
||||
{
|
||||
return options.Queries.UserFilter.Replace("{username}", EscapeFilterValue(username), StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
var attribute = options.Connection.UsernameAttribute ?? "uid";
|
||||
return $"({attribute}={EscapeFilterValue(username)})";
|
||||
}
|
||||
|
||||
private static string EscapeFilterValue(string value)
|
||||
{
|
||||
Span<char> buffer = stackalloc char[value.Length * 3];
|
||||
var index = 0;
|
||||
|
||||
foreach (var ch in value)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case '\\':
|
||||
buffer[index++] = '\\';
|
||||
buffer[index++] = '5';
|
||||
buffer[index++] = 'c';
|
||||
break;
|
||||
case '*':
|
||||
buffer[index++] = '\\';
|
||||
buffer[index++] = '2';
|
||||
buffer[index++] = 'a';
|
||||
break;
|
||||
case '(':
|
||||
buffer[index++] = '\\';
|
||||
buffer[index++] = '2';
|
||||
buffer[index++] = '8';
|
||||
break;
|
||||
case ')':
|
||||
buffer[index++] = '\\';
|
||||
buffer[index++] = '2';
|
||||
buffer[index++] = '9';
|
||||
break;
|
||||
case '\0':
|
||||
buffer[index++] = '\\';
|
||||
buffer[index++] = '0';
|
||||
buffer[index++] = '0';
|
||||
break;
|
||||
default:
|
||||
buffer[index++] = ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new string(buffer[..index]);
|
||||
}
|
||||
|
||||
private AuthorityUserDescriptor BuildDescriptor(LdapSearchEntry entry, string normalizedUsername, bool passwordRequiresReset)
|
||||
{
|
||||
var attributes = entry.Attributes;
|
||||
string? displayName = null;
|
||||
|
||||
if (attributes.TryGetValue("displayName", out var displayValues) && displayValues.Count > 0)
|
||||
{
|
||||
displayName = displayValues[0];
|
||||
}
|
||||
else if (attributes.TryGetValue("cn", out var cnValues) && cnValues.Count > 0)
|
||||
{
|
||||
displayName = cnValues[0];
|
||||
}
|
||||
|
||||
var attributeSnapshot = attributes.ToDictionary(
|
||||
pair => pair.Key,
|
||||
pair => (string?)string.Join(",", pair.Value),
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
return new AuthorityUserDescriptor(
|
||||
entry.DistinguishedName,
|
||||
normalizedUsername,
|
||||
displayName,
|
||||
passwordRequiresReset,
|
||||
Array.Empty<string>(),
|
||||
attributeSnapshot);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.Plugin.Ldap.Claims;
|
||||
using StellaOps.Authority.Plugin.Ldap.Connections;
|
||||
using StellaOps.Authority.Plugin.Ldap.Credentials;
|
||||
using StellaOps.Authority.Plugin.Ldap.Security;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap;
|
||||
|
||||
internal sealed class LdapIdentityProviderPlugin : IIdentityProviderPlugin
|
||||
{
|
||||
private readonly AuthorityPluginContext pluginContext;
|
||||
private readonly LdapCredentialStore credentialStore;
|
||||
private readonly LdapClaimsEnricher claimsEnricher;
|
||||
private readonly ILdapConnectionFactory connectionFactory;
|
||||
private readonly IOptionsMonitor<LdapPluginOptions> optionsMonitor;
|
||||
private readonly ILogger<LdapIdentityProviderPlugin> logger;
|
||||
|
||||
private readonly AuthorityIdentityProviderCapabilities capabilities = new(true, false, false);
|
||||
|
||||
public LdapIdentityProviderPlugin(
|
||||
AuthorityPluginContext pluginContext,
|
||||
LdapCredentialStore credentialStore,
|
||||
LdapClaimsEnricher claimsEnricher,
|
||||
ILdapConnectionFactory connectionFactory,
|
||||
IOptionsMonitor<LdapPluginOptions> optionsMonitor,
|
||||
ILogger<LdapIdentityProviderPlugin> logger)
|
||||
{
|
||||
this.pluginContext = pluginContext ?? throw new ArgumentNullException(nameof(pluginContext));
|
||||
this.credentialStore = credentialStore ?? throw new ArgumentNullException(nameof(credentialStore));
|
||||
this.claimsEnricher = claimsEnricher ?? throw new ArgumentNullException(nameof(claimsEnricher));
|
||||
this.connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory));
|
||||
this.optionsMonitor = optionsMonitor ?? throw new ArgumentNullException(nameof(optionsMonitor));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public string Name => pluginContext.Manifest.Name;
|
||||
|
||||
public string Type => pluginContext.Manifest.Type;
|
||||
|
||||
public AuthorityPluginContext Context => pluginContext;
|
||||
|
||||
public IUserCredentialStore Credentials => credentialStore;
|
||||
|
||||
public IClaimsEnricher ClaimsEnricher => claimsEnricher;
|
||||
|
||||
public IClientProvisioningStore? ClientProvisioning => null;
|
||||
|
||||
public AuthorityIdentityProviderCapabilities Capabilities => capabilities;
|
||||
|
||||
public async ValueTask<AuthorityPluginHealthResult> CheckHealthAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var connection = await connectionFactory.CreateAsync(cancellationToken).ConfigureAwait(false);
|
||||
var options = optionsMonitor.Get(Name);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(options.Connection.BindDn))
|
||||
{
|
||||
var secret = LdapSecretResolver.Resolve(options.Connection.BindPasswordSecret);
|
||||
await connection.BindAsync(options.Connection.BindDn!, secret, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return AuthorityPluginHealthResult.Healthy();
|
||||
}
|
||||
catch (LdapAuthenticationException ex)
|
||||
{
|
||||
logger.LogWarning(ex, "LDAP plugin {Plugin} service bind failed during health check.", Name);
|
||||
return AuthorityPluginHealthResult.Degraded("Service bind failed: check credentials.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "LDAP plugin {Plugin} health check failed.", Name);
|
||||
return AuthorityPluginHealthResult.Degraded(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap;
|
||||
|
||||
internal sealed class LdapPluginOptions
|
||||
{
|
||||
public LdapConnectionOptions Connection { get; set; } = new();
|
||||
|
||||
public LdapSecurityOptions Security { get; set; } = new();
|
||||
|
||||
public LdapQueryOptions Queries { get; set; } = new();
|
||||
|
||||
public void Normalize(string configPath)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(configPath);
|
||||
|
||||
Connection.Normalize(configPath);
|
||||
Security.Normalize();
|
||||
Queries.Normalize();
|
||||
}
|
||||
|
||||
public void Validate(string pluginName)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(pluginName);
|
||||
|
||||
Connection.Validate(pluginName);
|
||||
Security.Validate(pluginName);
|
||||
Queries.Validate(pluginName);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class LdapConnectionOptions
|
||||
{
|
||||
public string? Host { get; set; }
|
||||
|
||||
public int Port { get; set; } = 636;
|
||||
|
||||
public bool UseStartTls { get; set; }
|
||||
|
||||
public bool ValidateCertificates { get; set; } = true;
|
||||
|
||||
public LdapClientCertificateOptions? ClientCertificate { get; set; }
|
||||
|
||||
public LdapTrustStoreOptions TrustStore { get; set; } = new();
|
||||
|
||||
public string? SearchBase { get; set; }
|
||||
|
||||
public string? UsernameAttribute { get; set; }
|
||||
|
||||
public string? UserDnFormat { get; set; }
|
||||
|
||||
public string? BindDn { get; set; }
|
||||
|
||||
public string? BindPasswordSecret { get; set; }
|
||||
|
||||
internal void Normalize(string configPath)
|
||||
{
|
||||
Host = NormalizeString(Host);
|
||||
SearchBase = NormalizeString(SearchBase);
|
||||
UsernameAttribute = NormalizeString(UsernameAttribute);
|
||||
UserDnFormat = NormalizeString(UserDnFormat);
|
||||
BindDn = NormalizeString(BindDn);
|
||||
BindPasswordSecret = NormalizeString(BindPasswordSecret);
|
||||
|
||||
if (ClientCertificate is { })
|
||||
{
|
||||
ClientCertificate.Normalize(configPath);
|
||||
}
|
||||
|
||||
TrustStore.Normalize(configPath);
|
||||
}
|
||||
|
||||
internal void Validate(string pluginName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Host))
|
||||
{
|
||||
throw new InvalidOperationException($"LDAP plugin '{pluginName}' requires connection.host to be configured.");
|
||||
}
|
||||
|
||||
if (Port <= 0 || Port > 65535)
|
||||
{
|
||||
throw new InvalidOperationException($"LDAP plugin '{pluginName}' requires connection.port to be between 1 and 65535.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(BindDn))
|
||||
{
|
||||
throw new InvalidOperationException($"LDAP plugin '{pluginName}' requires connection.bindDn to be configured.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(BindPasswordSecret))
|
||||
{
|
||||
throw new InvalidOperationException($"LDAP plugin '{pluginName}' requires connection.bindPasswordSecret to be configured.");
|
||||
}
|
||||
|
||||
var hasUserDnFormat = !string.IsNullOrWhiteSpace(UserDnFormat);
|
||||
var hasSearchBase = !string.IsNullOrWhiteSpace(SearchBase);
|
||||
var hasUsernameAttribute = !string.IsNullOrWhiteSpace(UsernameAttribute);
|
||||
|
||||
if (!hasUserDnFormat && (!hasSearchBase || !hasUsernameAttribute))
|
||||
{
|
||||
throw new InvalidOperationException($"LDAP plugin '{pluginName}' requires either connection.userDnFormat or both connection.searchBase and connection.usernameAttribute to be configured.");
|
||||
}
|
||||
|
||||
if (ClientCertificate is { } certificate && certificate.IsConfigured)
|
||||
{
|
||||
certificate.Validate(pluginName);
|
||||
}
|
||||
|
||||
TrustStore.Validate(pluginName, ValidateCertificates);
|
||||
}
|
||||
|
||||
private static string? NormalizeString(string? value)
|
||||
=> string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||
}
|
||||
|
||||
internal sealed class LdapClientCertificateOptions
|
||||
{
|
||||
public string? PfxPath { get; set; }
|
||||
|
||||
public string? PasswordSecret { get; set; }
|
||||
|
||||
public bool SendChain { get; set; } = true;
|
||||
|
||||
public bool IsConfigured =>
|
||||
!string.IsNullOrWhiteSpace(PfxPath) || !string.IsNullOrWhiteSpace(PasswordSecret);
|
||||
|
||||
internal void Normalize(string configPath)
|
||||
{
|
||||
PfxPath = LdapPathUtilities.NormalizePath(PfxPath, configPath);
|
||||
PasswordSecret = string.IsNullOrWhiteSpace(PasswordSecret) ? null : PasswordSecret.Trim();
|
||||
}
|
||||
|
||||
internal void Validate(string pluginName)
|
||||
{
|
||||
if (!IsConfigured)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(PfxPath))
|
||||
{
|
||||
throw new InvalidOperationException($"LDAP plugin '{pluginName}' requires connection.clientCertificate.pfxPath when client certificates are enabled.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(PasswordSecret))
|
||||
{
|
||||
throw new InvalidOperationException($"LDAP plugin '{pluginName}' requires connection.clientCertificate.passwordSecret when client certificates are enabled.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class LdapTrustStoreOptions
|
||||
{
|
||||
public LdapTrustStoreMode Mode { get; set; } = LdapTrustStoreMode.System;
|
||||
|
||||
public string? BundlePath { get; set; }
|
||||
|
||||
internal void Normalize(string configPath)
|
||||
{
|
||||
if (Mode == LdapTrustStoreMode.Bundle)
|
||||
{
|
||||
BundlePath = LdapPathUtilities.NormalizePath(BundlePath, configPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
BundlePath = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal void Validate(string pluginName, bool validateCertificates)
|
||||
{
|
||||
if (!validateCertificates)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Mode == LdapTrustStoreMode.Bundle && string.IsNullOrWhiteSpace(BundlePath))
|
||||
{
|
||||
throw new InvalidOperationException($"LDAP plugin '{pluginName}' requires connection.trustStore.bundlePath when trustStore.mode is 'bundle'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal enum LdapTrustStoreMode
|
||||
{
|
||||
System,
|
||||
Bundle
|
||||
}
|
||||
|
||||
internal sealed class LdapSecurityOptions
|
||||
{
|
||||
private const string AllowInsecureVariable = "STELLAOPS_LDAP_ALLOW_INSECURE";
|
||||
|
||||
public bool RequireTls { get; set; } = true;
|
||||
|
||||
public bool AllowInsecureWithEnvToggle { get; set; }
|
||||
|
||||
public bool ReferralChasing { get; set; }
|
||||
|
||||
public string[] AllowedCipherSuites { get; set; } = Array.Empty<string>();
|
||||
|
||||
internal void Normalize()
|
||||
{
|
||||
AllowedCipherSuites = AllowedCipherSuites?
|
||||
.Where(static suite => !string.IsNullOrWhiteSpace(suite))
|
||||
.Select(static suite => suite.Trim())
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray() ?? Array.Empty<string>();
|
||||
}
|
||||
|
||||
internal void Validate(string pluginName)
|
||||
{
|
||||
if (RequireTls)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AllowInsecureWithEnvToggle)
|
||||
{
|
||||
throw new InvalidOperationException($"LDAP plugin '{pluginName}' cannot disable TLS unless security.allowInsecureWithEnvToggle is true and environment variable {AllowInsecureVariable}=true.");
|
||||
}
|
||||
|
||||
var envValue = Environment.GetEnvironmentVariable(AllowInsecureVariable);
|
||||
if (!string.Equals(envValue, "true", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException($"LDAP plugin '{pluginName}' requires environment variable {AllowInsecureVariable}=true to allow insecure connections.");
|
||||
}
|
||||
}
|
||||
|
||||
public static string AllowInsecureEnvironmentVariable => AllowInsecureVariable;
|
||||
}
|
||||
|
||||
internal static class LdapPathUtilities
|
||||
{
|
||||
public static string? NormalizePath(string? path, string configPath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var trimmed = path.Trim();
|
||||
|
||||
if (Uri.TryCreate(trimmed, UriKind.Absolute, out var uri) && uri.IsFile)
|
||||
{
|
||||
trimmed = uri.LocalPath;
|
||||
}
|
||||
else if (trimmed.StartsWith("file:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (Uri.TryCreate(trimmed, UriKind.Absolute, out var fileUri))
|
||||
{
|
||||
trimmed = fileUri.LocalPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
trimmed = trimmed["file:".Length..].TrimStart('/');
|
||||
}
|
||||
}
|
||||
|
||||
var expanded = Environment.ExpandEnvironmentVariables(trimmed);
|
||||
string candidate;
|
||||
|
||||
if (Path.IsPathRooted(expanded))
|
||||
{
|
||||
candidate = expanded;
|
||||
}
|
||||
else
|
||||
{
|
||||
var baseDirectory = Path.GetDirectoryName(configPath);
|
||||
if (string.IsNullOrEmpty(baseDirectory))
|
||||
{
|
||||
baseDirectory = Directory.GetCurrentDirectory();
|
||||
}
|
||||
|
||||
candidate = Path.Combine(baseDirectory, expanded);
|
||||
}
|
||||
|
||||
return Path.GetFullPath(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class LdapQueryOptions
|
||||
{
|
||||
public string? UserFilter { get; set; }
|
||||
|
||||
public string[] Attributes { get; set; } = Array.Empty<string>();
|
||||
|
||||
internal void Normalize()
|
||||
{
|
||||
Attributes = Attributes?
|
||||
.Where(static attribute => !string.IsNullOrWhiteSpace(attribute))
|
||||
.Select(static attribute => attribute.Trim())
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray() ?? Array.Empty<string>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(UserFilter))
|
||||
{
|
||||
UserFilter = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
UserFilter = UserFilter.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
internal void Validate(string pluginName)
|
||||
{
|
||||
if (UserFilter is { Length: > 0 } && !UserFilter.Contains("{username}", StringComparison.Ordinal))
|
||||
{
|
||||
throw new InvalidOperationException($"LDAP plugin '{pluginName}' requires queries.userFilter to include '{{username}}' placeholder when configured.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Authority.Plugins.Abstractions;
|
||||
using StellaOps.Authority.Plugin.Ldap.Claims;
|
||||
using StellaOps.Authority.Plugin.Ldap.Connections;
|
||||
using StellaOps.Authority.Plugin.Ldap.Credentials;
|
||||
using StellaOps.Authority.Plugin.Ldap.Monitoring;
|
||||
using StellaOps.Authority.Plugin.Ldap.Security;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap;
|
||||
|
||||
internal sealed class LdapPluginRegistrar : IAuthorityPluginRegistrar
|
||||
{
|
||||
public string PluginType => "ldap";
|
||||
|
||||
public void Register(AuthorityPluginRegistrationContext context)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
|
||||
var pluginManifest = context.Plugin.Manifest;
|
||||
var pluginName = pluginManifest.Name;
|
||||
var configPath = pluginManifest.ConfigPath;
|
||||
|
||||
context.Services.AddOptions<LdapPluginOptions>(pluginName)
|
||||
.Bind(context.Plugin.Configuration)
|
||||
.PostConfigure(options =>
|
||||
{
|
||||
options.Normalize(configPath);
|
||||
options.Validate(pluginName);
|
||||
})
|
||||
.ValidateOnStart();
|
||||
|
||||
context.Services.AddSingleton(_ => new LdapMetrics(pluginName));
|
||||
|
||||
context.Services.AddSingleton<ILdapConnectionFactory>(sp => new DirectoryServicesLdapConnectionFactory(
|
||||
pluginName,
|
||||
sp.GetRequiredService<IOptionsMonitor<LdapPluginOptions>>(),
|
||||
sp.GetRequiredService<ILogger<DirectoryServicesLdapConnectionFactory>>(),
|
||||
sp.GetRequiredService<LdapMetrics>()));
|
||||
|
||||
context.Services.AddScoped(sp => new LdapCredentialStore(
|
||||
pluginName,
|
||||
sp.GetRequiredService<IOptionsMonitor<LdapPluginOptions>>(),
|
||||
sp.GetRequiredService<ILdapConnectionFactory>(),
|
||||
sp.GetRequiredService<ILogger<LdapCredentialStore>>(),
|
||||
sp.GetRequiredService<LdapMetrics>()));
|
||||
|
||||
context.Services.AddScoped<LdapClaimsEnricher>();
|
||||
context.Services.AddScoped<IClaimsEnricher>(sp => sp.GetRequiredService<LdapClaimsEnricher>());
|
||||
|
||||
context.Services.AddScoped<IUserCredentialStore>(sp => sp.GetRequiredService<LdapCredentialStore>());
|
||||
|
||||
context.Services.AddScoped<IIdentityProviderPlugin>(sp => new LdapIdentityProviderPlugin(
|
||||
context.Plugin,
|
||||
sp.GetRequiredService<LdapCredentialStore>(),
|
||||
sp.GetRequiredService<LdapClaimsEnricher>(),
|
||||
sp.GetRequiredService<ILdapConnectionFactory>(),
|
||||
sp.GetRequiredService<IOptionsMonitor<LdapPluginOptions>>(),
|
||||
sp.GetRequiredService<ILogger<LdapIdentityProviderPlugin>>()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Metrics;
|
||||
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap.Monitoring;
|
||||
|
||||
internal sealed class LdapMetrics
|
||||
{
|
||||
private const string MeterName = "StellaOps.Authority.Plugin.Ldap";
|
||||
|
||||
private static readonly Meter Meter = new(MeterName);
|
||||
|
||||
private readonly string pluginName;
|
||||
private readonly Counter<long> bindAttempts;
|
||||
private readonly Counter<long> bindFailures;
|
||||
private readonly Counter<long> searchAttempts;
|
||||
private readonly Counter<long> searchFailures;
|
||||
private readonly Counter<long> retryCounter;
|
||||
private readonly Counter<long> bindSuccesses;
|
||||
private readonly Counter<long> searchHits;
|
||||
private readonly Counter<long> searchMisses;
|
||||
|
||||
public LdapMetrics(string pluginName)
|
||||
{
|
||||
this.pluginName = pluginName ?? throw new ArgumentNullException(nameof(pluginName));
|
||||
bindAttempts = Meter.CreateCounter<long>("ldap.bind.attempts");
|
||||
bindSuccesses = Meter.CreateCounter<long>("ldap.bind.successes");
|
||||
bindFailures = Meter.CreateCounter<long>("ldap.bind.failures");
|
||||
searchAttempts = Meter.CreateCounter<long>("ldap.search.attempts");
|
||||
searchHits = Meter.CreateCounter<long>("ldap.search.hits");
|
||||
searchMisses = Meter.CreateCounter<long>("ldap.search.misses");
|
||||
searchFailures = Meter.CreateCounter<long>("ldap.search.failures");
|
||||
retryCounter = Meter.CreateCounter<long>("ldap.operation.retries");
|
||||
}
|
||||
|
||||
public void RecordBindAttempt() => bindAttempts.Add(1, KeyValuePair.Create<string, object?>("plugin", pluginName));
|
||||
|
||||
public void RecordBindSuccess() => bindSuccesses.Add(1, KeyValuePair.Create<string, object?>("plugin", pluginName));
|
||||
|
||||
public void RecordBindFailure() => bindFailures.Add(1, KeyValuePair.Create<string, object?>("plugin", pluginName));
|
||||
|
||||
public void RecordSearchAttempt() => searchAttempts.Add(1, KeyValuePair.Create<string, object?>("plugin", pluginName));
|
||||
|
||||
public void RecordSearchHit() => searchHits.Add(1, KeyValuePair.Create<string, object?>("plugin", pluginName));
|
||||
|
||||
public void RecordSearchMiss() => searchMisses.Add(1, KeyValuePair.Create<string, object?>("plugin", pluginName));
|
||||
|
||||
public void RecordSearchFailure() => searchFailures.Add(1, KeyValuePair.Create<string, object?>("plugin", pluginName));
|
||||
|
||||
public void RecordRetry() => retryCounter.Add(1, KeyValuePair.Create<string, object?>("plugin", pluginName));
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("StellaOps.Authority.Plugin.Ldap.Tests")]
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace StellaOps.Authority.Plugin.Ldap.Security;
|
||||
|
||||
internal static class LdapSecretResolver
|
||||
{
|
||||
public static string Resolve(string? reference)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(reference))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var trimmed = reference.Trim();
|
||||
|
||||
if (trimmed.StartsWith("file:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var path = trimmed[5..];
|
||||
return File.Exists(path) ? File.ReadAllText(path).Trim() : string.Empty;
|
||||
}
|
||||
|
||||
if (trimmed.StartsWith("env:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var variable = trimmed[4..];
|
||||
return Environment.GetEnvironmentVariable(variable)?.Trim() ?? string.Empty;
|
||||
}
|
||||
|
||||
return trimmed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<IsAuthorityPlugin>true</IsAuthorityPlugin>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="System.DirectoryServices.Protocols" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\\StellaOps.Authority.Plugins.Abstractions\\StellaOps.Authority.Plugins.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\\StellaOps.Auth.Abstractions\\StellaOps.Auth.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\\..\\..\\__Libraries\\StellaOps.Plugin\\StellaOps.Plugin.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -6,8 +6,18 @@
|
||||
| SEC3.PLG | BLOCKED (2025-10-21) | Security Guild, BE-Auth Plugin | CORE8, SEC3.A (rate limiter) | Ensure lockout responses and rate-limit metadata flow through plugin logs/events (include retry-after). <br>⛔ Pending AUTH-DPOP-11-001 / AUTH-MTLS-11-002 / PLUGIN-DI-08-001 so limiter telemetry contract matches final authority surface. | ✅ Audit record includes retry-after; ✅ Tests confirm lockout + limiter interplay. |
|
||||
| SEC5.PLG | BLOCKED (2025-10-21) | Security Guild | SEC5.A (threat model) | Address plugin-specific mitigations (bootstrap user handling, password policy docs) in threat model backlog. <br>⛔ Final documentation depends on AUTH-DPOP-11-001 / AUTH-MTLS-11-002 / PLUGIN-DI-08-001 outcomes. | ✅ Threat model lists plugin attack surfaces; ✅ Mitigation items filed. |
|
||||
| PLG4-6.CAPABILITIES | BLOCKED (2025-10-12) | BE-Auth Plugin, Docs Guild | PLG1–PLG3 | Finalise capability metadata exposure, config validation, and developer guide updates; remaining action is Docs polish/diagram export. | ✅ Capability metadata + validation merged; ✅ Plugin guide updated with final copy & diagrams; ✅ Release notes mention new toggles. <br>⛔ Blocked awaiting Authority rate-limiter stream (CORE8/SEC3) to resume so doc updates reflect final limiter behaviour. |
|
||||
| PLG7.RFC | REVIEW | BE-Auth Plugin, Security Guild | PLG4 | Socialize LDAP plugin RFC (`docs/rfcs/authority-plugin-ldap.md`) and capture guild feedback. | ✅ Guild review sign-off recorded; ✅ Follow-up issues filed in module boards. |
|
||||
| PLG6.DIAGRAM | TODO | Docs Guild | PLG6.DOC | Export final sequence/component diagrams for the developer guide and add offline-friendly assets under `docs/assets/authority`. | ✅ Mermaid sources committed; ✅ Rendered SVG/PNG linked from Section 2 + Section 9; ✅ Docs build preview shared with Plugin + Docs guilds. |
|
||||
| PLG7.RFC | DONE (2025-11-03) | BE-Auth Plugin, Security Guild | PLG4 | Socialize LDAP plugin RFC (`docs/rfcs/authority-plugin-ldap.md`) and capture guild feedback. | ✅ Guild review sign-off recorded; ✅ Follow-up issues filed in module boards. |
|
||||
| PLG7.IMPL-001 | DONE (2025-11-03) | BE-Auth Plugin | PLG7.RFC | Scaffold `StellaOps.Authority.Plugin.Ldap` + tests, bind configuration (client certificate, trust-store, insecure toggle) with validation and docs samples. | ✅ Project + test harness build; ✅ Configuration bound & validated; ✅ Sample config updated. |
|
||||
| PLG7.IMPL-002 | DOING (2025-11-03) | BE-Auth Plugin, Security Guild | PLG7.IMPL-001 | Implement LDAP credential store with TLS/mutual TLS enforcement, deterministic retry/backoff, and structured logging/metrics. | ✅ Credential store passes integration tests (OpenLDAP + mtls); ✅ Metrics/logs emitted; ✅ Error mapping documented. |
|
||||
| PLG7.IMPL-003 | TODO | BE-Auth Plugin | PLG7.IMPL-001 | Deliver claims enricher with DN-to-role dictionary and regex mapping plus Mongo cache, including determinism + eviction tests. | ✅ Regex mapping deterministic; ✅ Cache TTL + invalidation tested; ✅ Claims doc updated. |
|
||||
| PLG7.IMPL-004 | TODO | BE-Auth Plugin, DevOps Guild | PLG7.IMPL-002 | Implement client provisioning store with LDAP write toggles, Mongo audit mirror, bootstrap validation, and health reporting. | ✅ Audit mirror records persisted; ✅ Bootstrap validation logs capability summary; ✅ Health checks cover LDAP + audit mirror. |
|
||||
| PLG7.IMPL-005 | TODO | BE-Auth Plugin, Docs Guild | PLG7.IMPL-001..004 | Update developer guide, samples, and release notes for LDAP plugin (mutual TLS, regex mapping, audit mirror) and ensure Offline Kit coverage. | ✅ Docs merged; ✅ Release notes drafted; ✅ Offline kit config templates updated. |
|
||||
| PLG6.DIAGRAM | DONE (2025-11-03) | Docs Guild | PLG6.DOC | Export final sequence/component diagrams for the developer guide and add offline-friendly assets under `docs/assets/authority`. | ✅ Mermaid sources committed; ✅ Rendered SVG/PNG linked from Section 2 + Section 9; ✅ Docs build preview shared with Plugin + Docs guilds. |
|
||||
> 2025-11-03: Task moved to DOING – drafting component + sequence diagrams and prepping offline-friendly exports for the developer guide.
|
||||
> 2025-11-03: Task marked DONE – added component topology + bootstrap sequence diagrams (Mermaid + SVG) and refreshed developer guide references for offline kits.
|
||||
> 2025-11-03: LDAP plugin RFC accepted; review notes in `docs/notes/2025-11-03-authority-plugin-ldap-review.md`. Follow-up implementation items PLG7.IMPL-001..005 added per review outcomes.
|
||||
> 2025-11-03: PLG7.IMPL-001 completed – created `StellaOps.Authority.Plugin.Ldap` + tests projects, implemented configuration normalization/validation (client certificate, trust store, insecure toggle) with coverage (`dotnet test src/Authority/StellaOps.Authority/StellaOps.Authority.Plugin.Ldap.Tests/StellaOps.Authority.Plugin.Ldap.Tests.csproj`), and refreshed `etc/authority.plugins/ldap.yaml`.
|
||||
> 2025-11-04: PLG7.IMPL-002 progress – StartTLS initialization now uses `StartTransportLayerSecurity(null)` and LDAP result-code handling normalized for `System.DirectoryServices.Protocols` 8.0 (invalid credentials + transient cases). Plugin tests rerun via `dotnet test src/Authority/StellaOps.Authority/StellaOps.Authority.Plugin.Ldap.Tests/StellaOps.Authority.Plugin.Ldap.Tests.csproj` (green).
|
||||
|
||||
> Update statuses to DOING/DONE/BLOCKED as you make progress. Always run `dotnet test` for touched projects before marking DONE.
|
||||
|
||||
|
||||
@@ -2901,6 +2901,100 @@ public class ClientCredentialsHandlersTests
|
||||
Assert.Contains("jobs:read", inserted.Scope);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleClientCredentials_EmitsDelegationAuditProperties()
|
||||
{
|
||||
var clientDocument = CreateClient(
|
||||
secret: "s3cr3t!",
|
||||
allowedGrantTypes: "client_credentials",
|
||||
allowedScopes: "jobs:read",
|
||||
tenant: "tenant-alpha");
|
||||
|
||||
var serviceAccount = new AuthorityServiceAccountDocument
|
||||
{
|
||||
AccountId = "svc-ops",
|
||||
Tenant = "tenant-alpha",
|
||||
AllowedScopes = new List<string> { "jobs:read" },
|
||||
AuthorizedClients = new List<string> { clientDocument.ClientId },
|
||||
Enabled = true
|
||||
};
|
||||
|
||||
var registry = CreateRegistry(withClientProvisioning: true, clientDescriptor: CreateDescriptor(clientDocument));
|
||||
var tokenStore = new TestTokenStore();
|
||||
var sessionAccessor = new NullMongoSessionAccessor();
|
||||
var authSink = new TestAuthEventSink();
|
||||
var metadataAccessor = new TestRateLimiterMetadataAccessor();
|
||||
var serviceAccountStore = new TestServiceAccountStore(serviceAccount);
|
||||
var options = TestHelpers.CreateAuthorityOptions(opts =>
|
||||
{
|
||||
opts.Delegation.Quotas.MaxActiveTokens = 5;
|
||||
});
|
||||
|
||||
var validateHandler = new ValidateClientCredentialsHandler(
|
||||
new TestClientStore(clientDocument),
|
||||
registry,
|
||||
TestActivitySource,
|
||||
authSink,
|
||||
metadataAccessor,
|
||||
serviceAccountStore,
|
||||
tokenStore,
|
||||
TimeProvider.System,
|
||||
new NoopCertificateValidator(),
|
||||
new HttpContextAccessor(),
|
||||
options,
|
||||
NullLogger<ValidateClientCredentialsHandler>.Instance);
|
||||
|
||||
var transaction = CreateTokenTransaction(clientDocument.ClientId, "s3cr3t!", scope: "jobs:read");
|
||||
transaction.Options.AccessTokenLifetime = TimeSpan.FromMinutes(15);
|
||||
var delegationActor = "pipeline://exporter/step/42";
|
||||
|
||||
SetParameter(transaction, AuthorityOpenIddictConstants.ServiceAccountParameterName, serviceAccount.AccountId);
|
||||
SetParameter(transaction, AuthorityOpenIddictConstants.DelegationActorParameterName, delegationActor);
|
||||
|
||||
var validateContext = new OpenIddictServerEvents.ValidateTokenRequestContext(transaction);
|
||||
await validateHandler.HandleAsync(validateContext);
|
||||
Assert.False(validateContext.IsRejected);
|
||||
|
||||
var handleHandler = new HandleClientCredentialsHandler(
|
||||
registry,
|
||||
tokenStore,
|
||||
sessionAccessor,
|
||||
metadataAccessor,
|
||||
TimeProvider.System,
|
||||
TestActivitySource,
|
||||
NullLogger<HandleClientCredentialsHandler>.Instance);
|
||||
var persistHandler = new PersistTokensHandler(tokenStore, sessionAccessor, TimeProvider.System, TestActivitySource, NullLogger<PersistTokensHandler>.Instance);
|
||||
|
||||
var handleContext = new OpenIddictServerEvents.HandleTokenRequestContext(transaction);
|
||||
await handleHandler.HandleAsync(handleContext);
|
||||
Assert.True(handleContext.IsRequestHandled);
|
||||
|
||||
var signInContext = new OpenIddictServerEvents.ProcessSignInContext(transaction)
|
||||
{
|
||||
Principal = handleContext.Principal,
|
||||
AccessTokenPrincipal = handleContext.Principal
|
||||
};
|
||||
|
||||
await persistHandler.HandleAsync(signInContext);
|
||||
|
||||
var inserted = tokenStore.Inserted ?? throw new InvalidOperationException("Delegation token was not persisted.");
|
||||
Assert.Equal("service_account", inserted.TokenKind);
|
||||
Assert.Equal(serviceAccount.AccountId, inserted.ServiceAccountId);
|
||||
Assert.Equal(new[] { clientDocument.ClientId, delegationActor }, inserted.ActorChain);
|
||||
|
||||
var grantEvent = authSink.Events.LastOrDefault(evt => evt.EventType == "authority.client_credentials.grant");
|
||||
Assert.NotNull(grantEvent);
|
||||
|
||||
var serviceProperty = Assert.Single(grantEvent!.Properties.Where(prop => prop.Name == "delegation.service_account"));
|
||||
Assert.Equal(serviceAccount.AccountId, serviceProperty.Value.Value);
|
||||
|
||||
var actorPropertyValues = grantEvent.Properties
|
||||
.Where(prop => prop.Name.StartsWith("delegation.actor[", StringComparison.Ordinal))
|
||||
.Select(prop => prop.Value.Value)
|
||||
.ToArray();
|
||||
Assert.Contains(delegationActor, actorPropertyValues);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandleClientCredentials_ProjectsServiceAccountAttributeClaims()
|
||||
{
|
||||
|
||||
@@ -49,6 +49,13 @@ public sealed class DiscoveryMetadataTests : IClassFixture<AuthorityWebApplicati
|
||||
Assert.Contains(StellaOpsScopes.AirgapImport, airgapScopes);
|
||||
Assert.Contains(StellaOpsScopes.AirgapStatusRead, airgapScopes);
|
||||
|
||||
Assert.True(root.TryGetProperty("stellaops_packs_scopes_supported", out var packsNode));
|
||||
var packsScopes = packsNode.EnumerateArray().Select(element => element.GetString()).ToArray();
|
||||
Assert.Contains(StellaOpsScopes.PacksRead, packsScopes);
|
||||
Assert.Contains(StellaOpsScopes.PacksWrite, packsScopes);
|
||||
Assert.Contains(StellaOpsScopes.PacksRun, packsScopes);
|
||||
Assert.Contains(StellaOpsScopes.PacksApprove, packsScopes);
|
||||
|
||||
Assert.True(root.TryGetProperty("stellaops_observability_scopes_supported", out var observabilityNode));
|
||||
var observabilityScopes = observabilityNode.EnumerateArray().Select(element => element.GetString()).ToArray();
|
||||
Assert.Contains(StellaOpsScopes.ObservabilityRead, observabilityScopes);
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.Authority.Signing;
|
||||
using StellaOps.Cryptography;
|
||||
using StellaOps.Cryptography.Kms;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Authority.Tests.Signing;
|
||||
|
||||
public sealed class KmsAuthoritySigningKeySourceTests
|
||||
{
|
||||
[Fact]
|
||||
public void Load_ReturnsRawKey_WhenKmsOmitsPrivateScalar()
|
||||
{
|
||||
using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
|
||||
var parameters = ecdsa.ExportParameters(includePrivateParameters: true);
|
||||
var material = new KmsKeyMaterial(
|
||||
"arn:aws:kms:us-east-1:123456789012:key/demo",
|
||||
"arn:aws:kms:us-east-1:123456789012:key/demo/1",
|
||||
KmsAlgorithms.Es256,
|
||||
"P-256",
|
||||
Array.Empty<byte>(),
|
||||
parameters.Q.X!,
|
||||
parameters.Q.Y!,
|
||||
DateTimeOffset.UtcNow);
|
||||
|
||||
var kms = new StubKmsClient(material);
|
||||
var source = new KmsAuthoritySigningKeySource(kms);
|
||||
|
||||
var request = new AuthoritySigningKeyRequest(
|
||||
keyId: "demo",
|
||||
algorithm: KmsAlgorithms.Es256,
|
||||
source: "kms",
|
||||
location: material.KeyId,
|
||||
status: AuthoritySigningKeyStatus.Active,
|
||||
basePath: "/tmp",
|
||||
provider: "kms",
|
||||
expiresAt: null,
|
||||
additionalMetadata: new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
[KmsAuthoritySigningKeySource.KmsMetadataKeys.Version] = material.VersionId
|
||||
});
|
||||
|
||||
var signingKey = source.Load(request);
|
||||
|
||||
Assert.Equal(CryptoSigningKeyKind.Raw, signingKey.Kind);
|
||||
Assert.Equal(material.KeyId, signingKey.Reference.KeyId);
|
||||
Assert.True(signingKey.PrivateKey.Length > 0);
|
||||
Assert.True(signingKey.PublicKey.Length > 0);
|
||||
Assert.Equal(material.VersionId, signingKey.Metadata[KmsAuthoritySigningKeySource.KmsMetadataKeys.Version]);
|
||||
}
|
||||
|
||||
private sealed class StubKmsClient : IKmsClient
|
||||
{
|
||||
private readonly KmsKeyMaterial _material;
|
||||
|
||||
public StubKmsClient(KmsKeyMaterial material)
|
||||
=> _material = material;
|
||||
|
||||
public Task<KmsSignResult> SignAsync(string keyId, string? keyVersion, ReadOnlyMemory<byte> data, CancellationToken cancellationToken = default)
|
||||
=> throw new NotSupportedException();
|
||||
|
||||
public Task<bool> VerifyAsync(string keyId, string? keyVersion, ReadOnlyMemory<byte> data, ReadOnlyMemory<byte> signature, CancellationToken cancellationToken = default)
|
||||
=> throw new NotSupportedException();
|
||||
|
||||
public Task<KmsKeyMetadata> GetMetadataAsync(string keyId, CancellationToken cancellationToken = default)
|
||||
=> Task.FromResult(new KmsKeyMetadata(_material.KeyId, _material.Algorithm, KmsKeyState.Active, _material.CreatedAt, ImmutableArray<KmsKeyVersionMetadata>.Empty));
|
||||
|
||||
public Task<KmsKeyMaterial> ExportAsync(string keyId, string? keyVersion, CancellationToken cancellationToken = default)
|
||||
=> Task.FromResult(_material);
|
||||
|
||||
public Task<KmsKeyMetadata> RotateAsync(string keyId, CancellationToken cancellationToken = default)
|
||||
=> throw new NotSupportedException();
|
||||
|
||||
public Task RevokeAsync(string keyId, CancellationToken cancellationToken = default)
|
||||
=> throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
@@ -1,398 +1,426 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority", "StellaOps.Authority\StellaOps.Authority.csproj", "{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority.Plugins.Abstractions", "StellaOps.Authority.Plugins.Abstractions\StellaOps.Authority.Plugins.Abstractions.csproj", "{B4E5DC28-0693-4708-8B07-5206053CACDB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority.Plugin.Standard", "StellaOps.Authority.Plugin.Standard\StellaOps.Authority.Plugin.Standard.csproj", "{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.Abstractions", "StellaOps.Auth.Abstractions\StellaOps.Auth.Abstractions.csproj", "{A399A886-B7B7-4ACE-811E-3F4B7051A725}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.ServerIntegration", "StellaOps.Auth.ServerIntegration\StellaOps.Auth.ServerIntegration.csproj", "{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.Client", "StellaOps.Auth.Client\StellaOps.Auth.Client.csproj", "{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Configuration.Tests", "..\StellaOps.Configuration.Tests\StellaOps.Configuration.Tests.csproj", "{A33529C5-1552-4216-B080-B621F077BE10}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Plugin", "..\StellaOps.Plugin\StellaOps.Plugin.csproj", "{C8F10390-5ED3-4638-A27E-F53F07583745}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.DependencyInjection", "..\StellaOps.DependencyInjection\StellaOps.DependencyInjection.csproj", "{D3FCB965-348C-4050-B4F7-7E065A562E2C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Configuration", "..\StellaOps.Configuration\StellaOps.Configuration.csproj", "{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority.Plugins.Abstractions.Tests", "StellaOps.Authority.Plugins.Abstractions.Tests\StellaOps.Authority.Plugins.Abstractions.Tests.csproj", "{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Testing", "..\StellaOps.Concelier.Testing\StellaOps.Concelier.Testing.csproj", "{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Connector.Common", "..\StellaOps.Concelier.Connector.Common\StellaOps.Concelier.Connector.Common.csproj", "{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Storage.Mongo", "..\StellaOps.Concelier.Storage.Mongo\StellaOps.Concelier.Storage.Mongo.csproj", "{67C85AC6-1670-4A0D-A81F-6015574F46C7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Core", "..\StellaOps.Concelier.Core\StellaOps.Concelier.Core.csproj", "{17829125-C0F5-47E6-A16C-EC142BD58220}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Models", "..\StellaOps.Concelier.Models\StellaOps.Concelier.Models.csproj", "{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Normalization", "..\StellaOps.Concelier.Normalization\StellaOps.Concelier.Normalization.csproj", "{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority.Tests", "StellaOps.Authority.Tests\StellaOps.Authority.Tests.csproj", "{D719B01C-2424-4DAB-94B9-C9B6004F450B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority.Plugin.Standard.Tests", "StellaOps.Authority.Plugin.Standard.Tests\StellaOps.Authority.Plugin.Standard.Tests.csproj", "{0C222CD9-96B1-4152-BD29-65FFAE27C880}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority.Storage.Mongo", "StellaOps.Authority.Storage.Mongo\StellaOps.Authority.Storage.Mongo.csproj", "{977FD870-91B5-44BA-944B-496B2C68DAA0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.Abstractions.Tests", "StellaOps.Auth.Abstractions.Tests\StellaOps.Auth.Abstractions.Tests.csproj", "{4A5D29B8-959A-4EAC-A827-979CD058EC16}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.ServerIntegration.Tests", "StellaOps.Auth.ServerIntegration.Tests\StellaOps.Auth.ServerIntegration.Tests.csproj", "{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.Client.Tests", "StellaOps.Auth.Client.Tests\StellaOps.Auth.Client.Tests.csproj", "{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography", "..\StellaOps.Cryptography\StellaOps.Cryptography.csproj", "{35D22E43-729A-4D43-A289-5A0E96BA0199}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography.Tests", "..\StellaOps.Cryptography.Tests\StellaOps.Cryptography.Tests.csproj", "{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography.DependencyInjection", "..\StellaOps.Cryptography.DependencyInjection\StellaOps.Cryptography.DependencyInjection.csproj", "{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.Security", "..\StellaOps.Auth.Security\StellaOps.Auth.Security.csproj", "{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Release|x86.Build.0 = Release|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Release|x86.Build.0 = Release|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Release|x64.Build.0 = Release|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Release|x86.Build.0 = Release|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Release|x86.Build.0 = Release|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Release|x86.Build.0 = Release|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Release|x86.Build.0 = Release|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Release|x64.Build.0 = Release|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Release|x86.Build.0 = Release|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Release|x86.Build.0 = Release|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Release|x64.Build.0 = Release|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Release|x86.Build.0 = Release|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Release|x64.Build.0 = Release|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Release|x86.Build.0 = Release|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Release|x86.Build.0 = Release|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Release|x64.Build.0 = Release|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Release|x86.Build.0 = Release|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Release|x86.Build.0 = Release|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Release|x64.Build.0 = Release|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Release|x86.Build.0 = Release|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Release|x64.Build.0 = Release|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Release|x86.Build.0 = Release|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Release|x64.Build.0 = Release|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Release|x86.Build.0 = Release|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Release|x64.Build.0 = Release|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Release|x86.Build.0 = Release|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Release|x64.Build.0 = Release|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Release|x86.Build.0 = Release|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Release|x64.Build.0 = Release|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Release|x86.Build.0 = Release|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Release|x86.Build.0 = Release|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority", "StellaOps.Authority\StellaOps.Authority.csproj", "{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority.Plugins.Abstractions", "StellaOps.Authority.Plugins.Abstractions\StellaOps.Authority.Plugins.Abstractions.csproj", "{B4E5DC28-0693-4708-8B07-5206053CACDB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority.Plugin.Standard", "StellaOps.Authority.Plugin.Standard\StellaOps.Authority.Plugin.Standard.csproj", "{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.Abstractions", "StellaOps.Auth.Abstractions\StellaOps.Auth.Abstractions.csproj", "{A399A886-B7B7-4ACE-811E-3F4B7051A725}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.ServerIntegration", "StellaOps.Auth.ServerIntegration\StellaOps.Auth.ServerIntegration.csproj", "{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.Client", "StellaOps.Auth.Client\StellaOps.Auth.Client.csproj", "{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Configuration.Tests", "..\StellaOps.Configuration.Tests\StellaOps.Configuration.Tests.csproj", "{A33529C5-1552-4216-B080-B621F077BE10}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Plugin", "..\StellaOps.Plugin\StellaOps.Plugin.csproj", "{C8F10390-5ED3-4638-A27E-F53F07583745}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.DependencyInjection", "..\StellaOps.DependencyInjection\StellaOps.DependencyInjection.csproj", "{D3FCB965-348C-4050-B4F7-7E065A562E2C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Configuration", "..\StellaOps.Configuration\StellaOps.Configuration.csproj", "{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority.Plugins.Abstractions.Tests", "StellaOps.Authority.Plugins.Abstractions.Tests\StellaOps.Authority.Plugins.Abstractions.Tests.csproj", "{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Testing", "..\StellaOps.Concelier.Testing\StellaOps.Concelier.Testing.csproj", "{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Connector.Common", "..\StellaOps.Concelier.Connector.Common\StellaOps.Concelier.Connector.Common.csproj", "{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Storage.Mongo", "..\StellaOps.Concelier.Storage.Mongo\StellaOps.Concelier.Storage.Mongo.csproj", "{67C85AC6-1670-4A0D-A81F-6015574F46C7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Core", "..\StellaOps.Concelier.Core\StellaOps.Concelier.Core.csproj", "{17829125-C0F5-47E6-A16C-EC142BD58220}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Models", "..\StellaOps.Concelier.Models\StellaOps.Concelier.Models.csproj", "{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Normalization", "..\StellaOps.Concelier.Normalization\StellaOps.Concelier.Normalization.csproj", "{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority.Tests", "StellaOps.Authority.Tests\StellaOps.Authority.Tests.csproj", "{D719B01C-2424-4DAB-94B9-C9B6004F450B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority.Plugin.Standard.Tests", "StellaOps.Authority.Plugin.Standard.Tests\StellaOps.Authority.Plugin.Standard.Tests.csproj", "{0C222CD9-96B1-4152-BD29-65FFAE27C880}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority.Storage.Mongo", "StellaOps.Authority.Storage.Mongo\StellaOps.Authority.Storage.Mongo.csproj", "{977FD870-91B5-44BA-944B-496B2C68DAA0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.Abstractions.Tests", "StellaOps.Auth.Abstractions.Tests\StellaOps.Auth.Abstractions.Tests.csproj", "{4A5D29B8-959A-4EAC-A827-979CD058EC16}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.ServerIntegration.Tests", "StellaOps.Auth.ServerIntegration.Tests\StellaOps.Auth.ServerIntegration.Tests.csproj", "{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.Client.Tests", "StellaOps.Auth.Client.Tests\StellaOps.Auth.Client.Tests.csproj", "{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography", "..\StellaOps.Cryptography\StellaOps.Cryptography.csproj", "{35D22E43-729A-4D43-A289-5A0E96BA0199}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography.Tests", "..\StellaOps.Cryptography.Tests\StellaOps.Cryptography.Tests.csproj", "{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography.DependencyInjection", "..\StellaOps.Cryptography.DependencyInjection\StellaOps.Cryptography.DependencyInjection.csproj", "{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.Security", "..\StellaOps.Auth.Security\StellaOps.Auth.Security.csproj", "{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority.Plugin.Ldap", "StellaOps.Authority.Plugin.Ldap\StellaOps.Authority.Plugin.Ldap.csproj", "{8B07FB7E-6C49-49F9-8919-5708E3C39907}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority.Plugin.Ldap.Tests", "StellaOps.Authority.Plugin.Ldap.Tests\StellaOps.Authority.Plugin.Ldap.Tests.csproj", "{3C2B782A-19F7-4B2A-8FD1-9DEF0059FA2F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{93CEF308-E217-41F3-BBF3-AFC1D32D9B4C}.Release|x86.Build.0 = Release|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{B4E5DC28-0693-4708-8B07-5206053CACDB}.Release|x86.Build.0 = Release|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Release|x64.Build.0 = Release|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{753A4FF4-BE1D-4361-9FE5-F2FF7CBDE3E3}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A399A886-B7B7-4ACE-811E-3F4B7051A725}.Release|x86.Build.0 = Release|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{0BA36155-0024-42D9-9DC9-8F85A72F9CA6}.Release|x86.Build.0 = Release|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9C8918FA-626F-41DE-8B89-4E216DCBF2A8}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A33529C5-1552-4216-B080-B621F077BE10}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C8F10390-5ED3-4638-A27E-F53F07583745}.Release|x86.Build.0 = Release|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D3FCB965-348C-4050-B4F7-7E065A562E2C}.Release|x86.Build.0 = Release|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3CB099C3-F41F-46AD-B81D-DB31C4EF643A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Release|x64.Build.0 = Release|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{EE97137B-22AF-4A84-9F65-9B4C6468B3CF}.Release|x86.Build.0 = Release|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D48E48BF-80C8-43DA-8BE6-E2B9E769C49E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E0B9CD7A-C4FF-44EB-BE04-9B998C1C4166}.Release|x86.Build.0 = Release|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Release|x64.Build.0 = Release|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{67C85AC6-1670-4A0D-A81F-6015574F46C7}.Release|x86.Build.0 = Release|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Release|x64.Build.0 = Release|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{17829125-C0F5-47E6-A16C-EC142BD58220}.Release|x86.Build.0 = Release|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9B4BA030-C979-4191-8B4F-7E2AD9F88A94}.Release|x86.Build.0 = Release|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Release|x64.Build.0 = Release|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{26B58A9B-DB0B-4E3D-9827-3722859E5FB4}.Release|x86.Build.0 = Release|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D719B01C-2424-4DAB-94B9-C9B6004F450B}.Release|x86.Build.0 = Release|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Release|x64.Build.0 = Release|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{0C222CD9-96B1-4152-BD29-65FFAE27C880}.Release|x86.Build.0 = Release|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Release|x64.Build.0 = Release|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{977FD870-91B5-44BA-944B-496B2C68DAA0}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4A5D29B8-959A-4EAC-A827-979CD058EC16}.Release|x86.Build.0 = Release|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Release|x64.Build.0 = Release|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{CB7FD547-1EC7-4A6F-87FE-F73003512AFE}.Release|x86.Build.0 = Release|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Release|x64.Build.0 = Release|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2DB48E45-BEFE-40FC-8E7D-1697A8EB0749}.Release|x86.Build.0 = Release|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Release|x64.Build.0 = Release|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{35D22E43-729A-4D43-A289-5A0E96BA0199}.Release|x86.Build.0 = Release|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Release|x64.Build.0 = Release|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{84AEC0C8-EE60-4AB1-A59B-B8E7CCFC0A25}.Release|x86.Build.0 = Release|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{159A9B4E-61F8-4A82-8F6E-D01E3FB7E18F}.Release|x86.Build.0 = Release|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{ACEFD2D2-D4B9-47FB-91F2-1EA94C28D93C}.Release|x86.Build.0 = Release|Any CPU
|
||||
{8B07FB7E-6C49-49F9-8919-5708E3C39907}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8B07FB7E-6C49-49F9-8919-5708E3C39907}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8B07FB7E-6C49-49F9-8919-5708E3C39907}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{8B07FB7E-6C49-49F9-8919-5708E3C39907}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{8B07FB7E-6C49-49F9-8919-5708E3C39907}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{8B07FB7E-6C49-49F9-8919-5708E3C39907}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{8B07FB7E-6C49-49F9-8919-5708E3C39907}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8B07FB7E-6C49-49F9-8919-5708E3C39907}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8B07FB7E-6C49-49F9-8919-5708E3C39907}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{8B07FB7E-6C49-49F9-8919-5708E3C39907}.Release|x64.Build.0 = Release|Any CPU
|
||||
{8B07FB7E-6C49-49F9-8919-5708E3C39907}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{8B07FB7E-6C49-49F9-8919-5708E3C39907}.Release|x86.Build.0 = Release|Any CPU
|
||||
{3C2B782A-19F7-4B2A-8FD1-9DEF0059FA2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3C2B782A-19F7-4B2A-8FD1-9DEF0059FA2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3C2B782A-19F7-4B2A-8FD1-9DEF0059FA2F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3C2B782A-19F7-4B2A-8FD1-9DEF0059FA2F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3C2B782A-19F7-4B2A-8FD1-9DEF0059FA2F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{3C2B782A-19F7-4B2A-8FD1-9DEF0059FA2F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{3C2B782A-19F7-4B2A-8FD1-9DEF0059FA2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3C2B782A-19F7-4B2A-8FD1-9DEF0059FA2F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3C2B782A-19F7-4B2A-8FD1-9DEF0059FA2F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3C2B782A-19F7-4B2A-8FD1-9DEF0059FA2F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3C2B782A-19F7-4B2A-8FD1-9DEF0059FA2F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3C2B782A-19F7-4B2A-8FD1-9DEF0059FA2F}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -248,14 +248,12 @@ internal sealed class AuthorityAckTokenKeyManager
|
||||
var previous = activeKey;
|
||||
var metadata = BuildMetadata(AuthoritySigningKeyStatus.Retired, ackOptions.KeyUse, previous.Key.Metadata);
|
||||
|
||||
var privateParameters = previous.Key.PrivateParameters;
|
||||
var retiredKey = new CryptoSigningKey(
|
||||
previous.Key.Reference,
|
||||
previous.Key.AlgorithmId,
|
||||
in privateParameters,
|
||||
previous.Key.CreatedAt,
|
||||
previous.Key.ExpiresAt,
|
||||
metadata);
|
||||
CryptoSigningKey retiredKey = previous.Key.Kind switch
|
||||
{
|
||||
CryptoSigningKeyKind.Ec => CreateEcRetiredKey(previous, metadata),
|
||||
CryptoSigningKeyKind.Raw => CreateRawRetiredKey(previous, metadata),
|
||||
_ => throw new InvalidOperationException($"Unsupported signing key kind '{previous.Key.Kind}' for retirement."),
|
||||
};
|
||||
|
||||
var provider = ResolveProvider(previous.ProviderName, retiredKey.AlgorithmId);
|
||||
provider.UpsertSigningKey(retiredKey);
|
||||
@@ -297,6 +295,36 @@ internal sealed class AuthorityAckTokenKeyManager
|
||||
});
|
||||
}
|
||||
|
||||
private static CryptoSigningKey CreateEcRetiredKey(RegisteredAckKey previous, IReadOnlyDictionary<string, string?> metadata)
|
||||
{
|
||||
var privateParameters = previous.Key.PrivateParameters;
|
||||
return new CryptoSigningKey(
|
||||
previous.Key.Reference,
|
||||
previous.Key.AlgorithmId,
|
||||
in privateParameters,
|
||||
previous.Key.CreatedAt,
|
||||
previous.Key.ExpiresAt,
|
||||
metadata);
|
||||
}
|
||||
|
||||
private static CryptoSigningKey CreateRawRetiredKey(RegisteredAckKey previous, IReadOnlyDictionary<string, string?> metadata)
|
||||
{
|
||||
var privateKey = previous.Key.PrivateKey;
|
||||
if (privateKey.IsEmpty)
|
||||
{
|
||||
throw new InvalidOperationException($"Ack signing key '{previous.Key.Reference.KeyId}' is missing backing material for retirement.");
|
||||
}
|
||||
|
||||
return new CryptoSigningKey(
|
||||
previous.Key.Reference,
|
||||
previous.Key.AlgorithmId,
|
||||
privateKey,
|
||||
previous.Key.CreatedAt,
|
||||
previous.Key.ExpiresAt,
|
||||
previous.Key.PublicKey,
|
||||
metadata);
|
||||
}
|
||||
|
||||
private CryptoSignerResolution ResolveSigner(RegisteredAckKey key)
|
||||
{
|
||||
var resolution = registry.ResolveSigner(
|
||||
|
||||
@@ -40,6 +40,14 @@ internal sealed class ConfigureAuthorityDiscoveryHandler : IOpenIddictServerHand
|
||||
StellaOpsScopes.AirgapStatusRead
|
||||
};
|
||||
|
||||
context.Metadata["stellaops_packs_scopes_supported"] = new[]
|
||||
{
|
||||
StellaOpsScopes.PacksRead,
|
||||
StellaOpsScopes.PacksWrite,
|
||||
StellaOpsScopes.PacksRun,
|
||||
StellaOpsScopes.PacksApprove
|
||||
};
|
||||
|
||||
context.Metadata["stellaops_notify_scopes_supported"] = new[]
|
||||
{
|
||||
StellaOpsScopes.NotifyViewer,
|
||||
|
||||
@@ -242,14 +242,12 @@ internal sealed class AuthoritySigningKeyManager
|
||||
["status"] = AuthoritySigningKeyStatus.Retired
|
||||
};
|
||||
|
||||
var privateParameters = previous.Key.PrivateParameters;
|
||||
var retiredKey = new CryptoSigningKey(
|
||||
previous.Key.Reference,
|
||||
previous.Key.AlgorithmId,
|
||||
in privateParameters,
|
||||
previous.Key.CreatedAt,
|
||||
previous.Key.ExpiresAt,
|
||||
metadata);
|
||||
CryptoSigningKey retiredKey = previous.Key.Kind switch
|
||||
{
|
||||
CryptoSigningKeyKind.Ec => CreateEcRetiredKey(previous, metadata),
|
||||
CryptoSigningKeyKind.Raw => CreateRawRetiredKey(previous, metadata),
|
||||
_ => throw new InvalidOperationException($"Unsupported signing key kind '{previous.Key.Kind}' for retirement."),
|
||||
};
|
||||
|
||||
var provider = ResolveProvider(previous.ProviderName, retiredKey.AlgorithmId);
|
||||
provider.UpsertSigningKey(retiredKey);
|
||||
@@ -350,6 +348,36 @@ internal sealed class AuthoritySigningKeyManager
|
||||
return string.IsNullOrWhiteSpace(provider) ? null : provider.Trim();
|
||||
}
|
||||
|
||||
private static CryptoSigningKey CreateEcRetiredKey(RegisteredSigningKey previous, Dictionary<string, string?> metadata)
|
||||
{
|
||||
var privateParameters = previous.Key.PrivateParameters;
|
||||
return new CryptoSigningKey(
|
||||
previous.Key.Reference,
|
||||
previous.Key.AlgorithmId,
|
||||
in privateParameters,
|
||||
previous.Key.CreatedAt,
|
||||
previous.Key.ExpiresAt,
|
||||
metadata);
|
||||
}
|
||||
|
||||
private static CryptoSigningKey CreateRawRetiredKey(RegisteredSigningKey previous, Dictionary<string, string?> metadata)
|
||||
{
|
||||
var privateKey = previous.Key.PrivateKey;
|
||||
if (privateKey.IsEmpty)
|
||||
{
|
||||
throw new InvalidOperationException($"Signing key '{previous.Key.Reference.KeyId}' is missing backing material for retirement.");
|
||||
}
|
||||
|
||||
return new CryptoSigningKey(
|
||||
previous.Key.Reference,
|
||||
previous.Key.AlgorithmId,
|
||||
privateKey,
|
||||
previous.Key.CreatedAt,
|
||||
previous.Key.ExpiresAt,
|
||||
previous.Key.PublicKey,
|
||||
metadata);
|
||||
}
|
||||
|
||||
private sealed record RegisteredSigningKey(
|
||||
CryptoSigningKey Key,
|
||||
string ProviderName,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using StellaOps.Cryptography;
|
||||
using StellaOps.Cryptography.Kms;
|
||||
|
||||
@@ -39,23 +41,51 @@ internal sealed class KmsAuthoritySigningKeySource : IAuthoritySigningKeySource
|
||||
|
||||
var material = _kmsClient.ExportAsync(keyId, versionId).GetAwaiter().GetResult();
|
||||
|
||||
var publicKey = new byte[material.Qx.Length + material.Qy.Length];
|
||||
Buffer.BlockCopy(material.Qx, 0, publicKey, 0, material.Qx.Length);
|
||||
Buffer.BlockCopy(material.Qy, 0, publicKey, material.Qx.Length, material.Qy.Length);
|
||||
|
||||
var publicKey = CombineCoordinates(material.Qx, material.Qy);
|
||||
var metadata = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
[KmsMetadataKeys.Version] = material.VersionId
|
||||
};
|
||||
|
||||
var reference = new CryptoKeyReference(request.KeyId, request.Provider);
|
||||
|
||||
if (material.D.Length == 0)
|
||||
{
|
||||
var privateHandle = Encoding.UTF8.GetBytes(string.IsNullOrWhiteSpace(material.VersionId) ? material.KeyId : material.VersionId);
|
||||
if (privateHandle.Length == 0)
|
||||
{
|
||||
privateHandle = publicKey.Length > 0
|
||||
? publicKey
|
||||
: throw new InvalidOperationException($"KMS key '{material.KeyId}' did not expose exportable material.");
|
||||
}
|
||||
|
||||
return new CryptoSigningKey(
|
||||
reference,
|
||||
material.Algorithm,
|
||||
privateHandle,
|
||||
material.CreatedAt,
|
||||
request.ExpiresAt,
|
||||
publicKey,
|
||||
metadata: metadata);
|
||||
}
|
||||
|
||||
var parameters = new ECParameters
|
||||
{
|
||||
Curve = ECCurve.NamedCurves.nistP256,
|
||||
D = material.D,
|
||||
Q = new ECPoint
|
||||
{
|
||||
X = material.Qx,
|
||||
Y = material.Qy,
|
||||
}
|
||||
};
|
||||
|
||||
return new CryptoSigningKey(
|
||||
reference,
|
||||
material.Algorithm,
|
||||
material.D,
|
||||
in parameters,
|
||||
material.CreatedAt,
|
||||
request.ExpiresAt,
|
||||
publicKey,
|
||||
metadata: metadata);
|
||||
}
|
||||
|
||||
@@ -63,4 +93,25 @@ internal sealed class KmsAuthoritySigningKeySource : IAuthoritySigningKeySource
|
||||
{
|
||||
public const string Version = "kms.version";
|
||||
}
|
||||
|
||||
private static byte[] CombineCoordinates(byte[] qx, byte[] qy)
|
||||
{
|
||||
if (qx.Length == 0 && qy.Length == 0)
|
||||
{
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
|
||||
var buffer = new byte[qx.Length + qy.Length];
|
||||
if (qx.Length > 0)
|
||||
{
|
||||
Buffer.BlockCopy(qx, 0, buffer, 0, qx.Length);
|
||||
}
|
||||
|
||||
if (qy.Length > 0)
|
||||
{
|
||||
Buffer.BlockCopy(qy, 0, buffer, qx.Length, qy.Length);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Authority Host Task Board — Epic 1: Aggregation-Only Contract
|
||||
| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |
|
||||
|----|--------|----------|------------|-------------|---------------|
|
||||
| SIGN-REPLAY-186-003 | TODO | Authority Core & Signing Guild | REPLAY-CORE-185-001 | Provide replay-aware DSSE profile configuration, RootPack selection, and multi-profile validation; document flow updates in `docs/modules/authority/architecture.md` referencing `docs/replay/DETERMINISTIC_REPLAY.md` Section 5. | Authority integration tests cover replay signing; docs merged; RootPack rotation guidance updated. |
|
||||
> 2025-10-26: Rate limiter metadata/audit records now include tenants, password grant scopes/tenants enforced, token persistence + tests updated. Docs refresh tracked via AUTH-AOC-19-003.
|
||||
> 2025-10-27: Client credential ingestion scopes now require tenant assignment; access token validation backfills tenants and rejects cross-tenant mismatches with tests.
|
||||
> 2025-10-27: `dotnet test` blocked — Concelier build fails (`AdvisoryObservationQueryService` returns `ImmutableHashSet<string?>`), preventing Authority test suite run; waiting on Concelier fix before rerun.
|
||||
@@ -72,7 +73,8 @@
|
||||
| AUTH-POLICY-27-002 | DONE (2025-11-02) | Authority Core & Security Guild | AUTH-POLICY-27-001, REGISTRY-API-27-007 | Provide attestation signing service bindings (OIDC token exchange, cosign integration) and enforce publish/promote scope checks, fresh-auth requirements, and audit logging. | Publish/promote requests require fresh auth + correct scopes; attestations signed with validated identity; audit logs enriched with digest + tenant; integration tests pass. |
|
||||
> Docs dependency: `DOCS-POLICY-27-009` awaiting signing guidance from this work.
|
||||
> 2025-11-02: Added `policy:publish`/`policy:promote` scopes with interactive-only enforcement, metadata parameters (`policy_reason`, `policy_ticket`, `policy_digest`), fresh-auth token validation, audit augmentations, and updated config/docs references.
|
||||
| AUTH-POLICY-27-003 | DOING (2025-11-02) | Authority Core & Docs Guild | AUTH-POLICY-27-001, AUTH-POLICY-27-002 | Update Authority configuration/docs for Policy Studio roles, signing policies, approval workflows, and CLI integration; include compliance checklist. | Docs merged; samples validated; governance checklist appended; release notes updated. |
|
||||
| AUTH-POLICY-27-003 | DONE (2025-11-03) | Authority Core & Docs Guild | AUTH-POLICY-27-001, AUTH-POLICY-27-002 | Update Authority configuration/docs for Policy Studio roles, signing policies, approval workflows, and CLI integration; include compliance checklist. | Docs merged; samples validated; governance checklist appended; release notes updated. |
|
||||
> 2025-11-03: Authority/policy docs refreshed for publish/promote metadata, DSSE signing workflow, CLI commands, and compliance checklist alignment.
|
||||
|
||||
## Exceptions v1
|
||||
|
||||
@@ -93,8 +95,10 @@
|
||||
|----|--------|----------|------------|-------------|---------------|
|
||||
| AUTH-VULN-29-001 | DONE (2025-11-03) | Authority Core & Security Guild | AUTH-POLICY-27-001 | Define Vuln Explorer scopes/roles (`vuln:view`, `vuln:investigate`, `vuln:operate`, `vuln:audit`) with ABAC attributes (env, owner, business_tier) and update discovery metadata/offline kit defaults. | Roles/scopes published; issuer templates updated; integration tests cover ABAC filters; docs refreshed. |
|
||||
| AUTH-VULN-29-002 | DONE (2025-11-03) | Authority Core & Security Guild | AUTH-VULN-29-001, LEDGER-29-002 | Enforce CSRF/anti-forgery tokens for workflow actions, sign attachment tokens, and record audit logs with ledger event hashes. | Workflow calls require valid tokens; audit logs include ledger references; security tests cover token expiry/abuse. |
|
||||
| AUTH-VULN-29-003 | DOING (2025-11-03) | Authority Core & Docs Guild | AUTH-VULN-29-001..002 | Update security docs/config samples for Vuln Explorer roles, ABAC policies, attachment signing, and ledger verification guidance. | Docs merged with compliance checklist; configuration examples validated; release notes updated. |
|
||||
| AUTH-VULN-29-003 | DONE (2025-11-03) | Authority Core & Docs Guild | AUTH-VULN-29-001..002 | Update security docs/config samples for Vuln Explorer roles, ABAC policies, attachment signing, and ledger verification guidance. | Docs merged with compliance checklist; configuration examples validated; release notes updated. |
|
||||
> 2025-11-03: `docs/11_AUTHORITY.md`, `docs/security/authority-scopes.md`, Vuln Explorer architecture, and release updates refreshed; proofread post-build.
|
||||
> 2025-11-03: Vuln workflow CSRF + attachment token services live with audit enrichment and negative-path tests. Awaiting completion of full Authority suite run after repository-wide build finishes.
|
||||
> 2025-11-03: Continuing doc/config/release-note updates for Vuln Explorer roles, ABAC enforcement, attachment signing, and ledger verification guidance.
|
||||
|
||||
## Advisory AI (Sprint 31)
|
||||
|
||||
@@ -120,22 +124,24 @@
|
||||
## CLI Parity & Task Packs
|
||||
| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |
|
||||
|----|--------|----------|------------|-------------|---------------|
|
||||
| AUTH-PACKS-41-001 | DOING (2025-11-02) | Authority Core & Security Guild | AUTH-AOC-19-001 | Define CLI SSO profiles and pack scopes (`Packs.Read`, `Packs.Write`, `Packs.Run`, `Packs.Approve`), update discovery metadata, offline defaults, and issuer templates. | Scopes available; metadata updated; tests ensure enforcement; offline kit templates refreshed. |
|
||||
| AUTH-PACKS-41-001 | DONE (2025-11-03) | Authority Core & Security Guild | AUTH-AOC-19-001 | Define CLI SSO profiles and pack scopes (`Packs.Read`, `Packs.Write`, `Packs.Run`, `Packs.Approve`), update discovery metadata, offline defaults, and issuer templates. | Scopes available; metadata updated; tests ensure enforcement; offline kit templates refreshed. |
|
||||
> 2025-11-02: Added Pack scope policies, Authority role defaults, and CLI profile guidance covering operator/publisher/approver flows.
|
||||
> 2025-11-02: Shared OpenSSL 1.1 shim feeds Authority & Signals Mongo2Go harnesses so pack scope coverage keeps running on OpenSSL 3 hosts (AUTH-PACKS-41-001).
|
||||
> 2025-11-03: Discovery metadata now emits `stellaops_packs_scopes_supported`; OpenAPI scope catalog and Authority tests updated. Offline kit + issuer templates already include `packs.*` roles.
|
||||
| AUTH-PACKS-43-001 | BLOCKED (2025-10-27) | Authority Core & Security Guild | AUTH-PACKS-41-001, TASKRUN-42-001, ORCH-SVC-42-101 | Enforce pack signing policies, approval RBAC checks, CLI CI token scopes, and audit logging for approvals. | Signing policies enforced; approvals require correct roles; CI token scope tests pass; audit logs recorded. |
|
||||
> Blocked: Pack scopes (`AUTH-PACKS-41-001`) and Task Runner pack approvals (`ORCH-SVC-42-101`, `TASKRUN-42-001`) are still TODO. Authority lacks baseline `Packs.*` scope definitions and approval/audit endpoints to enforce policies. Revisit once dependent teams deliver scope catalog + Task Runner approval API.
|
||||
> Blocked: Awaiting Task Runner approval API (`ORCH-SVC-42-101`, `TASKRUN-42-001`) before enforcing pack approval workflows; Authority scope catalog + discovery metadata ready.
|
||||
|
||||
## Authority-Backed Scopes & Tenancy (Epic 14)
|
||||
| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |
|
||||
|----|--------|----------|------------|-------------|---------------|
|
||||
> 2025-10-28: Tidied advisory raw idempotency migration to avoid LINQ-on-`BsonValue` (explicit array copy) while continuing duplicate guardrail validation; scoped scanner/policy token call sites updated to honor new metadata parameter.
|
||||
| AUTH-TEN-49-001 | DOING (2025-11-02) | Authority Core & Security Guild | AUTH-TEN-47-001 | Implement service accounts & delegation tokens (`act` chain), per-tenant quotas, audit stream of auth decisions, and revocation APIs. | Service tokens minted with scopes/TTL; delegation logged; quotas configurable; audit stream live; docs updated. |
|
||||
| AUTH-TEN-49-001 | DONE (2025-11-03) | Authority Core & Security Guild | AUTH-TEN-47-001 | Implement service accounts & delegation tokens (`act` chain), per-tenant quotas, audit stream of auth decisions, and revocation APIs. | Service tokens minted with scopes/TTL; delegation logged; quotas configurable; audit stream live; docs updated. |
|
||||
> 2025-11-03: Delegation quota/persistence tests added (`ServiceAccountAdminEndpointsTests`, `DelegationTokenAuditTests`), Authority suite re-run successfully.
|
||||
> 2025-11-02: Authority bootstrap test harness now seeds service accounts via AuthorityDelegation options; `/internal/service-accounts` endpoints validated with targeted vstest run.
|
||||
> 2025-11-02: Added Mongo service-account store, seeded options/collection initializers, token persistence metadata (`tokenKind`, `serviceAccountId`, `actorChain`), and docs/config samples. Introduced quota checks + tests covering service account issuance and persistence.
|
||||
> 2025-11-02: Documented bootstrap service-account admin APIs in `docs/11_AUTHORITY.md`, noting API key requirements and stable upsert behaviour.
|
||||
> 2025-11-03: Seeded explicit enabled service-account fixtures for integration tests and reran `StellaOps.Authority.Tests` to greenlight `/internal/service-accounts` listing + revocation scenarios.
|
||||
|
||||
> 2025-11-03: Continuing to extend delegation token persistence/quota tests and audit coverage prior to completion (Authority Core & Security Guild).
|
||||
## Observability & Forensics (Epic 15)
|
||||
|
||||
| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |
|
||||
@@ -148,10 +154,12 @@
|
||||
|
||||
| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |
|
||||
|----|--------|----------|------------|-------------|---------------|
|
||||
| AUTH-AIRGAP-56-001 | DOING (2025-11-01) | Authority Core & Security Guild | AIRGAP-CTL-56-001 | Provision new scopes (`airgap:seal`, `airgap:import`, `airgap:status:read`) in configuration metadata, offline kit defaults, and issuer templates. | Scopes exposed in discovery docs; offline kit updated; integration tests cover issuance. |
|
||||
| AUTH-AIRGAP-56-002 | DOING | Authority Core & Security Guild | AUTH-AIRGAP-56-001, AIRGAP-IMP-58-001 | Audit import actions with actor, tenant, bundle ID, and trace ID; expose `/authority/audit/airgap` endpoint. | Audit records persisted; endpoint paginates results; tests cover RBAC + filtering. |
|
||||
| AUTH-AIRGAP-56-001 | DONE (2025-11-03) | Authority Core & Security Guild | AIRGAP-CTL-56-001 | Provision new scopes (`airgap:seal`, `airgap:import`, `airgap:status:read`) in configuration metadata, offline kit defaults, and issuer templates. | Scopes exposed in discovery docs; offline kit updated; integration tests cover issuance. |
|
||||
| AUTH-AIRGAP-56-002 | DONE (2025-11-03) | Authority Core & Security Guild | AUTH-AIRGAP-56-001, AIRGAP-IMP-58-001 | Audit import actions with actor, tenant, bundle ID, and trace ID; expose `/authority/audit/airgap` endpoint. | Audit records persisted; endpoint paginates results; tests cover RBAC + filtering. |
|
||||
| AUTH-AIRGAP-57-001 | BLOCKED (2025-11-01) | Authority Core & Security Guild, DevOps Guild | AUTH-AIRGAP-56-001, DEVOPS-AIRGAP-57-002 | Enforce sealed-mode CI gating by refusing token issuance when declared sealed install lacks sealing confirmation. | Awaiting clarified sealed-confirmation contract and configuration structure before implementation. |
|
||||
> 2025-11-01: AUTH-AIRGAP-57-001 blocked pending guidance on sealed-confirmation contract and configuration expectations before gating changes (Authority Core & Security Guild, DevOps Guild).
|
||||
> 2025-11-03: Air-gap scopes wired through discovery metadata (`stellaops_airgap_scopes_supported`), sample configs, issuer templates, and offline kit roles; Authority OpenID discovery tests updated.
|
||||
> 2025-11-03: `/authority/audit/airgap` endpoint finalized with Mongo-backed store, pagination/filters, and RBAC coverage in `AirgapAuditEndpointsTests`; Authority suite passing.
|
||||
|
||||
## SDKs & OpenAPI (Epic 17)
|
||||
| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |
|
||||
|
||||
Reference in New Issue
Block a user