Extract group authorisation into a route-level filter #43

Merged
rob merged 1 commit from feat/group-authorisation-helper into main 2026-08-04 21:30:00 +00:00
Owner

Tasks 147 and 148.

PlaceEndpoints.PostCreatePlace authorised imperatively (resolve GroupResource, call AuthorizeAsync, throw 403/404 by hand). Extracted to GroupAuthorizationEndpointExtensions.RequireGroupCapability, attached declaratively on the route (.RequireGroupCapability(GroupCapability.MayEditPlaces)). PostCreatePlace's existing 403/404 e2e tests pass unchanged.

Chose a filter over an extension method a handler calls — see ADR-0043 for the full weighing. The short version: an extension method is exactly as easy to omit as the code it replaces, which defeats task 148's point. The filter attaches GroupCapabilityMetadata to the endpoint, so the safety-net test (GroupScopedEndpointAuthorisationTests) can enumerate every route under /api/groups/{groupId} and assert each declares the requirement, without a database.

Proved the safety net: removed .RequireGroupCapability(...) from the create-place route, confirmed GroupScopedEndpointAuthorisationTests reddened (listing that exact endpoint as missing the requirement), then restored it and confirmed green.

Full local CI sequence run under the pinned SDK (10.0.100): restore, build (Release), test (Release, 581 tests), dotnet format --verify-no-changes — all clean.

Tasks 147 and 148. `PlaceEndpoints.PostCreatePlace` authorised imperatively (resolve `GroupResource`, call `AuthorizeAsync`, throw 403/404 by hand). Extracted to `GroupAuthorizationEndpointExtensions.RequireGroupCapability`, attached declaratively on the route (`.RequireGroupCapability(GroupCapability.MayEditPlaces)`). `PostCreatePlace`'s existing 403/404 e2e tests pass unchanged. Chose a filter over an extension method a handler calls — see ADR-0043 for the full weighing. The short version: an extension method is exactly as easy to omit as the code it replaces, which defeats task 148's point. The filter attaches `GroupCapabilityMetadata` to the endpoint, so the safety-net test (`GroupScopedEndpointAuthorisationTests`) can enumerate every route under `/api/groups/{groupId}` and assert each declares the requirement, without a database. Proved the safety net: removed `.RequireGroupCapability(...)` from the create-place route, confirmed `GroupScopedEndpointAuthorisationTests` reddened (listing that exact endpoint as missing the requirement), then restored it and confirmed green. Full local CI sequence run under the pinned SDK (10.0.100): restore, build (Release), test (Release, 581 tests), `dotnet format --verify-no-changes` — all clean.
Extract group authorisation into a route-level filter
All checks were successful
CI / build (pull_request) Successful in 2m12s
2c8322d71f
rob left a comment
No description provided.
Verdict: mergeable Verified independently, not just read: - `CreatePlaceEndpointTests` is untouched by the diff and passes unmodified, including the Viewer-403, non-member-404 and 404-indistinguishability cases, over real HTTP against Postgres. - Removing `.RequireGroupCapability(...)` from `PostCreatePlace` reddens both `GroupScopedEndpointAuthorisationTests` and the real e2e 403/404 tests, as claimed. - Added a genuinely new, never-before-registered route under `/api/groups/{groupId}` with no filter attached — `GroupScopedEndpointAuthorisationTests` failed and named it, listing it as missing the requirement. It enumerates registered `EndpointDataSource`s at runtime rather than a fixed list, so it does catch new endpoints, not just regressions on known ones. - Metadata and enforcement can't drift apart in practice: `WithMetadata` and `AddEndpointFilter` are both added unconditionally by the same `RequireGroupCapability` call, so nothing can attach the marker without the check actually running (short of hand-constructing the internal `GroupCapabilityMetadata` record elsewhere, which nothing in this codebase does). - Full solution builds and tests clean under the pinned 10.0.100 SDK; `dotnet format --verify-no-changes` clean. One thing worth knowing but not blocking: `RequestValidationFilter`, registered at the group level, runs before `RequireGroupCapability`'s route-level filter, so a request with an invalid body gets 400 before authorisation ever runs — for a non-member and for a nonexistent group alike, so it doesn't create a 404-vs-400 oracle for group existence. This ordering predates this PR (validation was already group-level before the imperative check ran inside the handler body) and isn't a regression, just worth flagging since eleven more endpoints inherit it unchanged.
rob merged commit 9dc5571d03 into main 2026-08-04 21:30:00 +00:00
rob deleted branch feat/group-authorisation-helper 2026-08-04 21:30:00 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rob/PlaceMark!43
No description provided.