Draft: Compute effective permissions from role grants and evaluate
Related to gitlab-org/gitlab#600048
Draft / stacked on !20 (
dr/policy-granted-permissions). Targets that branch, so the diff here is just the two commits on top of it — review/merge !20 first. The organization-administrator foundation + the engine compute are this MR.
What
Implements GLAZ's effective-permissions check for the Artifact Registry interim (proposal 014): given the role grants a subject holds, the action, and the resource, decide allow/deny. GLAZ stays stateless about assignments — the host (AR) pre-assembles the inputs (it fetches the subject's grants from the IAM Relationships API, filtered to the resource and its ancestors) and passes them in the Check request's relationships.
The algorithm (glaz-engine::Engine::check)
check(subject, action, object, role_ids, context):
1. action ∉ catalogue (union of role permissions) → DENY "not a valid action"
2. role_ids contains the organization-administrator role id → ALLOW "organization administrator"
3. resolve role_ids → union of permissions (unknown role id → error)
4. policy.check(... granted ...) → ALLOW / DENY (Cedar permit_role_granted)- Validity gate first so even an organization administrator can't perform a bogus/typo action.
- Org-owner bypass is a fixed allow, not a permission set (see below).
- Membership runs through Cedar (
permit_role_granted), evaluated schema-less for now.
Organization administrator = identity, not a role
glaz-roles/roles/organization_admin.yml is an identity-only role: a stable UUID with empty permissions. An organization administrator's implicit Artifact Admin access is conferred by the bypass in step 2 (keyed on the role id), not by a permission list — so it cannot be reduced or removed by editing role data. It lives outside the AR catalogue, so it never enters the permission union. AR sends it as an ordinary grant in relationships (on the org); since the engine unions all grants regardless of object, an org-level grant counts for a resource check.
Catalogue / valid actions
glaz_roles::defined_permissions() = the union of all default-role permissions — the set of valid actions for the validity gate. There's no standalone permission catalogue yet, so the roles are the catalogue. A test asserts Artifact Admin == the union: organization administrators are effectively Admin, so if Admin ever lacks a permission some role grants, organization administrators would silently lose that action — the invariant guards against that.
Schema-less Cedar now; schema later (host-provided)
The MVP is pure membership (no resource conditions), so glaz-policy is made schema-optional and runs schema-less: load_policies validates against a schema only if one is present, and check evaluates with None schema + Entities::empty() otherwise. load_schema is kept for when AR ships its own Cedar policies + schema (e.g. forbid on a quarantined repository) — at which point GLAZ loads and validates them. The schema is AR's to provide, not GLAZ's to generate.
This also replaces the earlier behavior where an unrecognized action returned an Err (the GitLab Duo note on !20): the Rust validity gate now denies an invalid action gracefully before Cedar.
Changes
glaz-roles:organization_admin.yml(identity-only) +organization_admin_role_id();defined_permissions()catalogue accessor;Admin == union+ organization-administrator-identity tests.glaz-engine:Engine<P: PolicyEngine>holding aResolver+ aPolicyEngine;new()loads the embedded roles + policies (schema-less); thecheckalgorithm above;EngineError::UnknownRole. Dropped theS: Storagegeneric and the graphBuilderfor now (re-added when graph traversal lands).glaz-policy: schema optional (schema-less default); removed the placeholder schema +forbid_archived_projectdemo;load_policiesno longer requires a schema;checkhandles both modes.glaz-module: extractsrole.idfrom the request'srelationshipsand calls the engine (no more placeholdergranted).
Tests
cargo test --locked --workspace — all pass. Engine: invalid-action deny, organization-administrator allow, role-granted allow, valid-but-not-granted deny, unknown-role error. Module: same via the proto boundary. Policy: schema-less membership + that a present schema still validates.
Notes / follow-ups
- Relaxes the explicit schema-init work recently merged on
main(schema required → optional) — a relax, not a revert (load_schemastays). Heads-up to @bmarjanovic warranted before review. - Role-id UUIDs are v4;
relationships.protorequires v7 — separate follow-up to migrate (the ids are owned byglaz-roles). glaz-graphis now referenced by no crate (onlyglaz-engineused it) — left as a workspace member for the future graph design.- Minor:
defined_permissions()rebuilds its set per check — worth aLazyLockcache on the hot path.
Naming: follows the org-role ADR (gitlab-com/content-sites/handbook!20059 (merged)) — the org access level "Organization Owner" is now "Organization Administrator" (file/identifier
organization_admin, mirroringartifact_admin).