Add tenant context interfaces for multi-tenant operations and user context management. Refactor logging in webhook endpoints and improve async method calls in repositories for better readability and performance.

This commit is contained in:
StellaOps Bot
2025-12-29 20:07:59 +02:00
parent 1b61c72c90
commit 7825a79083
6 changed files with 49 additions and 14 deletions

View File

@@ -3,12 +3,14 @@ using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using StellaOps.Auth.Abstractions; using StellaOps.Auth.Abstractions;
using StellaOps.Scanner.Sources.Configuration;
using StellaOps.Scanner.Sources.Contracts; using StellaOps.Scanner.Sources.Contracts;
using StellaOps.Scanner.Sources.Domain; using StellaOps.Scanner.Sources.Domain;
using StellaOps.Scanner.Sources.Services; using StellaOps.Scanner.Sources.Services;
using StellaOps.Scanner.WebService.Constants; using StellaOps.Scanner.WebService.Constants;
using StellaOps.Scanner.WebService.Infrastructure; using StellaOps.Scanner.WebService.Infrastructure;
using StellaOps.Scanner.WebService.Security; using StellaOps.Scanner.WebService.Security;
using StellaOps.Scanner.WebService.Tenancy;
namespace StellaOps.Scanner.WebService.Endpoints; namespace StellaOps.Scanner.WebService.Endpoints;

View File

@@ -15,6 +15,11 @@ using StellaOps.Scanner.WebService.Infrastructure;
namespace StellaOps.Scanner.WebService.Endpoints; namespace StellaOps.Scanner.WebService.Endpoints;
/// <summary>
/// Marker class for webhook endpoint logging.
/// </summary>
internal sealed class WebhookEndpointLogger;
/// <summary> /// <summary>
/// Endpoints for receiving webhooks from container registries and Git providers. /// Endpoints for receiving webhooks from container registries and Git providers.
/// </summary> /// </summary>
@@ -98,7 +103,7 @@ internal static class WebhookEndpoints
IEnumerable<ISourceTypeHandler> handlers, IEnumerable<ISourceTypeHandler> handlers,
ISourceTriggerDispatcher dispatcher, ISourceTriggerDispatcher dispatcher,
ICredentialResolver credentialResolver, ICredentialResolver credentialResolver,
ILogger<WebhookEndpoints> logger, ILogger<WebhookEndpointLogger> logger,
HttpContext context, HttpContext context,
CancellationToken ct) CancellationToken ct)
{ {
@@ -140,7 +145,7 @@ internal static class WebhookEndpoints
logger.LogWarning("Webhook received without signature for source {SourceId}", sourceId); logger.LogWarning("Webhook received without signature for source {SourceId}", sourceId);
return ProblemResultFactory.Create( return ProblemResultFactory.Create(
context, context,
ProblemTypes.Unauthorized, ProblemTypes.Authentication,
"Missing webhook signature", "Missing webhook signature",
StatusCodes.Status401Unauthorized); StatusCodes.Status401Unauthorized);
} }
@@ -164,7 +169,7 @@ internal static class WebhookEndpoints
logger.LogWarning("Invalid webhook signature for source {SourceId}", sourceId); logger.LogWarning("Invalid webhook signature for source {SourceId}", sourceId);
return ProblemResultFactory.Create( return ProblemResultFactory.Create(
context, context,
ProblemTypes.Unauthorized, ProblemTypes.Authentication,
"Invalid webhook signature", "Invalid webhook signature",
StatusCodes.Status401Unauthorized); StatusCodes.Status401Unauthorized);
} }
@@ -252,7 +257,7 @@ internal static class WebhookEndpoints
IEnumerable<ISourceTypeHandler> handlers, IEnumerable<ISourceTypeHandler> handlers,
ISourceTriggerDispatcher dispatcher, ISourceTriggerDispatcher dispatcher,
ICredentialResolver credentialResolver, ICredentialResolver credentialResolver,
ILogger<WebhookEndpoints> logger, ILogger<WebhookEndpointLogger> logger,
HttpContext context, HttpContext context,
CancellationToken ct) CancellationToken ct)
{ {
@@ -290,7 +295,7 @@ internal static class WebhookEndpoints
IEnumerable<ISourceTypeHandler> handlers, IEnumerable<ISourceTypeHandler> handlers,
ISourceTriggerDispatcher dispatcher, ISourceTriggerDispatcher dispatcher,
ICredentialResolver credentialResolver, ICredentialResolver credentialResolver,
ILogger<WebhookEndpoints> logger, ILogger<WebhookEndpointLogger> logger,
HttpContext context, HttpContext context,
CancellationToken ct) CancellationToken ct)
{ {
@@ -337,7 +342,7 @@ internal static class WebhookEndpoints
IEnumerable<ISourceTypeHandler> handlers, IEnumerable<ISourceTypeHandler> handlers,
ISourceTriggerDispatcher dispatcher, ISourceTriggerDispatcher dispatcher,
ICredentialResolver credentialResolver, ICredentialResolver credentialResolver,
ILogger<WebhookEndpoints> logger, ILogger<WebhookEndpointLogger> logger,
HttpContext context, HttpContext context,
CancellationToken ct) CancellationToken ct)
{ {
@@ -377,7 +382,7 @@ internal static class WebhookEndpoints
IEnumerable<ISourceTypeHandler> handlers, IEnumerable<ISourceTypeHandler> handlers,
ISourceTriggerDispatcher dispatcher, ISourceTriggerDispatcher dispatcher,
ICredentialResolver credentialResolver, ICredentialResolver credentialResolver,
ILogger<WebhookEndpoints> logger, ILogger<WebhookEndpointLogger> logger,
HttpContext context, HttpContext context,
CancellationToken ct) CancellationToken ct)
{ {
@@ -420,7 +425,7 @@ internal static class WebhookEndpoints
IEnumerable<ISourceTypeHandler> handlers, IEnumerable<ISourceTypeHandler> handlers,
ISourceTriggerDispatcher dispatcher, ISourceTriggerDispatcher dispatcher,
ICredentialResolver credentialResolver, ICredentialResolver credentialResolver,
ILogger<WebhookEndpoints> logger, ILogger<WebhookEndpointLogger> logger,
HttpContext context, HttpContext context,
string signatureHeader, string signatureHeader,
CancellationToken ct) CancellationToken ct)
@@ -459,7 +464,7 @@ internal static class WebhookEndpoints
logger.LogWarning("Webhook received without signature for source {SourceId}", source.SourceId); logger.LogWarning("Webhook received without signature for source {SourceId}", source.SourceId);
return ProblemResultFactory.Create( return ProblemResultFactory.Create(
context, context,
ProblemTypes.Unauthorized, ProblemTypes.Authentication,
"Missing webhook signature", "Missing webhook signature",
StatusCodes.Status401Unauthorized); StatusCodes.Status401Unauthorized);
} }
@@ -483,7 +488,7 @@ internal static class WebhookEndpoints
logger.LogWarning("Invalid webhook signature for source {SourceId}", source.SourceId); logger.LogWarning("Invalid webhook signature for source {SourceId}", source.SourceId);
return ProblemResultFactory.Create( return ProblemResultFactory.Create(
context, context,
ProblemTypes.Unauthorized, ProblemTypes.Authentication,
"Invalid webhook signature", "Invalid webhook signature",
StatusCodes.Status401Unauthorized); StatusCodes.Status401Unauthorized);
} }

View File

@@ -0,0 +1,12 @@
namespace StellaOps.Scanner.WebService.Tenancy;
/// <summary>
/// Provides the current tenant context for multi-tenant operations.
/// </summary>
public interface ITenantContext
{
/// <summary>
/// Gets the current tenant ID.
/// </summary>
string TenantId { get; }
}

View File

@@ -0,0 +1,17 @@
namespace StellaOps.Scanner.WebService.Tenancy;
/// <summary>
/// Provides the current user context for audit and authorization.
/// </summary>
public interface IUserContext
{
/// <summary>
/// Gets the current user ID.
/// </summary>
string UserId { get; }
/// <summary>
/// Gets the current user's display name.
/// </summary>
string? DisplayName { get; }
}

View File

@@ -122,11 +122,11 @@ public sealed class SbomSourceRepository : RepositoryBase<ScannerSourcesDataSour
MapSource, MapSource,
ct); ct);
var totalCount = (await ExecuteScalarAsync<long>( var totalCount = await ExecuteScalarAsync<long>(
tenantId, tenantId,
countSb.ToString(), countSb.ToString(),
AddFilters, AddFilters,
ct)).Value; ct);
string? nextCursor = null; string? nextCursor = null;
if (items.Count > request.Limit) if (items.Count > request.Limit)

View File

@@ -98,12 +98,11 @@ public sealed class SbomSourceRunRepository : RepositoryBase<ScannerSourcesDataS
MapRun, MapRun,
ct); ct);
var totalCountResult = await ExecuteScalarAsync<long>( var totalCount = await ExecuteScalarAsync<long>(
"__system__", "__system__",
countSb.ToString(), countSb.ToString(),
AddFilters, AddFilters,
ct); ct);
var totalCount = totalCountResult.GetValueOrDefault();
string? nextCursor = null; string? nextCursor = null;
if (items.Count > request.Limit) if (items.Count > request.Limit)