Files
git.stella-ops.org/src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/UnsupportedRuntimeWiringTests.cs
master 34e70d9090 wip(concelier): adjust unsupported runtime wiring test after guard removal
Follow-up to SPRINT_20260419_027_Concelier_durable_affected_symbol_runtime.

UnsupportedRuntimeWiringTests updated for the removed non-testing
UnsupportedAffectedSymbol registration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:47:01 +03:00

608 lines
26 KiB
C#

using FluentAssertions;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Auth.Abstractions;
using StellaOps.Concelier.Core.Federation;
using StellaOps.Concelier.Core.Jobs;
using StellaOps.Concelier.Core.Observations;
using StellaOps.Concelier.Core.Orchestration;
using StellaOps.Concelier.Core.Raw;
using StellaOps.Concelier.Core.Signals;
using StellaOps.Concelier.Persistence.Postgres.Repositories;
using StellaOps.Replay.Core.FeedSnapshot;
using StellaOps.Concelier.WebService.Extensions;
using StellaOps.Concelier.WebService.Services;
using Moq;
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Security.Claims;
using System.Text.Json;
using System.Text.Encodings.Web;
using Xunit;
namespace StellaOps.Concelier.WebService.Tests;
public sealed class UnsupportedRuntimeWiringTests
{
[Fact]
public void DevelopmentHost_ResolvesUnsupportedRuntimeServices()
{
using var factory = new UnsupportedRuntimeWebApplicationFactory();
using var scope = factory.Services.CreateScope();
scope.ServiceProvider.GetRequiredService<IJobStore>().Should().BeOfType<UnsupportedJobStore>();
scope.ServiceProvider.GetRequiredService<IJobCoordinator>().Should().BeOfType<UnsupportedJobCoordinator>();
scope.ServiceProvider.GetRequiredService<IOrchestratorRegistryStore>().Should().BeOfType<UnsupportedOrchestratorRegistryStore>();
scope.ServiceProvider.GetRequiredService<IAffectedSymbolStore>().Should().BeOfType<PostgresAffectedSymbolStore>();
scope.ServiceProvider.GetRequiredService<IAffectedSymbolProvider>().Should().BeOfType<AffectedSymbolProvider>();
factory.HasJobSchedulerHostedService.Should().BeFalse();
}
[Fact]
public async Task DevelopmentHost_JobsEndpoint_ReturnsNotImplemented()
{
using var factory = new UnsupportedRuntimeWebApplicationFactory();
using var client = factory.CreateClient();
var response = await client.GetAsync("/jobs/definitions");
var body = await response.Content.ReadAsStringAsync();
response.StatusCode.Should().Be(System.Net.HttpStatusCode.NotImplemented);
body.Should().Contain("NOT_IMPLEMENTED");
body.Should().Contain("durable backend implementation");
}
[Fact]
public async Task DevelopmentHost_SourceSyncEndpoint_ReturnsNotImplemented()
{
using var factory = new UnsupportedRuntimeWebApplicationFactory();
using var client = factory.CreateClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "/api/v1/advisory-sources/nvd/sync");
request.Headers.Add(Program.TenantHeaderName, "tenant-a");
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
response.StatusCode.Should().Be(System.Net.HttpStatusCode.NotImplemented);
body.Should().Contain("NOT_IMPLEMENTED");
body.Should().Contain("durable backend implementation");
}
[Fact]
public async Task DevelopmentHost_BatchSourceSyncEndpoint_ReturnsNotImplemented()
{
using var factory = new UnsupportedRuntimeWebApplicationFactory();
using var client = factory.CreateClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "/api/v1/advisory-sources/sync");
request.Headers.Add(Program.TenantHeaderName, "tenant-a");
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
response.StatusCode.Should().Be(System.Net.HttpStatusCode.NotImplemented);
body.Should().Contain("NOT_IMPLEMENTED");
body.Should().Contain("durable backend implementation");
}
[Fact]
public async Task DevelopmentHost_InternalOrchestratorEndpoint_ReturnsNotImplemented()
{
using var factory = new UnsupportedRuntimeWebApplicationFactory();
using var client = factory.CreateClient();
using var request = new HttpRequestMessage(
HttpMethod.Get,
$"/internal/orch/commands?connectorId=nvd&runId={Guid.NewGuid()}");
request.Headers.Add(Program.TenantHeaderName, "tenant-a");
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
response.StatusCode.Should().Be(System.Net.HttpStatusCode.NotImplemented);
body.Should().Contain("NOT_IMPLEMENTED");
body.Should().Contain("orchestrator registry");
}
[Fact]
public async Task DevelopmentHost_ConfigSeededMirrorEndpoints_IgnoreConfiguredDomains()
{
using var temp = new TemporaryDirectory();
var exportId = "20251019T120000Z";
var domainRoot = Path.Combine(temp.Path, exportId, "mirror", "primary");
Directory.CreateDirectory(domainRoot);
await File.WriteAllTextAsync(
Path.Combine(temp.Path, exportId, "mirror", "index.json"),
"""{"schemaVersion":1,"domains":[{"id":"primary"}]}""");
await File.WriteAllTextAsync(
Path.Combine(domainRoot, "manifest.json"),
"""{"domainId":"primary"}""");
using var factory = new UnsupportedRuntimeWebApplicationFactory(new Dictionary<string, string?>
{
["CONCELIER_MIRROR__ENABLED"] = "true",
["CONCELIER_MIRROR__EXPORTROOT"] = temp.Path,
["CONCELIER_MIRROR__ACTIVEEXPORTID"] = exportId,
["CONCELIER_MIRROR__DOMAINS__0__ID"] = "primary",
["CONCELIER_MIRROR__DOMAINS__0__REQUIREAUTHENTICATION"] = "false",
["CONCELIER_MIRROR__MAXINDEXREQUESTSPERHOUR"] = "5",
});
using var client = factory.CreateClient();
using var indexRequest = new HttpRequestMessage(HttpMethod.Get, "/concelier/exports/index.json");
indexRequest.Headers.Add(Program.TenantHeaderName, "tenant-a");
var indexResponse = await client.SendAsync(indexRequest);
using var manifestRequest = new HttpRequestMessage(HttpMethod.Get, "/concelier/exports/mirror/primary/manifest.json");
manifestRequest.Headers.Add(Program.TenantHeaderName, "tenant-a");
var manifestResponse = await client.SendAsync(manifestRequest);
indexResponse.StatusCode.Should().Be(System.Net.HttpStatusCode.NotFound);
manifestResponse.StatusCode.Should().Be(System.Net.HttpStatusCode.NotFound);
}
[Fact]
public async Task DevelopmentHost_MirrorImportEndpoints_ImportBundleAndExposeMirrorArtifacts()
{
using var source = new TemporaryDirectory();
using var exportRoot = new TemporaryDirectory();
var exportId = "mirror-import-proof";
var bundleJson = """
{
"schemaVersion": 1,
"generatedAt": "2026-04-18T00:00:00Z",
"domainId": "primary",
"displayName": "Primary",
"exportFormat": "JSON",
"sourceIds": ["nvd"],
"advisories": [
{
"id": "CVE-2026-0001"
}
]
}
""";
var bundlePath = Path.Combine(source.Path, "bundle.json");
await File.WriteAllTextAsync(bundlePath, bundleJson);
var bundleDigest = Convert.ToHexString(SHA256.HashData(await File.ReadAllBytesAsync(bundlePath))).ToLowerInvariant();
var manifestJson = $$"""
{
"domainId": "primary",
"displayName": "Primary",
"generatedAt": "2026-04-18T00:00:00Z",
"exports": [
{
"key": "primary",
"exportId": "primary",
"format": "JSON",
"artifactSizeBytes": {{new FileInfo(bundlePath).Length}},
"artifactDigest": "sha256:{{bundleDigest}}"
}
]
}
""";
await File.WriteAllTextAsync(Path.Combine(source.Path, "manifest.json"), manifestJson);
using var factory = new UnsupportedRuntimeWebApplicationFactory(new Dictionary<string, string?>
{
["CONCELIER_MIRROR__ENABLED"] = "true",
["CONCELIER_MIRROR__EXPORTROOT"] = exportRoot.Path,
["CONCELIER_MIRROR__IMPORTROOT"] = source.Path,
["CONCELIER_MIRROR__ACTIVEEXPORTID"] = exportId,
});
using var client = factory.CreateClient();
using var importRequest = new HttpRequestMessage(HttpMethod.Post, "/api/v1/advisory-sources/mirror/import")
{
Content = JsonContent.Create(new { bundlePath = source.Path })
};
importRequest.Headers.Add(Program.TenantHeaderName, "tenant-a");
var importResponse = await client.SendAsync(importRequest);
var importBody = await importResponse.Content.ReadAsStringAsync();
using var statusRequest = new HttpRequestMessage(HttpMethod.Get, "/api/v1/advisory-sources/mirror/import/status");
statusRequest.Headers.Add(Program.TenantHeaderName, "tenant-a");
var statusResponse = await client.SendAsync(statusRequest);
var statusBody = await statusResponse.Content.ReadAsStringAsync();
using var manifestRequest = new HttpRequestMessage(HttpMethod.Get, "/concelier/exports/mirror/primary/manifest.json");
manifestRequest.Headers.Add(Program.TenantHeaderName, "tenant-a");
var manifestResponse = await client.SendAsync(manifestRequest);
var manifestBody = await manifestResponse.Content.ReadAsStringAsync();
importResponse.StatusCode.Should().Be(System.Net.HttpStatusCode.OK, importBody);
importBody.Should().Contain("completed");
statusResponse.StatusCode.Should().Be(System.Net.HttpStatusCode.OK, statusBody);
statusBody.Should().Contain("\"hasImport\":true");
statusBody.Should().Contain("\"status\":\"completed\"");
statusBody.Should().Contain("\"domainId\":\"primary\"");
statusBody.Should().Contain("Detached JWS signature was not found");
manifestResponse.StatusCode.Should().Be(System.Net.HttpStatusCode.OK, manifestBody);
using var manifestDocument = JsonDocument.Parse(manifestBody);
manifestDocument.RootElement.GetProperty("domainId").GetString().Should().Be("primary");
}
[Fact]
public async Task DevelopmentHost_MirrorImportEndpoints_RejectChecksumMismatch()
{
using var source = new TemporaryDirectory();
using var exportRoot = new TemporaryDirectory();
var bundlePath = Path.Combine(source.Path, "bundle.json");
await File.WriteAllTextAsync(
bundlePath,
"""
{
"schemaVersion": 1,
"generatedAt": "2026-04-18T00:00:00Z",
"domainId": "primary",
"displayName": "Primary",
"exportFormat": "JSON",
"sourceIds": ["nvd"],
"advisories": []
}
""");
var bundleLength = new FileInfo(bundlePath).Length;
await File.WriteAllTextAsync(
Path.Combine(source.Path, "manifest.json"),
$$"""
{
"domainId": "primary",
"displayName": "Primary",
"generatedAt": "2026-04-18T00:00:00Z",
"exports": [
{
"key": "primary",
"exportId": "primary",
"format": "JSON",
"artifactSizeBytes": {{bundleLength}},
"artifactDigest": "sha256:deadbeef"
}
]
}
""");
using var factory = new UnsupportedRuntimeWebApplicationFactory(new Dictionary<string, string?>
{
["CONCELIER_MIRROR__ENABLED"] = "true",
["CONCELIER_MIRROR__EXPORTROOT"] = exportRoot.Path,
["CONCELIER_MIRROR__IMPORTROOT"] = source.Path,
["CONCELIER_MIRROR__ACTIVEEXPORTID"] = "mirror-import-failure",
});
using var client = factory.CreateClient();
using var importRequest = new HttpRequestMessage(HttpMethod.Post, "/api/v1/advisory-sources/mirror/import")
{
Content = JsonContent.Create(new { bundlePath = source.Path })
};
importRequest.Headers.Add(Program.TenantHeaderName, "tenant-a");
var importResponse = await client.SendAsync(importRequest);
var importBody = await importResponse.Content.ReadAsStringAsync();
using var statusRequest = new HttpRequestMessage(HttpMethod.Get, "/api/v1/advisory-sources/mirror/import/status");
statusRequest.Headers.Add(Program.TenantHeaderName, "tenant-a");
var statusResponse = await client.SendAsync(statusRequest);
var statusBody = await statusResponse.Content.ReadAsStringAsync();
importResponse.StatusCode.Should().Be(System.Net.HttpStatusCode.BadRequest, importBody);
importBody.Should().Contain("\"status\":\"failed\"");
importBody.Should().Contain("digest mismatch");
statusResponse.StatusCode.Should().Be(System.Net.HttpStatusCode.OK, statusBody);
statusBody.Should().Contain("\"status\":\"failed\"");
statusBody.Should().Contain("digest mismatch");
}
[Fact]
public async Task DevelopmentHost_MirrorImportEndpoints_RejectPathsOutsideAllowlistedImportRoot()
{
using var source = new TemporaryDirectory();
using var importRoot = new TemporaryDirectory();
using var exportRoot = new TemporaryDirectory();
await File.WriteAllTextAsync(
Path.Combine(source.Path, "bundle.json"),
"""
{
"schemaVersion": 1,
"generatedAt": "2026-04-18T00:00:00Z",
"domainId": "primary",
"displayName": "Primary",
"exportFormat": "JSON",
"sourceIds": ["nvd"],
"advisories": []
}
""");
await File.WriteAllTextAsync(
Path.Combine(source.Path, "manifest.json"),
"""
{
"domainId": "primary",
"displayName": "Primary",
"generatedAt": "2026-04-18T00:00:00Z",
"exports": [
{
"key": "primary",
"exportId": "primary",
"format": "JSON",
"artifactSizeBytes": 120,
"artifactDigest": "sha256:deadbeef"
}
]
}
""");
using var factory = new UnsupportedRuntimeWebApplicationFactory(new Dictionary<string, string?>
{
["CONCELIER_MIRROR__ENABLED"] = "true",
["CONCELIER_MIRROR__EXPORTROOT"] = exportRoot.Path,
["CONCELIER_MIRROR__IMPORTROOT"] = importRoot.Path,
["CONCELIER_MIRROR__ACTIVEEXPORTID"] = "mirror-import-allowlist",
});
using var client = factory.CreateClient();
using var importRequest = new HttpRequestMessage(HttpMethod.Post, "/api/v1/advisory-sources/mirror/import")
{
Content = JsonContent.Create(new { bundlePath = source.Path })
};
importRequest.Headers.Add(Program.TenantHeaderName, "tenant-a");
var importResponse = await client.SendAsync(importRequest);
var importBody = await importResponse.Content.ReadAsStringAsync();
importResponse.StatusCode.Should().Be(System.Net.HttpStatusCode.BadRequest, importBody);
importBody.Should().Contain("\"status\":\"failed\"");
importBody.Should().Contain("must stay within the configured mirror import root");
}
private sealed class UnsupportedRuntimeWebApplicationFactory : WebApplicationFactory<Program>
{
private readonly Dictionary<string, string?> _previousEnvironment = new(StringComparer.OrdinalIgnoreCase);
public bool HasJobSchedulerHostedService { get; private set; }
public UnsupportedRuntimeWebApplicationFactory(IReadOnlyDictionary<string, string?>? environmentOverrides = null)
{
SetEnvironmentVariable("CONCELIER_POSTGRESSTORAGE__CONNECTIONSTRING", "Host=localhost;Port=5432;Database=concelier_test;Username=postgres;Password=postgres");
SetEnvironmentVariable("CONCELIER_POSTGRESSTORAGE__ENABLED", "true");
SetEnvironmentVariable("CONCELIER_POSTGRESSTORAGE__COMMANDTIMEOUTSECONDS", "30");
SetEnvironmentVariable("CONCELIER_POSTGRESSTORAGE__SCHEMANAME", "vuln");
SetEnvironmentVariable("CONCELIER_POSTGRES_DSN", "Host=localhost;Port=5432;Database=concelier_test;Username=postgres;Password=postgres");
SetEnvironmentVariable("CONCELIER_SKIP_OPTIONS_VALIDATION", "1");
SetEnvironmentVariable("CONCELIER_TELEMETRY__ENABLED", "false");
SetEnvironmentVariable("CONCELIER_TELEMETRY__ENABLELOGGING", "false");
SetEnvironmentVariable("CONCELIER_TELEMETRY__ENABLETRACING", "false");
SetEnvironmentVariable("CONCELIER_TELEMETRY__ENABLEMETRICS", "false");
SetEnvironmentVariable("CONCELIER_AUTHORITY__ENABLED", "false");
SetEnvironmentVariable("CONCELIER_AUTHORITY__REQUIREDSCOPES__0", "concelier.jobs.trigger");
SetEnvironmentVariable("DOTNET_ENVIRONMENT", "Production");
SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Production");
if (environmentOverrides is not null)
{
foreach (var entry in environmentOverrides)
{
SetEnvironmentVariable(entry.Key, entry.Value);
}
}
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Production");
builder.ConfigureServices(services =>
{
HasJobSchedulerHostedService = services.Any(descriptor =>
descriptor.ServiceType == typeof(IHostedService) &&
descriptor.ImplementationType == typeof(JobSchedulerHostedService));
services.AddHttpContextAccessor();
services.AddAuthentication("Test")
.AddScheme<AuthenticationSchemeOptions, AlwaysAllowAuthHandler>("Test", _ => { })
.AddScheme<AuthenticationSchemeOptions, AlwaysAllowAuthHandler>("StellaOpsBearer", _ => { });
services.AddAuthorization(options =>
{
options.AddPolicy("Concelier.Sources.Manage", policy => policy.RequireAssertion(_ => true));
options.AddPolicy("Concelier.Advisories.Read", policy => policy.RequireAssertion(_ => true));
});
services.TryAddSingleton<Func<Guid>>(_ => () => Guid.Parse("11111111-1111-1111-1111-111111111111"));
services.RemoveAll<IAdvisoryRawService>();
services.RemoveAll<IAdvisoryObservationQueryService>();
services.RemoveAll<ISnapshotIngestionOrchestrator>();
services.TryAddSingleton(Mock.Of<IAdvisoryRawRepository>());
services.TryAddSingleton(Mock.Of<IAdvisoryObservationLookup>());
services.TryAddSingleton(Mock.Of<IAdvisoryObservationSink>());
services.TryAddSingleton(CreateFeedSnapshotCoordinator());
services.RemoveAll<IMirrorDomainStore>();
services.AddSingleton<IMirrorDomainStore, InMemoryMirrorDomainStore>();
services.RemoveAll<IMirrorBundleImportStore>();
services.AddSingleton<IMirrorBundleImportStore, InMemoryMirrorBundleImportStore>();
services.RemoveAll<IHostedService>();
});
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
foreach (var entry in _previousEnvironment)
{
Environment.SetEnvironmentVariable(entry.Key, entry.Value);
}
}
base.Dispose(disposing);
}
private void SetEnvironmentVariable(string name, string? value)
{
_previousEnvironment[name] = Environment.GetEnvironmentVariable(name);
Environment.SetEnvironmentVariable(name, value);
}
private static IFeedSnapshotCoordinator CreateFeedSnapshotCoordinator()
{
var mock = new Mock<IFeedSnapshotCoordinator>();
mock.SetupGet(x => x.RegisteredSources).Returns(Array.Empty<string>());
return mock.Object;
}
}
private sealed class InMemoryMirrorDomainStore : IMirrorDomainStore
{
private readonly object _sync = new();
private readonly Dictionary<string, MirrorDomainRecord> _domains = new(StringComparer.OrdinalIgnoreCase);
public IReadOnlyList<MirrorDomainRecord> GetAllDomains()
{
lock (_sync)
{
return _domains.Values.Select(Clone).ToArray();
}
}
public MirrorDomainRecord? GetDomain(string domainId)
{
lock (_sync)
{
return _domains.TryGetValue(domainId, out var domain) ? Clone(domain) : null;
}
}
public Task SaveDomainAsync(MirrorDomainRecord domain, CancellationToken ct = default)
{
ct.ThrowIfCancellationRequested();
lock (_sync)
{
_domains[domain.Id] = Clone(domain);
}
return Task.CompletedTask;
}
public Task DeleteDomainAsync(string domainId, CancellationToken ct = default)
{
ct.ThrowIfCancellationRequested();
lock (_sync)
{
_domains.Remove(domainId);
}
return Task.CompletedTask;
}
private static MirrorDomainRecord Clone(MirrorDomainRecord domain) => new()
{
Id = domain.Id,
DisplayName = domain.DisplayName,
SourceIds = [.. domain.SourceIds],
ExportFormat = domain.ExportFormat,
RequireAuthentication = domain.RequireAuthentication,
MaxIndexRequestsPerHour = domain.MaxIndexRequestsPerHour,
MaxDownloadRequestsPerHour = domain.MaxDownloadRequestsPerHour,
SigningEnabled = domain.SigningEnabled,
SigningAlgorithm = domain.SigningAlgorithm,
SigningKeyId = domain.SigningKeyId,
Exports = domain.Exports
.Select(export => new MirrorExportRecord
{
Key = export.Key,
Format = export.Format,
Filters = new Dictionary<string, string>(export.Filters, StringComparer.Ordinal),
})
.ToList(),
CreatedAt = domain.CreatedAt,
UpdatedAt = domain.UpdatedAt,
LastGeneratedAt = domain.LastGeneratedAt,
LastGenerateTriggeredAt = domain.LastGenerateTriggeredAt,
BundleSizeBytes = domain.BundleSizeBytes,
AdvisoryCount = domain.AdvisoryCount,
};
}
private sealed class InMemoryMirrorBundleImportStore : IMirrorBundleImportStore
{
private MirrorImportStatusRecord? _status;
public MirrorImportStatusRecord? GetLatestStatus() => _status;
public void SetStatus(MirrorImportStatusRecord status) => _status = status;
}
private sealed class TemporaryDirectory : IDisposable
{
public TemporaryDirectory()
{
Path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "concelier-mirror-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(Path);
}
public string Path { get; }
public void Dispose()
{
try
{
if (Directory.Exists(Path))
{
Directory.Delete(Path, recursive: true);
}
}
catch (IOException)
{
}
catch (UnauthorizedAccessException)
{
}
}
}
private sealed class AlwaysAllowAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public AlwaysAllowAuthHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder)
: base(options, logger, encoder)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var scopes = new[]
{
StellaOpsScopes.ConcelierJobsTrigger,
StellaOpsScopes.AdvisoryRead,
StellaOpsScopes.IntegrationWrite,
StellaOpsScopes.IntegrationOperate,
};
var identity = new ClaimsIdentity(
[
new Claim(ClaimTypes.NameIdentifier, "unsupported-runtime"),
new Claim(ClaimTypes.Name, "unsupported-runtime"),
new Claim(StellaOpsClaimTypes.Tenant, "tenant-a"),
new Claim(StellaOpsClaimTypes.Scope, string.Join(' ', scopes)),
new Claim(StellaOpsClaimTypes.ScopeItem, StellaOpsScopes.ConcelierJobsTrigger),
new Claim(StellaOpsClaimTypes.ScopeItem, StellaOpsScopes.AdvisoryRead),
new Claim(StellaOpsClaimTypes.ScopeItem, StellaOpsScopes.IntegrationWrite),
new Claim(StellaOpsClaimTypes.ScopeItem, StellaOpsScopes.IntegrationOperate),
], Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
}
}