Free preview

Resolving Identity

In one line: the query needs the set of every principal this person counts as, and computing that is a graph traversal you cannot afford to do from scratch on every search.

The effective permission set

A user is not one identifier. They are a person, plus every group they belong to, plus every group those groups belong to, plus their roles, plus anything derived from their org position.

For a large organisation that set is routinely hundreds of entries, and it is what every query intersects against. Getting it wrong in the permissive direction is a breach; getting it wrong in the restrictive direction makes the product look broken.

Nested groups

The part that turns a lookup into a traversal.

Groups contain groups. all-engineering contains platform-team contains platform-oncall. A document shared with all-engineering must be visible to someone whose only direct membership is platform-oncall, which requires walking up the containment graph.

Three practical hazards.

Cycles happen. Directory data is edited by people and group A can end up containing group B which contains group A. A naive traversal does not terminate, so the walk needs cycle detection rather than assuming a tree.

Depth varies wildly. Most users resolve in two or three hops; some resolve in ten. Designing for the median produces timeouts on the tail.

Expansion is expensive and stable. It costs real work and changes rarely — which is precisely the profile of something to precompute rather than resolve per request.

Precompute, and the TTL problem

So the effective set is computed ahead of time and cached. That is the only workable answer, and the cache introduces the question that matters.

The TTL is a security parameter, not a performance one. A user removed from a group still holds their cached expansion until it expires, so the TTL is the maximum window during which someone retains access they have lost.

Which produces a genuine trade with no comfortable answer:

TTLConsequence
HoursFast, and someone can retain lost access all afternoon
MinutesA tolerable window, at real load on the directory
Per requestCorrect, and every search now depends on the directory's availability

Event-driven invalidation is the right answer and the TTL must remain as a backstop, because an event stream that silently stops is otherwise an unbounded exposure.

Normalising identity across sources

The unglamorous prerequisite for any of this working.

Eight source systems use eight identity schemes. The same person is an email address in one, an opaque directory id in another, a username in a third, and an employee number in the HR system. Their groups have different names and different ids in each.

The intersection is meaningless unless both sides speak the same language, so there has to be a canonical principal identity with a mapping from each source's scheme.

Three failure modes:

Unmapped principals. A group in a source system with no canonical equivalent. The safe behaviour is to treat an unmapped principal as not matching — restrictive rather than permissive — and to alarm, because a silently unmapped group means documents nobody can find.

Ambiguous mapping. Two source identities resolving to one person, or one resolving to two. The first is normal after a rename; the second is a bug that grants someone else's access.

Deleted principals. A departed employee's groups may persist and be reused. An identifier that is stable in one system and recycled in another is a real hazard.

Delegated access

The case that breaks the model, and it is worth raising because it is common.

Some systems support acting on behalf of someone else — a shared mailbox, a delegated calendar, an assistant with access to an executive's documents. Whose permissions apply?

The general answer is that the acting user's effective set is what the query intersects against, and delegation is represented as membership in a principal that the delegation grants. That keeps one model rather than two.

The failure to avoid is a service account with union-of-everyone access performing retrieval on the user's behalf. It is a tempting simplification because it makes retrieval easy, and it means the permission check exists only in whatever code remembered to apply it — which is the post-filter problem wearing a different hat.

The query-side cost

Bringing it back to the request path.

A permission set of several hundred principals goes into every query as a set intersection. In a lexical index that is another posting list to intersect and it is cheap. In a vector index it is a filter over a graph traversal, and a highly restrictive filter fragments the traversal — the selectivity problem from vector search, arriving with permissions as the filter.

The practical consequence: for a user permitted to see a small fraction of the corpus, approximate nearest-neighbour search over the permitted subset degrades, and scanning the eligible set exactly may be both faster and more accurate. Which is the same conclusion as the filtering lesson in search ranking, reached from a different direction.

Key takeaway

A person is hundreds of principals once nested groups are expanded, so the effective set is precomputed — and its cache TTL is a security parameter, being the window during which someone keeps access they have lost. Invalidate on identity events with the TTL as a backstop. Normalise identities to canonical principals across sources, treat unmapped principals as non-matching and alarm on them, and never let retrieval run as a service account with union access, or the permission check exists only where someone remembered to write it.

Next: ranking with none of the usual signals.

Enjoying the preview?

Create a free account to unlock the rest of this course, the in-browser judge, and live AI mock interviews.

Sign up free to continue