Repository navigation
Description
Summary & Root Cause
Publishing a tag (TAG_SCOPE_PUBLISHED) only relaxes the handler's scope check (actor.go:185-193). The authz check can_use_tag is still editor from parent_atespace regardless of scope. Templates use viewer from parent_atespace. To clone a published tag, a consumer team must therefore hold editor on the publishing atespace, which also lets them create, update and delete actors, templates and tags there. Scope does not enter the relation at all, so "published" adds nothing for a consumer that is only a viewer.
Note: This behavior is documented (
docs/authorization.md:178), andmodel.fga:121-124carries a TODO saying the tag scoping design is unsettled. Treat S-26 as a design and authz gap against theTAG_SCOPE_PUBLISHEDcontract inateapi.proto:400-402, not as code that diverges from its docs.
Affected Code Locations
cmd/ateapi/internal/authz/model.fga:114:define can_use_template: viewer from parent_atespace.cmd/ateapi/internal/authz/model.fga:125-133: ontype tag,define can_get: viewer from parent_atespace(129) anddefine can_use_tag: editor from parent_atespace(133).cmd/ateapi/internal/authz/model.fga:117-124: comment and TODO: "can_use_tag controls who may clone it ... Restrict who can create, publish, and clone tags once the tag scoping design is settled."cmd/ateapi/internal/authz/registry.go:126-133:onSourceTagchecksRelationCanUseTagon the source tag.cmd/ateapi/internal/authz/registry.go:221-227: theCreateActorrule checkscan_create_actor,can_use_templateandcan_use_tag. The tag check is built from the request'ssource_tagref alone (line 225), so it runs in the authz interceptor before the handler looks the tag up. A tag that does not exist still getsPermissionDenied, notTag not found.cmd/ateapi/internal/controlapi/actor.go:185-193:resolveTagSourceacceptsTAG_SCOPE_PUBLISHEDfrom any atespace.api/ateapi/v1/ateapi.proto:400-402:TAG_SCOPE_PUBLISHED: "Published for use by Actors in any Atespace."docs/authorization.md:178: documents theCreateActorrequirement as "3. Editor of the tag's atespace".
Detailed Reproduction Guide
- In
team-b, create templatecounter, actorb-actorand tagtb1, and publish the tag:
kubectl ate create tag tb1 -a team-b --actor b-actor --scope=published- Grant
bobeditoronate-demo-counterandvieweronteam-b. - Confirm the template path and tag read work, then try the tag:
kubectl ate --token-file bob.token create actor cross-tmpl -a ate-demo-counter --template team-b/counter
kubectl ate --token-file bob.token get tag tb1 -a team-b
kubectl ate --token-file bob.token create actor cross-tag -a ate-demo-counter --template team-b/counter --tag team-b/tb1Expected vs. Actual Behavior
- Expected: A principal who can see
team-b(viewer), or holds a dedicated consumer relation, can clone aTAG_SCOPE_PUBLISHEDtag fromteam-bwithout write access toteam-b. - Actual: The template path and tag read succeed, but the clone fails with
PermissionDenied ... lacks "can_use_tag" on "tag:team-b/tb1"unlessbobis madeeditoronteam-b.
Observed Output & Logs
$ kubectl ate --token-file bob.token create actor cross-tmpl -a ate-demo-counter --template team-b/counter
ATESPACE NAME TEMPLATE STATE WORKER POD WORKER IPS VERSION AGE
ate-demo-counter cross-tmpl team-b/counter ACTOR_STATE_SUSPENDED <none> 1 0s
RC=0
$ kubectl ate --token-file bob.token get tag tb1 -a team-b
ATESPACE NAME SCOPE STATE SNAPSHOT FIDELITY AGE
team-b tb1 TAG_SCOPE_PUBLISHED Ready gs://<SNAPSHOT_BUCKET>/atespaces/team-b/tags/92782ff9-6173-4801-b0cf-d70ff223465e SNAPSHOT_FIDELITY_MEMORY 2m47s
RC=0
$ kubectl ate --token-file bob.token create actor cross-tag -a ate-demo-counter --template team-b/counter --tag team-b/tb1
Error: failed to create actor: rpc error: code = PermissionDenied desc = permission denied: principal "user:system%3Aserviceaccount%3Aauthz-test%3Abob" lacks "can_use_tag" on "tag:team-b/tb1"
RC=1$ kubectl ate --token-file bob.token get tag r3-tb2 -a team-b
Error: failed to get tag "r3-tb2": rpc error: code = NotFound desc = Tag team-b/r3-tb2 not found
RC=1
$ kubectl ate --token-file bob.token create actor r3-cross-tag -a ate-demo-counter --template team-b/counter --tag team-b/r3-tb2
Error: failed to create actor: rpc error: code = PermissionDenied desc = permission denied: principal "user:system%3Aserviceaccount%3Aauthz-test%3Abob" lacks "can_use_tag" on "tag:team-b/r3-tb2"
RC=1$ kubectl ate --token-file bob.token get tag r3-bb-pub -a team-b
ATESPACE NAME SCOPE STATE SNAPSHOT FIDELITY AGE
team-b r3-bb-pub TAG_SCOPE_PUBLISHED Ready gs://<SNAPSHOT_BUCKET>/atespaces/team-b/tags/e762c564-6abd-4b82-a6dd-736fb1d75913 SNAPSHOT_FIDELITY_MEMORY 63s
RC=0
$ kubectl ate --token-file bob.token create actor r3-bb-cross-tmpl -a ate-demo-counter --template team-b/r3-bb-b
ATESPACE NAME TEMPLATE STATE WORKER POD WORKER IPS VERSION AGE
ate-demo-counter r3-bb-cross-tmpl team-b/r3-bb-b ACTOR_STATE_SUSPENDED <none> 1 0s
RC=0
$ kubectl ate --token-file bob.token create actor r3-bb-cross-tag -a ate-demo-counter --template team-b/r3-bb-b --tag team-b/r3-bb-pub
Error: failed to create actor: rpc error: code = PermissionDenied desc = permission denied: principal "user:system%3Aserviceaccount%3Aauthz-test%3Abob" lacks "can_use_tag" on "tag:team-b/r3-bb-pub"
RC=1
# control: same call as the global bootstrap owner
$ kubectl ate create actor r3-bb-cross-tag-owner -a ate-demo-counter --template team-b/r3-bb-b --tag team-b/r3-bb-pub
ATESPACE NAME TEMPLATE STATE WORKER POD WORKER IPS VERSION AGE
ate-demo-counter r3-bb-cross-tag-owner team-b/r3-bb-b ACTOR_STATE_SUSPENDED <none> 1 0s
RC=0
Activity