{"record":{"id":"5ec1615cc6d9e8d0","repo":"fullstackhero/dotnet-starter-kit","slug":"no-policy","errorCode":null,"errorMessage":"no policy","messagePattern":"no policy","errorType":"exception","errorClass":"ForbiddenException","httpStatus":403,"severity":"error","filePath":"src/Modules/Files/Modules.Files/Features/v1/ChangeVisibility/ChangeFileVisibilityCommandHandler.cs","lineNumber":42,"sourceCode":"    {\n        ArgumentNullException.ThrowIfNull(cmd);\n\n        if (cmd.Visibility is not (Visibility.Public or Visibility.Private))\n        {\n            throw new CustomException(\n                $\"Unknown visibility value '{cmd.Visibility}'.\",\n                errors: null,\n                System.Net.HttpStatusCode.BadRequest);\n        }\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.CanChangeVisibilityAsync(ctx, userId, cancellationToken).ConfigureAwait(false))\n        {\n            throw new ForbiddenException(\"not allowed to change this file's visibility\");\n        }\n\n        f.ChangeVisibility(cmd.Visibility);\n        await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);\n\n        var publicUrl = f.Visibility == Visibility.Public\n            ? storage.BuildPublicUrl(f.StorageKey)\n            : null;\n        return FileAssetMapper.ToDto(f, publicUrl);\n    }\n}\n","sourceCodeStart":24,"sourceCodeEnd":58,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Files/Modules.Files/Features/v1/ChangeVisibility/ChangeFileVisibilityCommandHandler.cs#L24-L58","documentation":"After loading the file, the handler asks FileAccessPolicyRegistry.Resolve(f.OwnerType) for a per-owner-type access policy; when the registry has no policy registered for that OwnerType it throws ForbiddenException(\"no policy\"). This is a configuration/registration failure, not a user-permission problem: a new OwnerType was introduced without an accompanying IFileAccessPolicy implementation.","triggerScenarios":"FileAssets row whose OwnerType has no IFileAccessPolicy registered in FileAccessPolicyRegistry (missing DI registration, policy assembly not scanned, typo in OwnerType string, or a new owner kind added without a policy).","commonSituations":"Developer adds a new owner type (e.g. new module owning files) and forgets to register its policy; DI container rebuilt without the policy's module; environment-specific registration omitted; OwnerType values edited in seed data without matching policies.","solutions":["Register an IFileAccessPolicy for the missing OwnerType in DI so FileAccessPolicyRegistry can resolve it.","Log/inspect the actual f.OwnerType value on the offending FileAssets row and compare it against the registered policy keys.","Add a startup guard or unit test that asserts every valid OwnerType enum/string value has a registered policy.","If the row's OwnerType is corrupt data, fix the row or delete it via a data migration."],"exampleFix":"// before\n// no registration for OwnerType \"Project\" -> policies.Resolve(\"Project\") returns null\n// after\nservices.AddSingleton<IFileAccessPolicy, ProjectFileAccessPolicy>(); // keyed/resolved by OwnerType \"Project\"","handlingStrategy":"validation","validationCode":"var ownerTypes = new[] { \"User\", \"Organization\" /* all valid values */ };\nif (!ownerTypes.Contains(asset.OwnerType)) throw new InvalidOperationException($\"No access policy registered for OwnerType '{asset.OwnerType}'.\");","typeGuard":"bool HasPolicy(FileAccessPolicyRegistry registry, string ownerType) => registry.Resolve(ownerType) is not null;","tryCatchPattern":"catch (ForbiddenException e) when (e.Message == \"no policy\") {\n    logger.LogError(e, \"Unregistered OwnerType on file {FileId}\", fileId);\n    return Results.Problem(\"Server misconfiguration: file access policy missing.\", statusCode: 500);\n}","preventionTips":["Register an IFileAccessPolicy for every new OwnerType at the same time you introduce it.","Add a startup assertion covering all OwnerType values against the registry.","Never hand-edit OwnerType in seed/import data without a matching policy.","Cover the registry with a unit test enumerating valid owner types."],"tags":["configuration","di","authorization","files"],"backgroundTag":"missing-config-key","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"}