Class: Familia::Encryption::Registry
- Inherits:
-
Object
- Object
- Familia::Encryption::Registry
- Defined in:
- lib/familia/encryption/registry.rb
Overview
Registry pattern for managing encryption providers
Class Method Summary collapse
- .available_algorithms ⇒ Object
- .default_provider ⇒ Object
- .get(algorithm) ⇒ Object
-
.known_providers ⇒ Object
Every provider class Familia knows how to register, regardless of whether its runtime dependency is available on this node.
- .providers ⇒ Object
- .register(provider_class) ⇒ Object
- .reset_default_provider! ⇒ Object
-
.setup! ⇒ Object
Auto-register known providers.
Class Method Details
.available_algorithms ⇒ Object
69 70 71 |
# File 'lib/familia/encryption/registry.rb', line 69 def available_algorithms providers.keys end |
.default_provider ⇒ Object
57 58 59 60 61 62 63 |
# File 'lib/familia/encryption/registry.rb', line 57 def default_provider # Select provider with highest priority @default_provider ||= begin available = providers.values.select(&:available?) available.max_by(&:priority)&.new end end |
.get(algorithm) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/familia/encryption/registry.rb', line 20 def get(algorithm) provider_class = providers[algorithm] return provider_class.new if provider_class # Not registered. `register` only stores providers whose runtime # dependency is present (available? == true), so distinguish a # genuinely unknown algorithm from a known one whose provider isn't # installed on this node. The latter is the common misstep when # pinning `encrypted_field ..., algorithm:` ahead of a fleet rollout # (e.g. pinning xchacha20poly1305 before rbnacl/libsodium is # deployed), so point at the real fix instead of implying a typo. known = known_providers.find { |klass| algorithm == klass::ALGORITHM } if known # get is on both the write (encrypt) and read (decrypt) paths -- # Manager#decrypt resolves the provider from the stored envelope's # own algorithm. Installing the dependency is the fix for BOTH; # pinning to another algorithm is a write-time workaround only and # cannot decrypt ciphertext already written under this one, so the # message frames pinning as write-only rather than a general fix. # Each provider declares its own dependency (via .dependency_hint), # so this generic path stays accurate as providers are added without # naming any one library here. dependency = known.dependency_hint requirement = dependency ? " (requires #{dependency})" : '' raise EncryptionError, "Algorithm #{algorithm.inspect} is known but its provider " \ "(#{known.name}) is not available on this node -- its " \ "runtime dependency is missing#{requirement}. Install the " \ 'dependency to read or write this algorithm. (For writes you ' \ 'may instead pin the field to an available algorithm ' \ "(#{available_algorithms.inspect}), but that cannot decrypt " \ 'ciphertext already written with this one.)' end raise EncryptionError, "Unsupported algorithm: #{algorithm}" end |
.known_providers ⇒ Object
Every provider class Familia knows how to register, regardless of
whether its runtime dependency is available on this node. providers
holds only the subset that passed available?; this is the full set,
and the single source of truth shared by setup! (which registers
the available ones) and get (which uses it to tell an unknown
algorithm apart from a known-but-unavailable one).
79 80 81 82 83 84 85 |
# File 'lib/familia/encryption/registry.rb', line 79 def known_providers [ Providers::XChaCha20Poly1305Provider, Providers::AESGCMProvider, # Future: Providers::ChaCha20Poly1305Provider ] end |
.providers ⇒ Object
10 11 12 |
# File 'lib/familia/encryption/registry.rb', line 10 def providers @providers ||= {} end |
.register(provider_class) ⇒ Object
14 15 16 17 18 |
# File 'lib/familia/encryption/registry.rb', line 14 def register(provider_class) return unless provider_class.available? providers[provider_class::ALGORITHM] = provider_class end |
.reset_default_provider! ⇒ Object
65 66 67 |
# File 'lib/familia/encryption/registry.rb', line 65 def reset_default_provider! @default_provider = nil end |
.setup! ⇒ Object
Auto-register known providers
88 89 90 |
# File 'lib/familia/encryption/registry.rb', line 88 def setup! known_providers.each { |provider_class| register(provider_class) } end |