Class: Parse::Authorization::Context

Inherits:
Object
  • Object
show all
Defined in:
lib/parse/authorization.rb

Overview

Per-client authorization state: the two caches, their TTLs, and the optional upstream-role reader.

One of these is owned by each Client. It deliberately does NOT own the HTTP client: it holds a back-reference and asks the client to make the /users/me call, so there is exactly one place that knows how to talk to a Parse application and it is the client itself.

Constant Summary collapse

DEFAULT_IDENTITY_TTL =
3600
DEFAULT_ROLE_TTL =
30
ROLE_GRAPH_MAX_DEPTH =

Depth cap for the role-graph walk. Bounds a cyclic or pathological hierarchy; see Role.all_for_user.

10

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(client:) ⇒ Context

Returns a new instance of Context.



171
172
173
174
175
176
177
178
179
# File 'lib/parse/authorization.rb', line 171

def initialize(client:)
  @client = client
  @identity_cache = MemoryCache.new
  @role_cache = MemoryCache.new
  @identity_cache_ttl = DEFAULT_IDENTITY_TTL
  @role_cache_ttl = DEFAULT_ROLE_TTL
  @upstream_role_reader = nil
  @compare_upstream_roles = false
end

Instance Attribute Details

#clientParse::Client (readonly)

Returns the client this context authorizes for.

Returns:



162
163
164
# File 'lib/parse/authorization.rb', line 162

def client
  @client
end

#compare_upstream_rolesBoolean

Returns when true, and a reader is set, every role resolution also reads the upstream closure and emits a parse.cache.role_compare event. The comparison NEVER changes what #resolve returns. The upstream value would become an authorization input the moment it were consumed, and it comes from a database this SDK does not own, so it stays observable-only until the two closures have been reconciled against real traffic.

Returns:

  • (Boolean)

    when true, and a reader is set, every role resolution also reads the upstream closure and emits a parse.cache.role_compare event. The comparison NEVER changes what #resolve returns. The upstream value would become an authorization input the moment it were consumed, and it comes from a database this SDK does not own, so it stays observable-only until the two closures have been reconciled against real traffic.



159
160
161
# File 'lib/parse/authorization.rb', line 159

def compare_upstream_roles
  @compare_upstream_roles
end

#identity_cacheObject

Returns the identity plane. Maps session token to user id.

Returns:

  • (Object)

    the identity plane. Maps session token to user id.



137
138
139
# File 'lib/parse/authorization.rb', line 137

def identity_cache
  @identity_cache
end

#identity_cache_ttlInteger

Returns identity-entry TTL in seconds.

Returns:

  • (Integer)

    identity-entry TTL in seconds.



143
144
145
# File 'lib/parse/authorization.rb', line 143

def identity_cache_ttl
  @identity_cache_ttl
end

#role_cacheObject

Returns the role plane. Maps user id to a Set of role names.

Returns:

  • (Object)

    the role plane. Maps user id to a Set of role names.



140
141
142
# File 'lib/parse/authorization.rb', line 140

def role_cache
  @role_cache
end

#role_cache_ttlInteger

Returns role-entry TTL in seconds.

Returns:

  • (Integer)

    role-entry TTL in seconds.



146
147
148
# File 'lib/parse/authorization.rb', line 146

def role_cache_ttl
  @role_cache_ttl
end

#upstream_role_reader#roles_for?

Returns read-only reader for Parse Server's own role cache. Never consumed for authorization; see #compare_upstream_roles.

Returns:

  • (#roles_for, nil)

    read-only reader for Parse Server's own role cache. Never consumed for authorization; see #compare_upstream_roles.



150
151
152
# File 'lib/parse/authorization.rb', line 150

def upstream_role_reader
  @upstream_role_reader
end

Instance Method Details

#configure(identity_cache: nil, role_cache: nil, identity_cache_ttl: nil, role_cache_ttl: nil, upstream_role_reader: nil, compare_upstream_roles: nil) ⇒ self

Apply settings, leaving anything not passed unchanged.

Returns:

  • (self)


183
184
185
186
187
188
189
190
191
192
193
# File 'lib/parse/authorization.rb', line 183

def configure(identity_cache: nil, role_cache: nil,
              identity_cache_ttl: nil, role_cache_ttl: nil,
              upstream_role_reader: nil, compare_upstream_roles: nil)
  @identity_cache = identity_cache unless identity_cache.nil?
  @role_cache = role_cache unless role_cache.nil?
  @identity_cache_ttl = identity_cache_ttl unless identity_cache_ttl.nil?
  @role_cache_ttl = role_cache_ttl unless role_cache_ttl.nil?
  @upstream_role_reader = upstream_role_reader unless upstream_role_reader.nil?
  @compare_upstream_roles = compare_upstream_roles unless compare_upstream_roles.nil?
  self
end

#inspectObject



253
254
255
# File 'lib/parse/authorization.rb', line 253

def inspect
  "#<Parse::Authorization::Context client=#{@client.respond_to?(:application_id) ? @client.application_id : @client.class}>"
end

#invalidate(session_token) ⇒ Object

Forget one session token. Call from a logout path that revokes out-of-band; Parse::Cache::Invalidation does this automatically from the _Session after_logout trigger when webhooks are installed.

The role plane is keyed by user id and is unaffected; use #invalidate_user_roles for that.

Parameters:



234
235
236
237
# File 'lib/parse/authorization.rb', line 234

def invalidate(session_token)
  return if session_token.nil?
  @identity_cache.invalidate(session_token.to_s)
end

#invalidate_user_roles(user_id) ⇒ Object

Forget one user's cached role closure. Call after any _Role.users mutation affecting them.

Parameters:



242
243
244
245
# File 'lib/parse/authorization.rb', line 242

def invalidate_user_roles(user_id)
  return if user_id.nil?
  @role_cache.invalidate(user_id.to_s)
end

#reset_caches!Object

Drop every entry in both planes.



248
249
250
251
# File 'lib/parse/authorization.rb', line 248

def reset_caches!
  @identity_cache.clear if @identity_cache.respond_to?(:clear)
  @role_cache.clear if @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.

A nil or empty token yields an anonymous Resolved. The caller decides whether that is acceptable; Parse::ACLScope.require_session_token and Parse::AtlasSearch.require_session_token are where that policy lives.

The two lookups are cached independently, so several sessions belonging to one user share a single role-graph walk.

Parameters:

Returns:

Raises:



210
211
212
213
214
215
# File 'lib/parse/authorization.rb', line 210

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)
  Resolved.new(user_id, lookup_role_names(user_id))
end

#resolve_user(user_id) ⇒ Resolved

Resolve a user id that is already trusted, skipping /users/me. Used by the acl_user: path, which has a User pointer rather than a token.

Parameters:

Returns:



222
223
224
225
# File 'lib/parse/authorization.rb', line 222

def resolve_user(user_id)
  return Resolved.new(nil, Set.new) if user_id.nil? || user_id.to_s.empty?
  Resolved.new(user_id.to_s, lookup_role_names(user_id.to_s))
end