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.
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/familia/features/external_identifier.rb', line 219 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 prefix from feature options, default to "ext" = self.class.(:external_identifier) prefix = [:prefix] || 'ext' "#{prefix}_#{external_part}" end |
#destroy! ⇒ Object
278 279 280 281 282 283 284 |
# File 'lib/familia/features/external_identifier.rb', line 278 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
266 267 268 |
# File 'lib/familia/features/external_identifier.rb', line 266 def external_identifier extid end |
#external_identifier=(value) ⇒ Object
Full-length alias setter for extid
274 275 276 |
# File 'lib/familia/features/external_identifier.rb', line 274 def external_identifier=(value) self.extid = value end |