feat: add Grant primitive XRD with polymorphic same-Org / cross-Org dispatch#9
Conversation
…ispatch
Second auth-group primitive that survives the "is it just a single-MR
wrapper?" test. Composes 1 or 2 underlying Zitadel MRs depending on
whether the user's home Org matches the project's enclosing Org.
Same-Org (spec.userOrgId == spec.projectOrgId): one MR
- user.zitadel.m.crossplane.io/Grant — the user's role assignment
within the project (no projectGrantId)
Cross-Org (spec.userOrgId != spec.projectOrgId): two MRs
- project.zitadel.m.crossplane.io/Grant — the cross-Org Project
Grant authorizing the role set for the user's home Org
- user.zitadel.m.crossplane.io/Grant — the user's role assignment
with projectGrantId pointing at the Project Grant, pulling roles
from the granted set
Multi-iter convergence: in cross-Org mode the user/Grant emits only
once the project/Grant's atProvider.id is observed (standard composition
gating per feedback_crossplane_composition_gates).
Schema takes flat IDs (userId + userOrgId + projectId + projectOrgId +
roles[]), matching the established convention from Tenant + MachineUser
— operator copies UUIDs from the relevant XR statuses or Zitadel UI.
Two examples (same-org + cross-org); 4 KCL CompositionTests covering
same-org, cross-org iter 1, multi-role, managementPolicies propagation.
Multi-iter convergence verified via observed-resources fixture.
21/21 KCL tests pass (13 AuthStack + 4 MachineUser + 4 Grant); all 8
examples render via make render:all.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (18)
📝 WalkthroughWalkthroughThis PR introduces a production-ready Grant composite resource for Crossplane that enables polymorphic user-to-project role assignment within the same organization or across organizations via Zitadel's grant APIs, complete with function pipeline orchestration, examples, tests, and CI integration. ChangesGrant Composite Resource Feature
Sequence DiagramsequenceDiagram
participant GrantXR as Grant XR<br/>(auth.hops.ops.com.ai)
participant StateInit as state-init<br/>(000)
participant ProjectGrant as project-grant<br/>(100)
participant UserGrant as user-grant<br/>(200)
participant ZitadelAPI as Zitadel API
participant StatusCompute as status-compute<br/>(010)
participant StatusEmit as status-emit<br/>(999)
GrantXR->>StateInit: spec: userOrgId, projectOrgId, userId, projectId, roles
StateInit->>StateInit: Compute mode: userOrgId == projectOrgId ?<br/>"same-org" : "cross-org"
StateInit-->>StateInit: $state = {mode, labels, config}
alt cross-org mode
StateInit->>ProjectGrant: render project.zitadel.../Grant
ProjectGrant->>ZitadelAPI: apply project-grant (orgId, projectId, grantedOrgId)
ZitadelAPI-->>ProjectGrant: projectGrantId, Ready status
end
alt same-org mode OR projectGrantId ready
ProjectGrant->>UserGrant: (cross-org) pass observed.projectGrantId
StateInit->>UserGrant: (same-org) render directly
UserGrant->>ZitadelAPI: apply user-grant (userId, projectId, [projectGrantId])
ZitadelAPI-->>UserGrant: userGrantId, Ready status
end
UserGrant->>StatusCompute: observed.resources = {project-grant, user-grant}
StatusCompute->>StatusCompute: ready = all Ready conditions true
StatusCompute-->>StatusCompute: $state.status = {ready, mode, userGrantId, projectGrantId}
StatusCompute->>StatusEmit: emit status
StatusEmit-->>GrantXR: status.ready, status.mode, status.userGrantId, status.projectGrantId
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes The PR introduces a substantial new composite resource with multi-mode orchestration logic, templated function implementations, and comprehensive test coverage. The core complexity lies in understanding the conditional rendering of project-grant only in cross-org mode and the multi-iteration readiness dependency. The changes are cohesive and well-scoped to a single feature, with clear separation between CRD definition, function pipeline, examples, and CI integration. Possibly related PRs
✨ 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 |
Published Crossplane PackageThe following Crossplane package was published as part of this PR: Package: ghcr.io/hops-ops/auth-stack:pr-9-14540e3ea9a3966c23faa2ace25ef2d0d7e1c91f |
Composes 1 or 2 underlying Zitadel MRs depending on whether userOrgId matches projectOrgId. Same-Org → user.zitadel/Grant. Cross-Org → project.zitadel/Grant + user.zitadel/Grant with projectGrantId. Multi-iter gating verified.
Same-Org path live-validated on colima (composed user.zitadel/Grant; XR status mode: same-org, ready: true, userGrantId populated; clean delete cascade). Cross-Org path validated via render tests + observed-resources fixture.
8/8 KCL Grant tests pass locally; 21/21 across the repo.
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Tests