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.
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/familia/features/external_identifier.rb', line 245 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
304 305 306 307 308 309 310 |
# File 'lib/familia/features/external_identifier.rb', line 304 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
292 293 294 |
# File 'lib/familia/features/external_identifier.rb', line 292 def external_identifier extid end |
#external_identifier=(value) ⇒ Object
Full-length alias setter for extid
300 301 302 |
# File 'lib/familia/features/external_identifier.rb', line 300 def external_identifier=(value) self.extid = value end |