{"record":{"id":"75623b204181491b","repo":"fullstackhero/dotnet-starter-kit","slug":"not-allowed-to-delete-this-file","errorCode":null,"errorMessage":"not allowed to delete this file","messagePattern":"not allowed to delete this file","errorType":"exception","errorClass":"ForbiddenException","httpStatus":403,"severity":"warning","filePath":"src/Modules/Files/Modules.Files/Features/v1/DeleteFile/DeleteFileCommandHandler.cs","lineNumber":33,"sourceCode":"    ICurrentUser currentUser)\n    : ICommandHandler<DeleteFileCommand, Unit>\n{\n    public async ValueTask<Unit> Handle(DeleteFileCommand cmd, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(cmd);\n\n        var f = await db.FileAssets\n            .FirstOrDefaultAsync(x => x.Id == cmd.FileAssetId, cancellationToken)\n            .ConfigureAwait(false)\n            ?? throw new NotFoundException(\"file not found\");\n\n        var userId = currentUser.GetUserId().ToString();\n        var policy = policies.Resolve(f.OwnerType)\n            ?? throw new ForbiddenException(\"no policy\");\n        var ctx = new FileAccessContext(f.Id, f.OwnerType, f.OwnerId, f.CreatedByUserId, (int)f.Visibility);\n        if (!await policy.CanDeleteAsync(ctx, userId, cancellationToken).ConfigureAwait(false))\n        {\n            throw new ForbiddenException(\"not allowed to delete this file\");\n        }\n\n        // Soft-delete: AuditableEntitySaveChangesInterceptor sets IsDeleted/DeletedOnUtc/DeletedBy on\n        // Remove() for ISoftDeletable; byte purge runs later via PurgeDeletedFilesJob post-retention.\n        db.FileAssets.Remove(f);\n        await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);\n        return Unit.Value;\n    }\n}\n","sourceCodeStart":15,"sourceCodeEnd":43,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Files/Modules.Files/Features/v1/DeleteFile/DeleteFileCommandHandler.cs#L15-L43","documentation":"The policy's CanDeleteAsync returned false for the acting user and file context, so the handler throws ForbiddenException before soft-deleting. Delete permission is decided per OwnerType policy (creator/admin checks), independent of authentication — the user is authenticated but not authorized for this specific file.","triggerScenarios":"User calls the delete endpoint for a file they did not create and do not administrate; policy requires an owner/admin role the user lacks; file belongs to a different owner within the same tenant.","commonSituations":"Shared-tenant setups where users assume any tenant file is deletable; UI hiding delete buttons only for the owner while API is called directly; role downgrades leaving users with stale UI; scripting bulk deletes over files owned by others.","solutions":["Verify the acting user is the file creator or holds the role the OwnerType policy requires for deletion.","Review CanDeleteAsync for the relevant OwnerType to understand the exact condition.","Request the elevated role/permission if deletion is legitimate, or have the owner perform the delete.","Adjust the policy implementation if the business rule itself is wrong."],"exampleFix":"// before\nawait client.DeleteFileAsync(otherUsersFileId); // 403\n// after\nif (file.CreatedByUserId == currentUserId || isAdmin) await client.DeleteFileAsync(file.Id);","handlingStrategy":"try-catch","validationCode":"var file = await client.GetFileAsync(id);\nif (file.CreatedByUserId != currentUserId && !userIsAdmin) throw new UnauthorizedAccessException(\"Only the creator or an admin may delete this file.\");","typeGuard":null,"tryCatchPattern":"catch (ForbiddenException e) when (e.Message.Contains(\"delete\")) {\n    notify(\"You are not allowed to delete this file.\");\n}","preventionTips":["Gate delete buttons on ownership/admin role in the UI.","Never assume same-tenant implies deletable; policies decide.","Surface the policy requirement in docs so integrators know who may delete.","Log denied deletes with actor + file ids for audit."],"tags":["authorization","forbidden","delete","files"],"backgroundTag":"permission-denied","analyzedSha":"3f2959e683e9f83f13e55e1678c9119f63c7e8e5","analyzedAt":"2026-09-15T22:20:53.684Z","contentChangedAt":"2026-09-15T22:20:53.684Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}