Class: KeeperSecretsManager::Dto::KeeperRecordLink

Inherits:
Object
  • Object
show all
Defined in:
lib/keeper_secrets_manager/dto.rb

Overview

Typed view over a single linked-credential entry of a record (record.links).

A link entry carries recordUid, optional base64 data, and an optional path discriminator. Observed payload shapes (verified against the live backend):

  • path "meta" (self-link, recordUid == owning record): plain base64 JSON with allowedSettings (rotation, connections, portForwards, sessionRecording, typescriptRecording, aiEnabled, aiSessionTerminate, remoteBrowserIsolation), plus rotateOnTermination, version and no_update_services.
  • path nil (credential link to another record): plain base64 JSON with is_admin, is_launch_credential, is_iam_user, belongs_to and rotation_settings; or no data at all (pure record reference).
  • path "ai_settings" / "jit_settings" (self-links): data is AES-256-GCM encrypted under the owning record's key - see #get_decrypted_data.

Accessors never raise: parse, decode or decryption failures yield nil/false. The original link hash is kept untouched in raw, and #get_link_data returns the complete parsed payload, so fields unknown to this SDK version are preserved.

Naming: Ruby predicates take a trailing ? and drop the redundant is_ prefix (house style), so the Python reference's is_admin_user/is_launch_credential/ is_iam_user map to admin_user?/launch_credential?/iam_user? here.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(link_dict = {}) ⇒ KeeperRecordLink

Returns a new instance of KeeperRecordLink.



212
213
214
215
216
217
218
# File 'lib/keeper_secrets_manager/dto.rb', line 212

def initialize(link_dict = {})
  link_dict = {} unless link_dict.is_a?(Hash)
  @raw = link_dict.dup
  @record_uid = link_dict['recordUid']
  @data = link_dict['data']
  @path = link_dict['path']
end

Instance Attribute Details

#dataObject (readonly)

Returns the value of attribute data.



210
211
212
# File 'lib/keeper_secrets_manager/dto.rb', line 210

def data
  @data
end

#pathObject (readonly)

Returns the value of attribute path.



210
211
212
# File 'lib/keeper_secrets_manager/dto.rb', line 210

def path
  @path
end

#rawObject (readonly)

Returns the value of attribute raw.



210
211
212
# File 'lib/keeper_secrets_manager/dto.rb', line 210

def raw
  @raw
end

#record_uidObject (readonly)

Returns the value of attribute record_uid.



210
211
212
# File 'lib/keeper_secrets_manager/dto.rb', line 210

def record_uid
  @record_uid
end

Instance Method Details

#admin_user?Boolean

Returns:

  • (Boolean)


224
225
226
# File 'lib/keeper_secrets_manager/dto.rb', line 224

def admin_user?
  boolean_value('is_admin')
end

#ai_enabled?Boolean

Returns:

  • (Boolean)


268
269
270
# File 'lib/keeper_secrets_manager/dto.rb', line 268

def ai_enabled?
  boolean_value('aiEnabled', true)
end

#ai_session_terminate?Boolean

Returns:

  • (Boolean)


272
273
274
# File 'lib/keeper_secrets_manager/dto.rb', line 272

def ai_session_terminate?
  boolean_value('aiSessionTerminate', true)
end

#allows_connections?Boolean

Returns:

  • (Boolean)


248
249
250
# File 'lib/keeper_secrets_manager/dto.rb', line 248

def allows_connections?
  boolean_value('connections', true)
end

#allows_port_forwards?Boolean

Returns:

  • (Boolean)


252
253
254
# File 'lib/keeper_secrets_manager/dto.rb', line 252

def allows_port_forwards?
  boolean_value('portForwards', true)
end

#allows_remote_browser_isolation?Boolean

Returns:

  • (Boolean)


264
265
266
# File 'lib/keeper_secrets_manager/dto.rb', line 264

def allows_remote_browser_isolation?
  boolean_value('remoteBrowserIsolation', true)
end

#allows_rotation?Boolean

Returns:

  • (Boolean)


244
245
246
# File 'lib/keeper_secrets_manager/dto.rb', line 244

def allows_rotation?
  boolean_value('rotation', true)
end

#allows_session_recording?Boolean

Returns:

  • (Boolean)


256
257
258
# File 'lib/keeper_secrets_manager/dto.rb', line 256

def allows_session_recording?
  boolean_value('sessionRecording', true)
end

#allows_typescript_recording?Boolean

Returns:

  • (Boolean)


260
261
262
# File 'lib/keeper_secrets_manager/dto.rb', line 260

def allows_typescript_recording?
  boolean_value('typescriptRecording', true)
end

#belongs_to?Boolean

Returns:

  • (Boolean)


236
237
238
# File 'lib/keeper_secrets_manager/dto.rb', line 236

def belongs_to?
  boolean_value('belongs_to')
end

#get_ai_settings_data(record_key = nil) ⇒ Object

AI settings data from this link - only when path == "ai_settings" (encrypted under the owning record's key). Returns nil for any other path.



372
373
374
375
376
# File 'lib/keeper_secrets_manager/dto.rb', line 372

def get_ai_settings_data(record_key = nil)
  return nil unless @path == 'ai_settings'

  get_link_data(record_key)
end

#get_allowed_settingsObject

The allowedSettings object from the link data (empty hash when absent).



286
287
288
289
290
# File 'lib/keeper_secrets_manager/dto.rb', line 286

def get_allowed_settings
  parsed = parse_json_data
  allowed = parsed ? parsed['allowedSettings'] : nil
  allowed.is_a?(Hash) ? allowed : {}
end

#get_decoded_dataObject

Base64-decode data to a string (for debugging/advanced use), or nil.



300
301
302
303
304
305
306
# File 'lib/keeper_secrets_manager/dto.rb', line 300

def get_decoded_data
  return nil if @data.nil?

  Utils.base64_to_bytes(@data).force_encoding('UTF-8').scrub
rescue StandardError
  nil
end

#get_decrypted_data(record_key = nil) ⇒ Object

Decrypt the link data with the owning record's key (AES-256-GCM). record_key is the record's decrypted key bytes (record.record_key). Returns the decrypted string, or nil if data/key is missing or decryption fails.



333
334
335
336
337
338
339
340
# File 'lib/keeper_secrets_manager/dto.rb', line 333

def get_decrypted_data(record_key = nil)
  return nil if @data.nil? || record_key.nil?

  encrypted = Utils.base64_to_bytes(@data)
  Crypto.decrypt_aes_gcm(encrypted, record_key).force_encoding('UTF-8').scrub
rescue StandardError
  nil
end

#get_jit_settings_data(record_key = nil) ⇒ Object

JIT settings data from this link - only when path == "jit_settings" (encrypted under the owning record's key). Returns nil for any other path.



380
381
382
383
384
# File 'lib/keeper_secrets_manager/dto.rb', line 380

def get_jit_settings_data(record_key = nil)
  return nil unless @path == 'jit_settings'

  get_link_data(record_key)
end

The complete link data payload, handling both plain and encrypted JSON. Plain base64 JSON parses without a key; encrypted data requires the owning record's key. Ciphertext can coincidentally start with "{" or "[", so a failed plain-JSON parse falls through to decryption rather than giving up. The returned hash preserves all fields sent by the server, including ones this SDK version doesn't know about yet.



348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/keeper_secrets_manager/dto.rb', line 348

def get_link_data(record_key = nil)
  decoded = get_decoded_data
  return nil if decoded.nil?

  if decoded.start_with?('{') || decoded.start_with?('[')
    parsed = parse_json_to_dict(decoded)
    return parsed unless parsed.nil?
    # Leading {/[ was coincidental ciphertext - fall through to decryption.
  end

  decrypted = get_decrypted_data(record_key)
  return nil if decrypted.nil?

  parse_json_to_dict(decrypted)
end

The link data schema version (version) when it is an integer, else nil.



281
282
283
# File 'lib/keeper_secrets_manager/dto.rb', line 281

def get_link_data_version
  int_value('version')
end

#get_meta_data(record_key = nil) ⇒ Object

PAM settings data from this link - only when path == "meta" (plain JSON today; the key is accepted for forward compatibility).



366
367
368
# File 'lib/keeper_secrets_manager/dto.rb', line 366

def (record_key = nil)
  get_settings_for_path('meta', record_key)
end

#get_rotation_settingsObject

The rotation_settings object from the link data, or nil when absent.



293
294
295
296
297
# File 'lib/keeper_secrets_manager/dto.rb', line 293

def get_rotation_settings
  parsed = parse_json_data
  rotation = parsed ? parsed['rotation_settings'] : nil
  rotation.is_a?(Hash) ? rotation : nil
end

#get_settings_for_path(settings_path, record_key = nil) ⇒ Object

Settings data for any path, current or future. Automatically detects whether the data is plain or encrypted and handles it appropriately. Returns nil when the path doesn't match or parsing fails.



389
390
391
392
393
# File 'lib/keeper_secrets_manager/dto.rb', line 389

def get_settings_for_path(settings_path, record_key = nil)
  return nil unless @path == settings_path

  get_link_data(record_key)
end

#has_encrypted_data?Boolean

Whether the data appears encrypted, by inspecting the actual content (non-JSON and mostly non-printable) rather than path naming conventions.

Returns:

  • (Boolean)


322
323
324
325
326
327
328
# File 'lib/keeper_secrets_manager/dto.rb', line 322

def has_encrypted_data?
  decoded = get_decoded_data
  return false if decoded.nil?
  return false if decoded.start_with?('{') || decoded.start_with?('[')

  !printable_text?(decoded)
end

#has_readable_data?Boolean

Whether the link has readable JSON data (vs. encrypted/binary data).

Returns:

  • (Boolean)


309
310
311
312
# File 'lib/keeper_secrets_manager/dto.rb', line 309

def has_readable_data?
  decoded = get_decoded_data
  !decoded.nil? && (decoded.start_with?('{') || decoded.start_with?('['))
end

#iam_user?Boolean

Returns:

  • (Boolean)


232
233
234
# File 'lib/keeper_secrets_manager/dto.rb', line 232

def iam_user?
  boolean_value('is_iam_user')
end

#launch_credential?Boolean

Returns:

  • (Boolean)


228
229
230
# File 'lib/keeper_secrets_manager/dto.rb', line 228

def launch_credential?
  boolean_value('is_launch_credential')
end

#might_be_encrypted?Boolean

Whether this link's path indicates potentially encrypted data (currently ai_settings / jit_settings; other paths carry plain base64 JSON).

Returns:

  • (Boolean)


316
317
318
# File 'lib/keeper_secrets_manager/dto.rb', line 316

def might_be_encrypted?
  %w[ai_settings jit_settings].include?(@path)
end

#no_update_services?Boolean

Returns:

  • (Boolean)


240
241
242
# File 'lib/keeper_secrets_manager/dto.rb', line 240

def no_update_services?
  boolean_value('no_update_services')
end

#rotates_on_termination?Boolean

Returns:

  • (Boolean)


276
277
278
# File 'lib/keeper_secrets_manager/dto.rb', line 276

def rotates_on_termination?
  boolean_value('rotateOnTermination')
end

#to_sObject



220
221
222
# File 'lib/keeper_secrets_manager/dto.rb', line 220

def to_s
  "[KeeperRecordLink: record_uid=#{@record_uid}, path=#{@path}]"
end