fix(backend): Remove TenantId injection vulnerability in CreateProjectCommand
CRITICAL SECURITY FIX: Removed client-provided TenantId parameter from CreateProjectCommand to prevent tenant impersonation attacks. Changes: - Removed TenantId property from CreateProjectCommand - Injected ITenantContext into CreateProjectCommandHandler - Now retrieves authenticated TenantId from JWT token via TenantContext - Prevents malicious users from creating projects under other tenants Security Impact: - Before: Client could provide any TenantId (HIGH RISK) - After: TenantId extracted from authenticated JWT token (SECURE) Note: CreateEpic, CreateStory, and CreateTask commands were already secure as they inherit TenantId from parent entities loaded via Global Query Filters. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,6 @@ namespace ColaFlow.Modules.ProjectManagement.Application.Commands.CreateProject;
|
||||
/// </summary>
|
||||
public sealed record CreateProjectCommand : IRequest<ProjectDto>
|
||||
{
|
||||
public Guid TenantId { get; init; }
|
||||
public string Name { get; init; } = string.Empty;
|
||||
public string Description { get; init; } = string.Empty;
|
||||
public string Key { get; init; } = string.Empty;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using MediatR;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.DTOs;
|
||||
using ColaFlow.Modules.ProjectManagement.Application.Common.Interfaces;
|
||||
using ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate;
|
||||
using ColaFlow.Modules.ProjectManagement.Domain.Repositories;
|
||||
using ColaFlow.Modules.ProjectManagement.Domain.ValueObjects;
|
||||
@@ -12,14 +13,19 @@ namespace ColaFlow.Modules.ProjectManagement.Application.Commands.CreateProject;
|
||||
/// </summary>
|
||||
public sealed class CreateProjectCommandHandler(
|
||||
IProjectRepository projectRepository,
|
||||
IUnitOfWork unitOfWork)
|
||||
IUnitOfWork unitOfWork,
|
||||
ITenantContext tenantContext)
|
||||
: IRequestHandler<CreateProjectCommand, ProjectDto>
|
||||
{
|
||||
private readonly IProjectRepository _projectRepository = projectRepository ?? throw new ArgumentNullException(nameof(projectRepository));
|
||||
private readonly IUnitOfWork _unitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
|
||||
private readonly ITenantContext _tenantContext = tenantContext ?? throw new ArgumentNullException(nameof(tenantContext));
|
||||
|
||||
public async Task<ProjectDto> Handle(CreateProjectCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// Get authenticated tenant ID from JWT token
|
||||
var tenantId = _tenantContext.GetCurrentTenantId();
|
||||
|
||||
// Check if project key already exists
|
||||
var existingProject = await _projectRepository.GetByKeyAsync(request.Key, cancellationToken);
|
||||
if (existingProject != null)
|
||||
@@ -27,9 +33,9 @@ public sealed class CreateProjectCommandHandler(
|
||||
throw new DomainException($"Project with key '{request.Key}' already exists");
|
||||
}
|
||||
|
||||
// Create project aggregate
|
||||
// Create project aggregate with authenticated tenant ID
|
||||
var project = Project.Create(
|
||||
TenantId.From(request.TenantId),
|
||||
TenantId.From(tenantId),
|
||||
request.Name,
|
||||
request.Description,
|
||||
request.Key,
|
||||
|
||||
Reference in New Issue
Block a user