[RBAC PR 5.1] Auto-assign owners for approved namespaces - #2428
[RBAC PR 5.1] Auto-assign owners for approved namespaces#2428philipfweiss wants to merge 5 commits into
Conversation
d383991 to
e89ed8b
Compare
a92a340 to
ab0c9b0
Compare
Assign personal and ad hoc namespace creators a scoped owner role only within configured patterns, with atomic and serialized boundary creation.
Keep the ownership path focused on configuration, atomic role assignment, and overlap safety while removing parser relocation and repetitive tests.
Use the base boundary marker and RBAC history so role renames and audit behavior stay consistent across both provisioning paths.
Exercise pattern matching through namespace behavior and remove incidental code churn after the base PR merged.
ab0c9b0 to
f38206b
Compare
✅ Deploy Preview for thriving-cassata-78ae72 canceled.
|
|
|
||
| # Exact namespaces or subtrees where a first human creator becomes the owner. | ||
| # CREATOR_OWNED_NAMESPACE_PATTERNS uses JSON list syntax, for example | ||
| # ["personal.*", "scratch"]. |
There was a problem hiding this comment.
A pattern like personal.* only matches child namespaces; it does not match the root namespace personal
If both are wanted, would personal* be sufficient, or would ["personal", "personal.*"] be preferred?
This could be a common use case, and it is worth having comments and examples here.
| await access_checker.check(on_denied=AccessDenialMode.RAISE) | ||
|
|
||
| # Create the namespace if required (idempotent) | ||
| await create_or_reactivate_namespace( |
There was a problem hiding this comment.
create_or_reactivate_namespace is a helper shared by three callers: POST /namespaces/{namespace}/, register_table and register_view
That means whoever first creates a matching namespace becomes its owner, and “create” here is not only the namespace API. Registering a table or view can do it too -- is this intentional?
| owner_role_name = f"namespace:{namespace}:owners" | ||
| if await Role.get_by_name(session, owner_role_name, include_deleted=True): | ||
| raise DJAlreadyExistsException( | ||
| message=f"Role `{owner_role_name}` already exists", |
There was a problem hiding this comment.
With Role.get_by_name(..., include_deleted=True) doesn't that mean that a soft-deleted namespace:<ns>:owners permanently blocks recreating that namespace?
Is there a path back to recreation that doesn't need an admin, maybe by reusing the existing role, or undeleting it when the same principal reclaims the namespace?
| namespace, | ||
| creator_owned_namespace_patterns, | ||
| ): | ||
| await lock_namespace_boundary_lifecycle(session) |
There was a problem hiding this comment.
Minor: Doesn't this serialize every creator-owned namespace creation against every other, including unrelated ones? So for example personal.alice and personal.bob would contend on the same lock.
Although it's probably fine at self-service volume, but just noting that it could become a bottleneck if the pattern is ever configured over something high-throughput.
Tracking: #2234 (step 5.1).
Today, if Alice creates
personal.alicewhile DJ is permissive, DJ records the namespace without giving Alice an RBAC grant. When write restrictions are later enabled for that namespace, Alice cannot manage what she created, so an administrator has to repair ownership.This PR adds opt-in first-creator ownership for configured personal and ad hoc namespaces:
CREATOR_OWNED_NAMESPACE_PATTERNSaccepts exact names and trailing.*subtrees. It is empty by default.namespace:<name>:owners, withMANAGEon the namespace, descendant namespaces, and contained nodes.Patterns are first-claim pools. With
personal.*, any user already authorized to create a namespace can claim an unusedpersonal.<name>, so operators should only configure prefixes intended for self-service.CREATOR_OWNED_NAMESPACE_PATTERNS='["personal.*", "scratch"]'Nonmatching and existing namespaces keep their current behavior. This adds no migration or backfill. #2408 is merged, and this draft is rebased on
main.Verification:
Started a local FastAPI harness on
:18082with a fresh SQLite database, the production namespace router and models, permissive RBAC, two principals (aliceasUSER,deploy-botasSERVICE_ACCOUNT), and:CREATOR_OWNED_NAMESPACE_PATTERNS='["personal.*"]'