Add the platform superuser type and Martyrology project roles - #18
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThe change adds platform and superuser inheritance to the Martyrology OpenFGA model, associates governance bodies with the platform, provisions three Zitadel project roles, and documents grant-management routes and rollout steps. ChangesMartyrology authorization
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@auth/handoffs/martyrology.md`:
- Line 191: Correct the grant-routing statement in the martyrology handoff
documentation: do not claim that subsequent superuser grants use
/admin/permissions. Document the appropriate separate operator/OpenFGA path for
granting user:<sub> on platform:martyrology, or specify a platform-scoped
endpoint if one exists, while preserving the first-superuser flow.
- Around line 184-189: Update the bootstrap curl command to include the
--fail-with-body option, ensuring HTTP errors from the OpenFGA write request
cause curl to exit unsuccessfully while preserving the existing request and
error-body output.
In `@auth/models/Martyrology.tuples.json`:
- Around line 12-14: Update the comment in Martyrology.tuples.json to describe
that the file contains all structural tuples, including both
governance-body-to-edition relationships and platform-to-governance on_platform
relationships. Leave the tuple data unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: c383f8b5-f138-41a6-a66c-6d6c79180867
📒 Files selected for processing (4)
auth/handoffs/martyrology.mdauth/models/Martyrology.jsonauth/models/Martyrology.tuples.jsonauth/setup-zitadel.sh
The handoff claimed that every grant after the bootstrap -- "including granting a second superuser" -- goes through /admin/permissions. That is wrong, and it inverts the security property it describes: the grant endpoint fixes its object type to governance_body in the route itself, precisely so platform: tuples are structurally unreachable. An endpoint that could write them would let any governance-body admin mint themselves a superuser. Superuser grants are therefore always out-of-band, not just the first one. Documented with the revoke form alongside. Also: the bootstrap curl gains --fail-with-body, since `curl -sS` exits 0 on an HTTP error and a silent failure there leaves the operator believing they hold a superuser tuple they do not; and the tuples file's $comment described only governed_by, though the file now carries on_platform tuples too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the pieces martyrology-api needs to express platform-wide authority and to
tell a curator apart from any other authenticated principal in the umbrella
instance. Pairs with CatholicOS/martyrology-api#23.
OpenFGA: a
platformtypeA platform superuser becomes
adminon every body pointing at the platform, andthrough the pre-existing
governed_byhop inheritscan_admin/can_edit/can_read_textson every edition those bodies govern. Theeditiontype isunchanged.
This is deliberately not a role bypass in the API. Platform authority is a
tuple — listable, auditable, revocable — rather than a claim that silently
defeats the governance model with no per-resource record.
Three structural
on_platformtuples are seeded, one per governance body. Nosuperusertuple is committed: the bootstrap grant is a per-person operatoraction made out-of-band, not version-controlled seed data.
Zitadel: three project roles
admin,martyrology_editor,developeron the MartyrologyAPI project, viathe existing
create_roleshelper.create_projectalready enablesprojectRoleAssertion, and a live introspection probe confirmed roles arereturned in both the generic and project-scoped claims.
developeris defined but enforced by nothing — martyrology-api has noAPI-consumer features to gate yet. It exists so the vocabulary is uniform across
CDCF properties before principals are onboarded, since issuing a role to
already-onboarded principals later is the disruptive path.
Corrections to existing text
Three comment blocks in
setup-zitadel.shargued that Martyrology deliberatelyhas no roles and that all authorization is OpenFGA. That is no longer true, so
they are rewritten rather than deleted. The handoff doc's claim that the
adminrole "is the intended role-granting authority" was never implemented by
anything; granting authority is the OpenFGA
adminrelation on a governancebody, reached directly or via
platform:martyrology.Verification
jqparses both JSON files; the model retains all original types and the tuplesfile its original eight
governed_byentries;shellcheck auth/setup-zitadel.shreports no findings. Review traced both authorization hops statically rather
than treating valid JSON as sufficient.
Not applied to any environment
The model upload, the bootstrap superuser tuple, the
/etc/martyrology/api.envedit and the live probe are operator steps, performed with a human after the API
side is released. Note that uploading this model mints a new authorization
model ID, and
MARTYROLOGY_OPENFGA_MODEL_IDis pinned — the pin, the token andthe project ID must land in one edit followed by a restart. Pinning to the old
model fails closed, so a mistake there is safe but inert.
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation