Module: Parse::AtlasSearch::Session

Defined in:
lib/parse/atlas_search/session.rb

Overview

Resolves session tokens to user identities and inherited role sets for ACL-scoped Atlas Search queries.

Atlas Search runs aggregations directly against MongoDB and therefore bypasses Parse Server’s per-request ACL enforcement. To compile a _rperm $match stage (see Parse::ACL.read_predicate) the caller needs to know two things about the requesting session:

1. The +_User.objectId+ that owns the session.
2. The transitive upward closure of role names that user
   inherits permissions from (cf. {Parse::Role.all_for_user}).

Both lookups can be expensive — token → user requires a /users/me round-trip, and user → roles can require multiple _Role queries to walk the inheritance graph. Both are cached separately so a single agent turn that runs several Atlas Search tools amortizes the cost.

Two distinct caches:

* +session_cache+: maps +session_token+ to +user_id+. Long
  TTL (1 hour default), invalidation profile is logout. Apps
  that need sub-TTL revocation must call {.invalidate}
  explicitly from their logout path.

* +role_cache+: maps +user_id+ to a +Set+ of role names. Short
  TTL (2 minutes default), invalidation profile is role-graph
  mutation. Stale role data here yields incorrect ACL
  decisions, so the default is conservatively short.

The default cache implementation is process-local (MemoryCache) and guarded by a Mutex. Apps that need shared cross-process caching (Redis, Memcached) may install a replacement via session_cache= / role_cache=; the replacement must respond to get(key), set(key, value, ttl:), and invalidate(key).

Defined Under Namespace

Classes: InvalidSession, MemoryCache, Resolved

Class Method Summary collapse

Class Method Details

.invalidate(session_token) ⇒ Object

Forget a session_token entry from the session-token cache. Apps that revoke sessions out-of-band (logout, password reset, admin revoke) should call this from the same path so subsequent Atlas Search requests don’t act on the stale user_id mapping. The role_names cache is keyed on user_id and is not affected — call invalidate_user_roles to clear that separately.

Parameters:



161
162
163
164
# File 'lib/parse/atlas_search/session.rb', line 161

def invalidate(session_token)
  return if session_token.nil?
  Parse::AtlasSearch.session_cache.invalidate(session_token.to_s)
end

.invalidate_user_roles(user_id) ⇒ Object

Forget cached role membership for a user_id. Call after any _Role.users mutation that affects this user (role grant / revoke, role-graph reshape).

Parameters:



170
171
172
173
# File 'lib/parse/atlas_search/session.rb', line 170

def invalidate_user_roles(user_id)
  return if user_id.nil?
  Parse::AtlasSearch.role_cache.invalidate(user_id.to_s)
end

.reset_caches!Object

Drop every cached entry across both caches. Useful in tests and in startup hooks for processes that fork after warming the cache.



178
179
180
181
# File 'lib/parse/atlas_search/session.rb', line 178

def reset_caches!
  Parse::AtlasSearch.session_cache.clear if Parse::AtlasSearch.session_cache.respond_to?(:clear)
  Parse::AtlasSearch.role_cache.clear if Parse::AtlasSearch.role_cache.respond_to?(:clear)
end

.resolve(session_token) ⇒ Resolved

Resolve a session_token to the requesting user and the transitive set of role names whose role:NAME permission strings should be checked against _rperm.

nil or empty session_token → anonymous Resolved with user_id: nil and an empty role_names set. The caller decides whether to refuse the request (the require_session_token toggle on Parse::AtlasSearch) or treat as public-only.

Cache layering: token-to-user_id is checked first; on hit the slower /users/me round-trip is skipped. User-to-roles is then checked independently (a single user shared across sessions amortizes the role lookup).

Parameters:

  • session_token (String, nil)

    the X-Parse-Session-Token value from the requesting session.

Returns:

Raises:

  • (InvalidSession)

    when the token cannot be resolved by /users/me (404 / 209 invalid session token / 401).



145
146
147
148
149
150
151
# File 'lib/parse/atlas_search/session.rb', line 145

def resolve(session_token)
  return Resolved.new(nil, Set.new) if session_token.nil? || session_token.to_s.empty?

  user_id = lookup_user_id(session_token.to_s)
  role_names = lookup_role_names(user_id)
  Resolved.new(user_id, role_names)
end