FDE PulseFDE jobs open 455New in 7 days 25Companies hiring 53Remote-friendly 25%Median US pay $216kTop hirer Databricks 125
VI

The newspaper of the Forward Deployed Engineer

Guides

RBAC, ABAC, ReBAC or PBAC: bringing a client's permission model into an AI application

When an internal chatbot leaks data, the model usually hasn't misbehaved. The retrieval step simply doesn't know who is asking.

RBAC, ABAC, ReBAC or PBAC: bringing a client's permission model into an AI application
Photo: Kaleidico / Unsplash

In brief

  • In RAG, permissions must be enforced at retrieval so the pipeline returns only documents the person asking is allowed to see.
  • RBAC is easy to audit but the number of roles tends to balloon; ABAC needs fewer policies because it matches on attributes; PBAC puts the rules in one policy engine, which makes them easy to version and audit.
  • When documents live in Drive, Confluence or SharePoint, sync both content and permissions, and keep both in step with the source system.
ShareLinkedInFacebookX
Comparison table of four authorisation models. RBAC: access granted by role; the cost is a growing number of roles. ABAC: by department tag and clearance level; labels must be clean. ReBAC (highlighted): by shared folder; the relationship graph must be kept in sync. PBAC: by central rules; needs a policy engine.
When a client says "we use roles", it is not necessarily RBAC: if access hinges on who a folder is shared with, model it with ReBAC. Source: the article's synthesis of documentation from OpenFGA, Auth0, AWS and Ping Identity.

Picture your second week on a client site. The internal chatbot demo has gone smoothly, until a salesperson asks about this year’s bonus policy and the bot quotes a passage straight from the board’s payroll sheet.

The model did nothing wrong. It answered the question it was asked, using exactly the documents the retrieval step handed it. The failure is that the pipeline had no idea who was asking, or what that person was allowed to see.

Every FDE building RAG or agents for an enterprise runs into this. The client already has a permission model. Sometimes it is explicit, but more often it is implicit in SharePoint or Confluence. Your job is to carry that model into the AI application without dropping a single rule.

Why the gap is in retrieval

OpenFGA’s documentation states the requirement plainly: document-level permissions must be enforced in the pipeline so that RAG returns only content the user is allowed to see. The authorisation decision, in other words, has to happen before any text reaches the model’s context.

Once a passage is in the prompt, any instruction along the lines of “do not reveal confidential information” is no more than a request. So the real engineering questions are which model you use to represent the client’s permissions, and where you filter.

One rule, four ways to describe it

Stay with the company from the example above and try writing the rule “only the board may see the payroll sheet” in each model. Each one asks a different question about the same permission.

The most familiar is RBAC. Auth0 defines it as assigning permissions by role within the organisation, where each role is a set of permissions applied to users. For payroll, you create a “Board” role and you are done. Auditing is simple too.

Trouble starts when rules acquire context. Say the client has 5 departments, 4 seniority levels and 3 regions. If each combination needs its own role, you already have 60 roles before projects even enter the picture.

That is the “role explosion” Ping Identity warns about. AWS points to a further drawback: every time a new resource appears, you have to update policies to grant access to it.

If the client’s documents are already labelled by department and confidentiality level, ABAC is far tidier. A single rule, such as “the asker’s department matches the document’s department, and their seniority is high enough for its confidentiality level”, replaces all 60 roles, because decisions are based on attributes of the person, the resource and the environment.

AWS says this approach needs fewer policies and scales automatically to new resources. The cost is that labels must be clean: one payroll file someone forgot to label is a leak.

Many clients, though, label nothing. Their permissions live in who a “Payroll” folder has been shared with, which is a relationship. ReBAC models exactly that: permissions flow from relationships between users, groups and documents, and you have to describe the whole relationship graph. Google’s Zanzibar is usually cited as the reference design.

PBAC answers an entirely different question: where the rules live and who owns them. Every decision goes through a policy engine, with rules written in a declarative language that the security team can author, version and audit. Permissions can change quickly without reviewing roles across the whole organisation.

Model Question it answers Fits a client when The cost
RBAC What role does this person hold? The organisation has few roles and wants simple audits Roles multiply; new resources require policy changes
ABAC Do the attributes of the person, document and context match? Documents are already tagged with department and confidentiality level Attributes must be clean and kept up to date
ReBAC What is this person’s relationship to the document? Permissions follow folders, groups and who shared what The full relationship graph must be described and synced
PBAC What does the central rule say? The security team wants to manage rules as code Requires a policy engine and someone to maintain the rules

The practical lesson: a client saying “we use roles” does not mean they use RBAC. If their permissions come down to “who has this folder been shared with”, that is a relationship, and you should model it with ReBAC.

Example: documents that inherit permissions from folders

OpenFGA gives an example very close to what you will find on a client site. A document belongs to a folder and inherits that folder’s viewers, so anyone who can view the folder can view every document inside it. In OpenFGA’s modelling language, the rule takes only a few lines:

model
  schema 1.1

type user

type folder
  relations
    define viewer: [user]

type document
  relations
    define parent: [folder]
    define viewer: [user] or viewer from parent

The last line contains all of the inheritance logic. The “Payroll” folder has only board members as viewers, so the payroll file inside it is automatically visible to them alone, with no new role required.

The next step is deciding where to filter. OpenFGA describes two approaches. Post-filtering runs the vector query first and then checks permissions on each result. Pre-filtering fetches the list of documents the user may view first, then passes those IDs as a filter to the vector search. A sketch of pre-filtering:

def retrieve(user_id, question, k=8):
    allowed = fga.list_objects(
        user=f"user:{user_id}",
        relation="viewer",
        type="document",
    )
    return vector_db.search(
        query=question,
        top_k=k,
        filter={"doc_id": {"$in": allowed}},
    )

The case for preferring pre-filtering is simple arithmetic. With post-filtering, if you take the top 8 and all 8 chunks come from confidential documents, the user gets 0 chunks, even though a valid document sits at rank 9.

Pre-filtering carries the opposite risk: the ID list can grow very long for someone who can see a lot, so you need to measure it on the client’s real data.

Starting from scratch on a client site

Start with discovery, not code. Bring a few short questions:

  • Which systems hold the documents?
  • Who decides on permissions?
  • Are permissions attached to roles, to tags or to shared folders?
  • When someone leaves, in which system is their access revoked?

The answer to the last question usually points to the source of truth you need to follow.

Then choose a model based on how permissions actually work, not on what the client calls them. Folder- and sharing-based permissions point towards ReBAC. Documents already labelled by department and confidentiality level suit ABAC. A security team that wants to review rules as code suggests PBAC.

When documents live in Drive, Confluence or SharePoint, OpenFGA recommends syncing content into the vector database and permissions into OpenFGA, and keeping both in step with the source system. That is two pipelines, not one. Each needs a schedule, alerts when they drift, and a way to handle deletions.

Finally, test with real users. Take three accounts from three departments, ask the same question, and check that the sets of cited documents differ exactly as expected.

On your CV, don’t write a generic “built RAG”; say that you modelled document-level permissions and synced them from the source systems. When interviewing for a role building enterprise RAG, ask how they handle document-level permissions.

That question tells you how mature the product is, and shows them you know where these projects tend to break.

Common traps

The first trap is syncing permissions once for the demo and then forgetting about it. Content can be a few days stale without anyone noticing, but permissions a few days stale mean someone whose access was just revoked can still query the bot.

The second is letting an agent read documents through a broadly privileged service account and trusting the prompt to hold it back. The third is handling every edge case by creating another role, the quickest route to role explosion.

The last is relying solely on post-filtering with a fixed top-k, so users with limited access get empty answers and conclude the bot is “bad”. At that point you lose the trust of precisely your largest group of users.

Whether an enterprise AI application is trusted does not depend on how well it answers. It depends on whether it stays silent at the right moment, for the right person.

Was this article useful?

Use with your AI assistantAsk Claude ↗Ask ChatGPT ↗
7 sources
Read next on the roadmap · Stage 5: DeploymentDesign error responses with RFC 9457 so customer ops teams can fix incidents themselvesIf your API returns only a 500 with "Something went wrong", every incident on the customer's side becomes a ticket for you. A few JSON fields in the right places can change that.