Module: LocalVault::EnvProjection
- Defined in:
- lib/localvault/env_projection.rb
Defined Under Namespace
Classes: Entry, InvalidMapping, UnknownProfile
Constant Summary collapse
- ENV_NAME_PATTERN =
/\A[A-Za-z_][A-Za-z0-9_]*\z/- PROFILES =
{ "aws" => { only: ["AWS_IAM.*"], map: { "AWS_IAM.access_key_id" => "AWS_ACCESS_KEY_ID", "AWS_IAM.secret_access_key" => "AWS_SECRET_ACCESS_KEY", "AWS_IAM.session_token" => "AWS_SESSION_TOKEN" } }, # Rails reads its credentials key from ENV["RAILS_MASTER_KEY"] when # config/master.key is absent (railties: encrypted(..., env_key: # "RAILS_MASTER_KEY")), so injecting that one variable is enough to run a # Rails app with no key file on disk. "rails" => { only: ["rails.*"], map: { "rails.master_key" => "RAILS_MASTER_KEY" } } }.freeze
Class Method Summary collapse
- .apply_mapping(entry, mappings, on_skip:) ⇒ Object
- .entries(secrets, project: nil, only: nil, except: nil, map: nil, profile: nil, on_skip: nil) ⇒ Object
- .flatten(secrets, project:, on_skip:) ⇒ Object
- .flatten_group(group, pairs, on_skip:) ⇒ Object
- .include_entry?(key, selectors) ⇒ Boolean
- .parse_map(value) ⇒ Object
- .parse_selectors(value) ⇒ Object
- .profile_config(profile) ⇒ Object
-
.rails_environment_mapping(environment) ⇒ Object
Per-environment credentials (config/credentials/production.key) still have to arrive as RAILS_MASTER_KEY — Rails reads no other variable.
- .safe_env_name?(name) ⇒ Boolean
- .selector_match?(key, selectors) ⇒ Boolean
Class Method Details
.apply_mapping(entry, mappings, on_skip:) ⇒ Object
139 140 141 142 143 144 145 146 147 |
# File 'lib/localvault/env_projection.rb', line 139 def self.apply_mapping(entry, mappings, on_skip:) mapped_name = mappings.fetch(entry.key, entry.env_name) unless safe_env_name?(mapped_name) on_skip&.call(entry.key) return nil end Entry.new(key: entry.key, env_name: mapped_name, value: entry.value) end |
.entries(secrets, project: nil, only: nil, except: nil, map: nil, profile: nil, on_skip: nil) ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/localvault/env_projection.rb', line 38 def self.entries(secrets, project: nil, only: nil, except: nil, map: nil, profile: nil, on_skip: nil) profile_config = profile_config(profile) selectors = parse_selectors(only) || profile_config[:only] exclusions = parse_selectors(except) || [] mappings = profile_config[:map].merge(parse_map(map)) flatten(secrets, project: project, on_skip: on_skip) .select { |entry| include_entry?(entry.key, selectors) } .reject { |entry| selector_match?(entry.key, exclusions) } .map { |entry| apply_mapping(entry, mappings, on_skip: on_skip) } .compact end |
.flatten(secrets, project:, on_skip:) ⇒ Object
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/localvault/env_projection.rb', line 82 def self.flatten(secrets, project:, on_skip:) if project group = secrets[project] return [] unless group.is_a?(Hash) group.filter_map do |key, value| if safe_env_name?(key) Entry.new(key: key, env_name: key, value: value.to_s) else on_skip&.call(key) nil end end else secrets.flat_map do |key, value| if value.is_a?(Hash) flatten_group(key, value, on_skip: on_skip) elsif safe_env_name?(key) [Entry.new(key: key, env_name: key, value: value.to_s)] else on_skip&.call(key) [] end end end end |
.flatten_group(group, pairs, on_skip:) ⇒ Object
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/localvault/env_projection.rb', line 109 def self.flatten_group(group, pairs, on_skip:) unless safe_env_name?(group) on_skip&.call(group) return [] end pairs.filter_map do |key, value| if safe_env_name?(key) Entry.new(key: "#{group}.#{key}", env_name: "#{group.upcase}__#{key}", value: value.to_s) else on_skip&.call("#{group}.#{key}") nil end end end |
.include_entry?(key, selectors) ⇒ Boolean
125 126 127 |
# File 'lib/localvault/env_projection.rb', line 125 def self.include_entry?(key, selectors) selectors.nil? || selector_match?(key, selectors) end |
.parse_map(value) ⇒ Object
60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/localvault/env_projection.rb', line 60 def self.parse_map(value) Array(value).compact.flat_map { |v| v.to_s.split(",") }.each_with_object({}) do |pair, hash| next if pair.strip.empty? key, env_name = pair.split("=", 2).map(&:strip) raise InvalidMapping, "Invalid map '#{pair}'. Use KEY=ENV_NAME" if key.to_s.empty? || env_name.to_s.empty? InputValidation.mapping!(key, env_name) hash[key] = env_name end rescue InputValidation::InvalidInput => e raise InvalidMapping, e. end |
.parse_selectors(value) ⇒ Object
51 52 53 54 55 56 57 58 |
# File 'lib/localvault/env_projection.rb', line 51 def self.parse_selectors(value) values = Array(value).compact.flat_map { |v| v.to_s.split(",") } selectors = values.map(&:strip).reject(&:empty?) selectors.each { |selector| InputValidation.selector!(selector) } selectors.empty? ? nil : selectors rescue InputValidation::InvalidInput => e raise InvalidMapping, e. end |
.profile_config(profile) ⇒ Object
74 75 76 77 78 79 80 |
# File 'lib/localvault/env_projection.rb', line 74 def self.profile_config(profile) return { only: nil, map: {} } if profile.nil? || profile.to_s.empty? PROFILES.fetch(profile.to_s) do raise UnknownProfile, "Unknown env profile '#{profile}'" end end |
.rails_environment_mapping(environment) ⇒ Object
Per-environment credentials (config/credentials/production.key) still have to arrive as RAILS_MASTER_KEY — Rails reads no other variable. This builds the mapping for one environment.
34 35 36 |
# File 'lib/localvault/env_projection.rb', line 34 def self.rails_environment_mapping(environment) { "rails.#{environment}_key" => "RAILS_MASTER_KEY" } end |
.safe_env_name?(name) ⇒ Boolean
149 150 151 |
# File 'lib/localvault/env_projection.rb', line 149 def self.safe_env_name?(name) name.is_a?(String) && name.match?(ENV_NAME_PATTERN) end |
.selector_match?(key, selectors) ⇒ Boolean
129 130 131 132 133 134 135 136 137 |
# File 'lib/localvault/env_projection.rb', line 129 def self.selector_match?(key, selectors) selectors.any? do |selector| if selector.end_with?(".*") key.start_with?("#{selector.delete_suffix(".*")}.") else key == selector end end end |