This commit includes all previously untracked backend files: Domain: - Accounts, Attachments, BankConnections, Customers - FiscalYears, Invoices, JournalEntryDrafts - Orders, Products, UserAccess Commands & Handlers: - Full CQRS command structure for all domains Repositories: - PostgreSQL repositories for all read models - Bank transaction and ledger repositories GraphQL: - Input types, scalars, and types for all entities - Mutations and queries Infrastructure: - Banking integration (Enable Banking client) - File storage, Invoicing, Reporting, SAF-T export - Database migrations (003-029) Tests: - Integration tests for GraphQL endpoints - Domain tests - Invoicing and reporting tests Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
72 lines
2.4 KiB
C#
72 lines
2.4 KiB
C#
using Books.Api.EventFlow.Repositories;
|
|
|
|
namespace Books.Api.Authorization;
|
|
|
|
/// <summary>
|
|
/// Middleware that extracts the X-Company-Id header and validates user access.
|
|
/// Stores the result in HttpContext.Items for use by GraphQL resolvers.
|
|
/// </summary>
|
|
public class CompanyContextMiddleware(RequestDelegate next)
|
|
{
|
|
public const string HeaderName = "X-Company-Id";
|
|
public const string ContextKey = "CompanyContext";
|
|
|
|
public async Task InvokeAsync(HttpContext context, IUserCompanyAccessRepository accessRepository)
|
|
{
|
|
var companyContext = new CompanyContext();
|
|
|
|
// Only process if user is authenticated
|
|
if (context.User.Identity?.IsAuthenticated == true)
|
|
{
|
|
var userId = context.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
|
|
|
|
if (userId != null && context.Request.Headers.TryGetValue(HeaderName, out var companyIdHeader))
|
|
{
|
|
var companyId = companyIdHeader.ToString();
|
|
|
|
if (!string.IsNullOrWhiteSpace(companyId))
|
|
{
|
|
// Validate user has access to this company
|
|
var access = await accessRepository.GetAccessAsync(userId, companyId);
|
|
|
|
if (access != null)
|
|
{
|
|
companyContext = new CompanyContext
|
|
{
|
|
CompanyId = companyId,
|
|
Role = access.Role
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Store in HttpContext.Items for resolvers to access
|
|
context.Items[ContextKey] = companyContext;
|
|
|
|
await next(context);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension methods for accessing CompanyContext.
|
|
/// </summary>
|
|
public static class CompanyContextExtensions
|
|
{
|
|
/// <summary>
|
|
/// Get the CompanyContext from HttpContext.
|
|
/// </summary>
|
|
public static CompanyContext GetCompanyContext(this HttpContext context)
|
|
{
|
|
return context.Items[CompanyContextMiddleware.ContextKey] as CompanyContext
|
|
?? new CompanyContext();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add the CompanyContext middleware to the pipeline.
|
|
/// </summary>
|
|
public static IApplicationBuilder UseCompanyContext(this IApplicationBuilder app)
|
|
{
|
|
return app.UseMiddleware<CompanyContextMiddleware>();
|
|
}
|
|
}
|