RAG Permission Filters: Why Order Matters
RAG authorization is not merely deleting passages from a final answer. Taking global top-k before removing inaccessible results can leave very little usable evidence. Showing restricted content to a generator and asking it not to cite that content is not a reliable data boundary. Authorization defines the candidate set; relevance ranking belongs within it.
Contents
- Define the authorized search set
- Global top-three loses two usable results
- Check semantics with a runnable reference
- Oversampling is not a general guarantee
- Derive filters through a trusted path
- Do not replace an intersection with a union
- Result caches must respect current authorization
- Earlier filtering does not remove delivery checks
- Define recall within the authorized scope
- Sources and backfill note
Six synthetic records expose the ordering issue and separate retrieval quality from access-control acceptance. 阅读中文版.
Define the authorized search set
Let D be all documents and A the set allowed by trusted authorization for the current request. The target is top-k within A, not top-k(D) intersected with A. Inaccessible documents can consume global candidate slots, so the two expressions generally differ.
This is a set-and-ranking distinction independent of a particular vector database. Qdrant filtering documentation provides payload-condition composition, but expressing a condition does not establish that an application derived the correct permissions for a request.
Trusted request identity
Determine allowed scope
Authorization conditions
Constrain candidates
Rank within that set
Select top-k
Recheck results
Then generate and cite
Global top-three loses two usable results
Construct scores 100 and 99 for inaccessible x1 and x2, followed by accessible a,b,c at 98,97,96. Filtering global top-three leaves only a. Ranking inside the authorized set returns a,b,c. Neither final list includes x1 or x2, but evidence coverage differs.
The scores are fictional ordering values, not cosine similarities or measured retrieval performance. The example establishes that filtering and truncation do not commute. Removing forbidden results at the end does not establish the same quality as constraining the candidate set first.
Global top-3
x1,x2,a
Filter afterward
Only a remains
Constrain scope first
a,b,c
Top-3 inside scope
a,b,c remain
Check semantics with a runnable reference
The program uses only synthetic IDs, scope labels, and scores in memory. trusted_scopes is explicitly assumed to come from trusted authorization. It does not implement authentication and must not simply be replaced with client-provided fields.
# Synthetic IDs and scores; no database, private text, or real ACL is used.
rows = [dict(id="x1",score=100,scope="other"),
dict(id="x2",score=99,scope="other"),
dict(id="a",score=98,scope="team-a"),
dict(id="b",score=97,scope="team-a"),
dict(id="c",score=96,scope="team-a"),
dict(id="unknown",score=95)]
def topk(items,k):
if not isinstance(k,int) or isinstance(k,bool) or k<0:
raise ValueError("invalid k")
return sorted(items,key=lambda x:(-x["score"],x["id"]))[:k]
def allowed(row,trusted_scopes):
return row.get("scope") in trusted_scopes
def retrieve(rows,k,trusted_scopes):
return topk([r for r in rows if allowed(r,trusted_scopes)],k)
scopes = frozenset(["team-a"]) # Assumed output of trusted authorization.
post = [r for r in topk(rows,3) if allowed(r,scopes)]
pre = retrieve(rows,3,scopes)
assert [r["id"] for r in post] == ["a"]
assert [r["id"] for r in pre] == ["a","b","c"]
assert retrieve(rows,3,frozenset()) == []
assert not allowed(rows[-1],scopes)
assert retrieve(rows,0,scopes) == []
assert len(retrieve(rows,10,scopes)) == 3
# Simulated revocation: the current scope set becomes empty.
assert retrieve(rows,3,frozenset()) == []
print("post-filter returns1; authorized-set top-k returns3; deny cases passed")
Executed checks cover one post-filtered result versus three authorized results, empty permissions, missing scope, k=0, insufficient candidates, and simulated revocation. No database or restricted text was accessed. This is exact ranking reference logic, not a promise that an approximate vector index returns the identical set.
Oversampling is not a general guarantee
Taking more global candidates before filtering can help for some data distributions, but no fixed multiplier universally guarantees k allowed results. Enough higher-scored inaccessible records can still occupy the entire finite candidate window.
Oversampling does not replace an access boundary either. If ranking reads every candidate body before inaccessible items are removed, content may already have reached an inappropriate component. Establish where filtering happens, which metadata that stage may process, and when bodies are read.
Earlier constraints do not guarantee that an approximate index finds an optimal set. Index behavior and filter selectivity affect search. Use an exact reference to establish business semantics, then evaluate the actual implementation under identical authorization conditions to distinguish ordering, approximation, and corpus limitations.
Derive filters through a trusted path
A user may describe desired information without thereby selecting their tenant or group membership. Derive access conditions from verified identity and authorization, preventing ordinary query or model-tool parameters from overwriting mandatory constraints.
The OWASP authorization guidance emphasizes deny-by-default and checks on every request. For RAG, missing labels should not silently mean public, and authorization failure should not fall back to unrestricted retrieval. These are application policies, not guarantees obtained merely by including a database filter.
Manage relevance and authorization separately. Query rewriting can change search terms without changing mandatory scope. Logs can retain policy version and result counts without default exposure of document bodies, enabling review of which rule was applied.
Do not replace an intersection with a union
If access requires both tenant matching and group membership, those requirements form an intersection. Encoding them as either condition expands the candidate set. Test condition composition with a minimal record set; the presence of both condition names in a configuration does not establish correct logic.
Arrays and nested payloads additionally need attention to whether conditions must match the same nested object. There is no universal ACL expression for every business. Write explicit allow and deny examples first, then compare the IDs returned by the actual database query.
Result caches must respect current authorization
Identical query text does not imply identical visible documents. A result cache keyed only by the query can reuse evidence from one access scope in another. Cache identity needs the authorization semantics that affect the result, represented appropriately for the existing policy.
Consider revocation and document updates too. A group name alone may not cover membership or policy changes. Invalidation, policy versions, and delivery-time checks need coordination. No real cache was changed here; the example’s frozenset is not a complete production authorization system.
Earlier filtering does not remove delivery checks
Retrieval and document-body reads can cross a time boundary. Access may be revoked after candidate selection, or indexed permission metadata may lag. Where the requirements demand it, check current trusted access state before delivering bodies to a model or user.
If rechecking removes candidates, do not relax permissions to fill k. Retrieve more within the same allowed scope or report insufficient evidence accurately. Fewer results can be a legitimate business outcome; crossing the boundary is not a retrieval optimization.
Once forbidden content enters a generation context, omitting its citation does not establish that it had no influence. The boundary must cover body reads, model input, caches, and logs, rather than only front-end presentation.
Define recall within the authorized scope
For a request identity, restrict relevant documents to those that identity may access before constructing a recall denominator. Counting inaccessible relevant documents penalizes correct exclusion and can encourage the wrong optimization. The site’s RAG recall article discusses denominators; authorization adds another condition.
Record quality and access acceptance separately. Quality asks whether sufficient permitted evidence was found. Access acceptance asks whether forbidden content entered downstream processing. A higher average score must not cancel an unauthorized result, and zero unauthorized results does not prove adequate recall.
Synthetic cases can cover cross-tenant access, empty permissions, missing labels, policy failure, revocation, cached reuse, and too few candidates before an authorized integration test. If this ordering example helps expose a filtering gap, voluntary support below is welcome.
Sources and backfill note
The Qdrant filtering reference and OWASP authorization guidance. Diagrams, scores, and code are original teaching constructions without real identity or document data. Backfilled on October 16 with the planned 19:00 Beijing article date.
Support
If the explanations or examples helped you solve a problem or save time, you’re welcome to support this site with a voluntary tip.
Click a QR code to enlarge. More options: support page。


