Class: Puppet::Provider::YumrepoMetadataKey

Inherits:
Puppet::Provider
  • Object
show all
Defined in:
lib/puppet/provider/yumrepo_metadata_key.rb

Direct Known Subclasses

GpgKeyring

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.all_homes_for(_repo) ⇒ Array<String>

Includes stale homes from a repo’s previous source URL, which ‘destroy` sweeps too.

Returns:

  • (Array<String>)

    every home a repo’s keys could be in

Raises:

  • (NotImplementedError)


18
19
20
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 18

def all_homes_for(_repo)
  raise NotImplementedError, "#{self} must implement .all_homes_for"
end

.canonical_key_digest(text) ⇒ Object

Canonicalise a key so semantically-equal keys hash equally: import ‘text` into a throwaway keyring, re-export it –armor, and SHA256 the result. This normalises armor formatting/packet ordering and captures expiry and subkey changes that a raw compare of the supplied ASCII would miss.



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 90

def canonical_key_digest(text)
  ret = Dir.mktmpdir do |tmp|
    keyfile = File.join(tmp, 'k.asc')
    File.write(keyfile, text)
    gpg_at(tmp, '--import', keyfile)
    primary = parse_primary_fprs(gpg_at(tmp, '--with-colons', '--fingerprint', '--list-keys')).first
    exported = primary && gpg_export_armored(tmp, primary)
    exported ? Digest::SHA256.hexdigest(exported) : nil
  rescue Puppet::ExecutionFailure => e
    debug("canonical_key_digest failed: #{e.message.lines.first.to_s.strip}")
    nil
  end
  debug("canonical_key_digest -> #{ret.inspect}")
  ret
end

.export_key(_home, _fingerprint) ⇒ String?

Returns the stored key, or nil if absent.

Returns:

  • (String, nil)

    the stored key, or nil if absent

Raises:

  • (NotImplementedError)


28
29
30
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 28

def export_key(_home, _fingerprint)
  raise NotImplementedError, "#{self} must implement .export_key"
end

.extract_armored_key(text, context) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 74

def extract_armored_key(text, context)
  match = text.to_s.match(%r{-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----}m)
  unless match
    warning("#{context} did not contain an armored public key: #{text.to_s.lines.first.to_s.strip}") unless text.to_s.strip.empty?
    return nil
  end

  extra = text.to_s.sub(match[0], '').strip
  warning("#{context} included unexpected output: #{extra.lines.first.to_s.strip}") unless extra.empty?
  "#{match[0]}\n"
end

.fingerprints_in(_home) ⇒ Array<String>

Returns fingerprint of each primary key in ‘home`, ignoring subkeys.

Returns:

  • (Array<String>)

    fingerprint of each primary key in ‘home`, ignoring subkeys

Raises:

  • (NotImplementedError)


23
24
25
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 23

def fingerprints_in(_home)
  raise NotImplementedError, "#{self} must implement .fingerprints_in"
end

.gpg_at(home, *args) ⇒ Object



130
131
132
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 130

def gpg_at(home, *args)
  gpg('--homedir', home, *gpg_common_flags, *args)
end

.gpg_common_flagsObject



134
135
136
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 134

def gpg_common_flags
  ['--batch', '--no-tty', '--no-autostart', '--no-permission-warning']
end

.gpg_export_armored(home, fingerprint) ⇒ Object



120
121
122
123
124
125
126
127
128
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 120

def gpg_export_armored(home, fingerprint)
  out =
    begin
      gpg_at(home, '--export', '--armor', fingerprint)
    rescue Puppet::ExecutionFailure
      ''
    end
  extract_armored_key(out, "gpg export for #{fingerprint}")
end

.instancesObject



45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 45

def instances
  ret = live_homes.flat_map do |repo, home|
    next [] unless File.directory?(home) # home may not exist yet if dnf hasn't populated the cache for this repo

    fingerprints_in(home).map do |fpr|
      new(ensure: :present, name: "#{repo}:#{fpr}", repo: repo, fingerprint: fpr,
          home: home, content: export_key(home, fpr))
    end
  end
  debug("instances -> #{ret.map(&:name).inspect}")
  ret
end

.live_homesArray<Array(String, String)>

Returns ‘[repo, home]` per configured repo. A home may not exist yet; dnf only creates it on first fetch.

Returns:

  • (Array<Array(String, String)>)

    ‘[repo, home]` per configured repo. A home may not exist yet; dnf only creates it on first fetch.

Raises:

  • (NotImplementedError)


12
13
14
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 12

def live_homes
  raise NotImplementedError, "#{self} must implement .live_homes"
end

.parse_primary_fprs(text) ⇒ Object

gpg –with-colons emits a ‘pub:` record for each primary key immediately followed by its `fpr:` record (fingerprint in field 10). Pair adjacent records and keep the fpr after each `pub:`, ignoring `sub:` (subkey) fprs.



68
69
70
71
72
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 68

def parse_primary_fprs(text)
  text.lines.map { |line| line.split(':') }.each_cons(2).filter_map do |record, next_record|
    next_record[9] if record[0] == 'pub' && next_record[0] == 'fpr'
  end
end

.prefetch(resources) ⇒ Object



58
59
60
61
62
63
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 58

def prefetch(resources)
  instances.each do |prov|
    res = resources[prov.name]
    res.provider = prov if res
  end
end

.primary_fpr_of_content(text) ⇒ Object



106
107
108
109
110
111
112
113
114
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 106

def primary_fpr_of_content(text)
  ret = Dir.mktmpdir do |tmp|
    keyfile = File.join(tmp, 'k.asc')
    File.write(keyfile, text)
    parse_primary_fprs(gpg_at(tmp, *show_keys_args(keyfile))).first
  end
  debug("primary_fpr_of_content -> #{ret.inspect}")
  ret
end

.remove_key(_home, _fingerprint) ⇒ Object

Raises:

  • (NotImplementedError)


36
37
38
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 36

def remove_key(_home, _fingerprint)
  raise NotImplementedError, "#{self} must implement .remove_key"
end

.show_keys_args(path) ⇒ Object



138
139
140
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 138

def show_keys_args(path)
  ['--with-colons', '--show-keys', path]
end

.store_key(_home, _fingerprint, _text) ⇒ Object

Raises:

  • (NotImplementedError)


32
33
34
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 32

def store_key(_home, _fingerprint, _text)
  raise NotImplementedError, "#{self} must implement .store_key"
end

.trusted?(_home, _fingerprint) ⇒ Boolean

Returns whether the key is present and trusted to verify metadata.

Returns:

  • (Boolean)

    whether the key is present and trusted to verify metadata

Raises:

  • (NotImplementedError)


41
42
43
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 41

def trusted?(_home, _fingerprint)
  raise NotImplementedError, "#{self} must implement .trusted?"
end

.url_hash(source) ⇒ Object



116
117
118
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 116

def url_hash(source)
  Digest::SHA256.digest(source)[0, 8].unpack1('H*')
end

Instance Method Details

#contentObject



169
170
171
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 169

def content
  @property_hash[:content] || :absent
end

#content=(value) ⇒ Object



173
174
175
176
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 173

def content=(value)
  debug("content= re-importing #{resource[:fingerprint]}")
  import_key(value)
end

#createObject



147
148
149
150
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 147

def create
  import_key(resource[:content])
  @property_hash[:ensure] = :present
end

#destroyObject

Deletes the key not only in the ‘live’ keystore, but also in any others than might exist (from eg. when the baseurl for a repo was different)



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 153

def destroy
  homes = self.class.all_homes_for(resource[:repo])
  debug("destroy sweeping #{resource[:fingerprint]} across #{homes.inspect}")
  homes.each do |home|
    next unless File.directory?(home)

    begin
      self.class.remove_key(home, resource[:fingerprint])
      debug("destroy removed #{resource[:fingerprint]} from #{home.inspect}")
    rescue Puppet::ExecutionFailure => e
      debug("destroy skipped #{home.inspect}: #{e.message.lines.first.to_s.strip}")
    end
  end
  @property_hash[:ensure] = :absent
end

#exists?Boolean

Returns:

  • (Boolean)


143
144
145
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 143

def exists?
  @property_hash[:ensure] == :present
end

#fingerprintObject



192
193
194
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 192

def fingerprint
  @property_hash[:fingerprint]
end

#repoObject

NOTE: mk_resource_methods can’t be used here. It needs a type-bound provider (this abstract parent has resource_type == nil), and running it in the concrete providers would regenerate a ‘content=` that just writes @property_hash, clobbering our importing content= below. These trivial getters are the mk_resource_methods equivalent, written by hand.



188
189
190
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 188

def repo
  @property_hash[:repo]
end

#trusted?Boolean

Returns:

  • (Boolean)


178
179
180
181
# File 'lib/puppet/provider/yumrepo_metadata_key.rb', line 178

def trusted?
  home = live_home
  home && self.class.trusted?(home, resource[:fingerprint])
end