Module: Parse::Authorization
- Defined in:
- lib/parse/authorization.rb
Overview
Resolution of a caller's identity and inherited roles, and the caches that make that resolution cheap.
Why this is not part of Atlas Search. It used to be. Session-token
resolution and role-closure expansion were written for
Parse::AtlasSearch, because $search was the first thing that ran
aggregations straight against MongoDB and therefore the first thing that
had to enforce ACLs itself. Everything since has reached back through it:
Parse::ACLScope called Parse::AtlasSearch::Session.resolve, and
Parse::MongoDB.aggregate calls Parse::ACLScope, so
Parse::Query#results_direct on a plain query with no $search anywhere
in it depended on the Atlas Search namespace to decide who the caller was.
That is backwards. Deciding who someone is and what they may read is
authorization infrastructure, and Atlas Search is one consumer of it:
Atlas Search ─┐
Aggregates ──┼─> Parse::Authorization ─> identity / role caches
Direct query ─┘
Policy that is genuinely about Atlas Search stays on Atlas Search:
Parse::AtlasSearch.require_session_token decides whether $search may
run anonymously, which is a question about that feature, not about
identity.
Two caches, because they invalidate on different events.
* The **identity plane** maps a session token to a user id. Long TTL
(1 hour), invalidated by logout and by a `_User` write. Named
`identity_cache` rather than `session_cache` because it stores neither
`_Session` rows nor session objects: it stores one string per token.
The old name led readers to reason about `_Session` semantics that
were never involved.
* The **role plane** maps a user id to a `Set` of role names. Short TTL
(30 seconds), invalidated by any `_Role` write. Stale entries here
produce wrong ACL decisions rather than merely slow ones, so the
default is conservative.
State belongs to a client, not to the process. A Context is owned by
one Client and reachable as client.authorization. Two named
clients pointed at two Parse applications must never resolve a token
against each other's caches or each other's /users/me, which is exactly
what a set of module-level globals allowed. Authorization.configure
exists as a boundary convenience and configures the DEFAULT client's
context; below the boundary, client: is required and has no default.
Defined Under Namespace
Classes: Context, InvalidSession, MemoryCache, Resolved
Class Method Summary collapse
-
.configure(**kwargs) ⇒ Parse::Authorization::Context
Configure the DEFAULT client's authorization context.
-
.resolve(session_token, client:) ⇒ Resolved
Resolve a session token against a specific client.
- .resolve_user(user_id, client:) ⇒ Object
Class Method Details
.configure(**kwargs) ⇒ Parse::Authorization::Context
Configure the DEFAULT client's authorization context.
This is a boundary convenience, matching the shape already used for
Parse::AtlasSearch.search(..., client: Parse.client): the common
single-application case should not have to name the client. It is
explicitly NOT the source of truth. The state lives on
client.authorization, one context per client, which is what stops
two named clients resolving tokens against each other's caches. To
configure a secondary application, call
other_client.authorization.configure(...) directly.
439 440 441 |
# File 'lib/parse/authorization.rb', line 439 def configure(**kwargs) Parse.client..configure(**kwargs) end |
.resolve(session_token, client:) ⇒ Resolved
Resolve a session token against a specific client.
client: is required and has no default. Below the API boundary
there is no such thing as "the" client, and defaulting to
Parse.client here is precisely the bug this module exists to close:
a token belonging to application B would be validated against
application A.
454 455 456 457 |
# File 'lib/parse/authorization.rb', line 454 def resolve(session_token, client:) raise ArgumentError, "Parse::Authorization.resolve requires client:" if client.nil? client..resolve(session_token) end |
.resolve_user(user_id, client:) ⇒ Object
460 461 462 463 |
# File 'lib/parse/authorization.rb', line 460 def resolve_user(user_id, client:) raise ArgumentError, "Parse::Authorization.resolve_user requires client:" if client.nil? client..resolve_user(user_id) end |