Skip to content

Cross-atespace TAG_SCOPE_PUBLISHED tags cannot be used by viewers of the publishing atespace because can_use_tag requires editor on parent_atespace #3293

Open

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), and model.fga:121-124 carries a TODO saying the tag scoping design is unsettled. Treat S-26 as a design and authz gap against the TAG_SCOPE_PUBLISHED contract in ateapi.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: on type tag, define can_get: viewer from parent_atespace (129) and define 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: onSourceTag checks RelationCanUseTag on the source tag.
  • cmd/ateapi/internal/authz/registry.go:221-227: the CreateActor rule checks can_create_actor, can_use_template and can_use_tag. The tag check is built from the request's source_tag ref alone (line 225), so it runs in the authz interceptor before the handler looks the tag up. A tag that does not exist still gets PermissionDenied, not Tag not found.
  • cmd/ateapi/internal/controlapi/actor.go:185-193: resolveTagSource accepts TAG_SCOPE_PUBLISHED from 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 the CreateActor requirement as "3. Editor of the tag's atespace".

Detailed Reproduction Guide

  1. In team-b, create template counter, actor b-actor and tag tb1, and publish the tag:
kubectl ate create tag tb1 -a team-b --actor b-actor --scope=published
  1. Grant bob editor on ate-demo-counter and viewer on team-b.
  2. 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/tb1

Expected vs. Actual Behavior

  • Expected: A principal who can see team-b (viewer), or holds a dedicated consumer relation, can clone a TAG_SCOPE_PUBLISHED tag from team-b without write access to team-b.
  • Actual: The template path and tag read succeed, but the clone fails with PermissionDenied ... lacks "can_use_tag" on "tag:team-b/tb1" unless bob is made editor on team-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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions