Module: Familia::Features::ExternalIdentifier
- Defined in:
- lib/familia/features/external_identifier.rb
Overview
Familia::Features::ExternalIdentifier
Defined Under Namespace
Modules: ModelClassMethods, ModelInstanceMethods Classes: ExternalIdentifierError, ExternalIdentifierFieldType
Instance Method Summary collapse
-
#derive_external_identifier ⇒ String?
Derives a deterministic, public-facing external identifier from the object's internal
objid. - #destroy! ⇒ Object
-
#external_identifier ⇒ String
Full-length alias for extid for clarity when needed.
-
#external_identifier=(value) ⇒ Object
Full-length alias setter for extid.
Instance Method Details
#derive_external_identifier ⇒ String?
Derives a deterministic, public-facing external identifier from the object's
internal objid.
This method uses the objid's high-quality randomness to seed a
pseudorandom number generator (PRNG). The PRNG then acts as a complex,
deterministic function to produce a new identifier that has no discernible
mathematical correlation to the objid. This is a security measure to
prevent leaking information (like timestamps from UUIDv7) from the internal
identifier to the public one.
The resulting identifier is always deterministic: the same objid will
always produce the same extid, which is crucial for lookups.
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
# File 'lib/familia/features/external_identifier.rb', line 274 def derive_external_identifier raise ExternalIdentifierError, 'Missing objid field' unless respond_to?(:objid) current_objid = objid return nil if current_objid.nil? || current_objid.to_s.empty? # Validate objid provenance for security guarantees validate_objid_provenance! # Normalize the objid to a consistent hex representation first. normalized_hex = normalize_objid_to_hex(current_objid) # Use the objid's randomness to create a deterministic, yet secure, # external identifier. We do not use SecureRandom here because the output # must be deterministic. # # The process is as follows: # 1. The objid (a high-entropy value) is hashed to create a uniform seed. # 2. The seed initializes a standard PRNG (Random.new). # 3. The PRNG acts as a deterministic function to generate a sequence of # bytes that appears random, obscuring the original objid. # 1. Create a high-quality, uniform seed from the objid's entropy. seed = Digest::SHA256.digest(normalized_hex) # 2. Initialize a PRNG with the seed. The same seed will always produce # the same sequence of "random" numbers. prng = Random.new(seed.unpack1('Q>')) # 3. Generate 16 bytes (128 bits) of deterministic output. random_bytes = prng.bytes(16) # Encode as a base36 string for a compact, URL-safe identifier. # 128 bits is approximately 25 characters in base36. external_part = random_bytes.unpack1('H*').to_i(16).to_s(36).rjust(25, '0') # Get format from feature options and interpolate the ID = self.class.(:external_identifier) format = [:format] || 'ext_%{id}' format % { id: external_part } end |
#destroy! ⇒ Object
333 334 335 336 337 338 339 |
# File 'lib/familia/features/external_identifier.rb', line 333 def destroy! # Clean up extid mapping when object is destroyed current_extid = instance_variable_get(:@extid) self.class.extid_lookup.remove_field(current_extid) if current_extid super if defined?(super) end |
#external_identifier ⇒ String
Full-length alias for extid for clarity when needed
321 322 323 |
# File 'lib/familia/features/external_identifier.rb', line 321 def external_identifier extid end |
#external_identifier=(value) ⇒ Object
Full-length alias setter for extid
329 330 331 |
# File 'lib/familia/features/external_identifier.rb', line 329 def external_identifier=(value) self.extid = value end |