Skip to content

/user-permissions drops ReBAC-derived instances after a policy schema delta, until the PDP is restarted (pdp-v2:0.9.13) #341

Description

@lassan

Summary

On permitio/pdp-v2:0.9.13, an additive policy schema change applied to a running PDP makes POST /user-permissions stop returning ReBAC-derived resource instances. Directly-assigned instances still come back, and POST /allowed still answers allow: true for the instances that vanished.

Restarting the pod — which forces a full data re-pull — restores them.

It fails silently: 200 with a smaller but well-formed payload, and /health, /healthy and /ready all stay green.

Environment

permitio/pdp-v2:0.9.13, 2 replicas on Kubernetes, cloud control plane (not offline mode), no FactDB backup configured. Policy is applied through the Permit API by an infrastructure-as-code tool.

Policy shape

Roles derive across two hops:

account:owner --owner--> brand:owner
brand:owner   --owner--> campaign:owner
brand:owner   --owner--> location:owner

Users are assigned a role on account. brand derives from that; campaign and location derive from brand. Relationship tuples are written through the API when a resource is created.

Steps to reproduce

  1. Confirm /user-permissions returns derived instances for all three types.
  2. Apply an additive schema change through the API: add one action to the brand resource, then grant it to the brand:owner and brand:manager roles. Nothing deleted, nothing recreated, relations and derivations untouched.
  3. The PDP applies it as OPAL deltas:
    Update on Role           x2
    Insert on RolePermission x2
    Insert on ResourceAction x1
    Update on Resource       x1
    
  4. Re-query /user-permissions for the same user.

Result

One user, before and after a pod restart, with no API writes in between:

/user-permissions after the delta after restart
campaign (derived, 2 hops) 0 84
location (derived, 2 hops) 0 99
brand (derived, 1 hop) 58 58

One-hop derivations survive; two-hop derivations disappear entirely.

Expected: the counts match. An additive change shouldn't alter which instances are enumerated.

Both replicas behaved identically. Two things ruled out:

  • Not truncation — only 84 campaigns exist, and all 84 return after the restart.
  • Not a filter bug — omitting resource_types gives the same result.

Why this is easy to miss

While /user-permissions returned zero campaigns, /allowed granted those same campaigns:

// POST /allowed {"user":{"key":"<user>"},"action":"read-manage",
//                "resource":{"type":"campaign","key":"<id>","tenant":"default"}}
{"allow": true, "debug": {"rbac": {"allow": false, "code": "no_permission"}}}
// denied by RBAC, granted via ReBAC derivation

check() evaluates the derivation live, while /user-permissions appears to serve a materialised set that the delta invalidated without recomputing. Anyone verifying with check() will conclude the PDP is healthy.

Impact

Any app that uses /user-permissions to list what a user may see will show an empty list after a routine policy change, with no error and every health check green. Ours emptied a user-facing list in production until we restarted the PDP. Because nothing forces a reload after an apply, it recurs on every schema change.

Questions

  1. Is /user-permissions meant to return ReBAC-derived instances? The docs don't say, so it's unclear which result is the correct one.
  2. If it is, should a ResourceAction / RolePermission delta recompute the derived set rather than drop it?
  3. Short of restarting, is POST /data-updater/trigger (feat: replace legacy /update_policy* 307 redirects with direct gated calls (PER-15246) #320, feat: debounce forced-reload trigger endpoints (PER-15248) #327) the right way to force a recompute after an apply?

Happy to run further probes or share more logs.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions