Module: Familia::Features::ExternalIdentifier
- Defined in:
- lib/familia/features/external_identifier.rb
Overview
Familia::Features::ExternalIdentifier
Defined Under Namespace
Modules: ClassMethods 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.
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/familia/features/external_identifier.rb', line 177 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
236 237 238 239 240 241 242 |
# File 'lib/familia/features/external_identifier.rb', line 236 def destroy! # Clean up extid mapping when object is destroyed current_extid = instance_variable_get(:@extid) self.class.extid_lookup.del(current_extid) if current_extid super if defined?(super) end |
#external_identifier ⇒ String
Full-length alias for extid for clarity when needed
224 225 226 |
# File 'lib/familia/features/external_identifier.rb', line 224 def external_identifier extid end |
#external_identifier=(value) ⇒ Object
Full-length alias setter for extid
232 233 234 |
# File 'lib/familia/features/external_identifier.rb', line 232 def external_identifier=(value) self.extid = value end |