puppet 6.25.0 → 6.27.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/CODEOWNERS +1 -1
  3. data/Gemfile +2 -2
  4. data/Gemfile.lock +39 -32
  5. data/lib/puppet/application/lookup.rb +74 -24
  6. data/lib/puppet/concurrent/thread_local_singleton.rb +5 -3
  7. data/lib/puppet/configurer.rb +65 -11
  8. data/lib/puppet/defaults.rb +19 -3
  9. data/lib/puppet/face/generate.rb +2 -0
  10. data/lib/puppet/file_serving/metadata.rb +3 -0
  11. data/lib/puppet/file_system/file_impl.rb +7 -7
  12. data/lib/puppet/file_system/jruby.rb +1 -1
  13. data/lib/puppet/file_system/windows.rb +4 -4
  14. data/lib/puppet/file_system.rb +1 -1
  15. data/lib/puppet/functions/next.rb +18 -1
  16. data/lib/puppet/functions/tree_each.rb +0 -1
  17. data/lib/puppet/functions/versioncmp.rb +6 -2
  18. data/lib/puppet/generate/type.rb +9 -0
  19. data/lib/puppet/http/client.rb +1 -1
  20. data/lib/puppet/http/redirector.rb +5 -0
  21. data/lib/puppet/node.rb +1 -1
  22. data/lib/puppet/parser/resource.rb +1 -1
  23. data/lib/puppet/pops/evaluator/closure.rb +7 -5
  24. data/lib/puppet/pops/evaluator/runtime3_resource_support.rb +1 -0
  25. data/lib/puppet/pops/parser/code_merger.rb +4 -4
  26. data/lib/puppet/pops/parser/egrammar.ra +2 -0
  27. data/lib/puppet/pops/parser/eparser.rb +813 -794
  28. data/lib/puppet/pops/serialization/to_data_converter.rb +6 -18
  29. data/lib/puppet/provider/service/init.rb +5 -4
  30. data/lib/puppet/resource/catalog.rb +1 -1
  31. data/lib/puppet/resource.rb +38 -5
  32. data/lib/puppet/ssl/ssl_provider.rb +10 -7
  33. data/lib/puppet/ssl/verifier.rb +6 -0
  34. data/lib/puppet/transaction/persistence.rb +22 -12
  35. data/lib/puppet/type/exec.rb +1 -1
  36. data/lib/puppet/type/file/data_sync.rb +1 -1
  37. data/lib/puppet/type/user.rb +40 -38
  38. data/lib/puppet/util/json.rb +17 -0
  39. data/lib/puppet/util/log.rb +7 -2
  40. data/lib/puppet/util/monkey_patches.rb +6 -2
  41. data/lib/puppet/util/package.rb +25 -16
  42. data/lib/puppet/util/windows/service.rb +0 -5
  43. data/lib/puppet/util/windows.rb +3 -0
  44. data/lib/puppet/util/yaml.rb +21 -2
  45. data/lib/puppet/util.rb +1 -2
  46. data/lib/puppet/version.rb +1 -1
  47. data/lib/puppet.rb +1 -0
  48. data/locales/puppet.pot +5 -10454
  49. data/man/man5/puppet.conf.5 +21 -2
  50. data/man/man8/puppet-agent.8 +1 -1
  51. data/man/man8/puppet-apply.8 +1 -1
  52. data/man/man8/puppet-catalog.8 +1 -1
  53. data/man/man8/puppet-config.8 +1 -1
  54. data/man/man8/puppet-describe.8 +1 -1
  55. data/man/man8/puppet-device.8 +1 -1
  56. data/man/man8/puppet-doc.8 +1 -1
  57. data/man/man8/puppet-epp.8 +1 -1
  58. data/man/man8/puppet-facts.8 +1 -1
  59. data/man/man8/puppet-filebucket.8 +1 -1
  60. data/man/man8/puppet-generate.8 +1 -1
  61. data/man/man8/puppet-help.8 +1 -1
  62. data/man/man8/puppet-key.8 +1 -1
  63. data/man/man8/puppet-lookup.8 +9 -6
  64. data/man/man8/puppet-man.8 +1 -1
  65. data/man/man8/puppet-module.8 +1 -1
  66. data/man/man8/puppet-node.8 +1 -1
  67. data/man/man8/puppet-parser.8 +1 -1
  68. data/man/man8/puppet-plugin.8 +1 -1
  69. data/man/man8/puppet-report.8 +1 -1
  70. data/man/man8/puppet-resource.8 +1 -1
  71. data/man/man8/puppet-script.8 +1 -1
  72. data/man/man8/puppet-ssl.8 +1 -1
  73. data/man/man8/puppet-status.8 +1 -1
  74. data/man/man8/puppet.8 +2 -2
  75. data/spec/fixtures/integration/application/agent/cached_deferred_catalog.json +2 -1
  76. data/spec/fixtures/unit/forge/bacula.json +1 -1
  77. data/spec/integration/application/agent_spec.rb +28 -0
  78. data/spec/integration/application/lookup_spec.rb +81 -50
  79. data/spec/integration/application/resource_spec.rb +6 -2
  80. data/spec/integration/http/client_spec.rb +30 -0
  81. data/spec/integration/parser/pcore_resource_spec.rb +10 -0
  82. data/spec/shared_contexts/l10n.rb +5 -0
  83. data/spec/unit/application/lookup_spec.rb +131 -10
  84. data/spec/unit/concurrent/thread_local_singleton_spec.rb +39 -0
  85. data/spec/unit/configurer_spec.rb +197 -73
  86. data/spec/unit/face/generate_spec.rb +64 -0
  87. data/spec/unit/file_system_spec.rb +34 -4
  88. data/spec/unit/forge/module_release_spec.rb +3 -3
  89. data/spec/unit/functions/versioncmp_spec.rb +40 -4
  90. data/spec/unit/http/client_spec.rb +58 -1
  91. data/spec/unit/node_spec.rb +6 -0
  92. data/spec/unit/pops/parser/parse_containers_spec.rb +2 -2
  93. data/spec/unit/pops/serialization/to_from_hr_spec.rb +0 -58
  94. data/spec/unit/pops/validator/validator_spec.rb +5 -0
  95. data/spec/unit/provider/service/gentoo_spec.rb +6 -5
  96. data/spec/unit/provider/service/init_spec.rb +15 -9
  97. data/spec/unit/provider/service/openwrt_spec.rb +21 -29
  98. data/spec/unit/provider/service/redhat_spec.rb +3 -2
  99. data/spec/unit/resource/catalog_spec.rb +14 -1
  100. data/spec/unit/resource_spec.rb +58 -2
  101. data/spec/unit/transaction/persistence_spec.rb +51 -0
  102. data/spec/unit/type/user_spec.rb +0 -45
  103. data/spec/unit/util/json_spec.rb +126 -0
  104. data/spec/unit/util/windows_spec.rb +23 -0
  105. data/spec/unit/util/yaml_spec.rb +54 -29
  106. metadata +9 -3
@@ -14,8 +14,6 @@ module Serialization
14
14
  # @option options [Boolean] :local_reference use local references instead of duplicating complex entries
15
15
  # @option options [Boolean] :type_by_reference `true` if Object types are converted to references rather than embedded.
16
16
  # @option options [Boolean] :symbol_as_string `true` if Symbols should be converted to strings (with type loss)
17
- # @option options [Boolean] :force_symbol `false` if Symbols should not be converted (rich_data and symbol_as_string must be false)
18
- # @option options [Boolean] :silence_warnings `false` if warnings should be silenced
19
17
  # @option options [String] :message_prefix String to prepend to in warnings and errors
20
18
  # @return [Data] the processed result. An object assignable to `Data`.
21
19
  #
@@ -43,12 +41,6 @@ module Serialization
43
41
  @symbol_as_string = options[:symbol_as_string]
44
42
  @symbol_as_string = false if @symbol_as_string.nil?
45
43
 
46
- @force_symbol = options[:force_symbol]
47
- @force_symbol = false if @force_symbol.nil?
48
-
49
- @silence_warnings = options[:silence_warnings]
50
- @silence_warnings = false if @silence_warnings.nil?
51
-
52
44
  @rich_data = options[:rich_data]
53
45
  @rich_data = false if @rich_data.nil?
54
46
 
@@ -100,11 +92,7 @@ module Serialization
100
92
  elsif @rich_data
101
93
  { PCORE_TYPE_KEY => PCORE_TYPE_SYMBOL, PCORE_VALUE_KEY => value.to_s }
102
94
  else
103
- if @force_symbol
104
- value
105
- else
106
- @silence_warnings ? unknown_to_string(value) : unknown_to_string_with_warning(value)
107
- end
95
+ unknown_to_string_with_warning(value)
108
96
  end
109
97
  elsif value.instance_of?(Array)
110
98
  process(value) do
@@ -129,11 +117,7 @@ module Serialization
129
117
  { PCORE_TYPE_KEY => PCORE_TYPE_SENSITIVE, PCORE_VALUE_KEY => to_data(value.unwrap) }
130
118
  end
131
119
  else
132
- if @rich_data
133
- value_to_data_hash(value)
134
- else
135
- @silence_warnings ? unknown_to_string(value) : unknown_to_string_with_warning(value)
136
- end
120
+ unknown_to_data(value)
137
121
  end
138
122
  end
139
123
 
@@ -207,6 +191,10 @@ module Serialization
207
191
  v
208
192
  end
209
193
 
194
+ def unknown_to_data(value)
195
+ @rich_data ? value_to_data_hash(value) : unknown_to_string_with_warning(value)
196
+ end
197
+
210
198
  def unknown_key_to_string_with_warning(value)
211
199
  str = unknown_to_string(value)
212
200
  serialization_issue(Issues::SERIALIZATION_UNKNOWN_KEY_CONVERTED_TO_STRING, :path => path_to_s, :klass => value.class, :value => str)
@@ -84,7 +84,7 @@ Puppet::Type.type(:service).provide :init, :parent => :base do
84
84
  defpath = [defpath] unless defpath.is_a? Array
85
85
  instances = []
86
86
  defpath.each do |path|
87
- unless FileTest.directory?(path)
87
+ unless Puppet::FileSystem.directory?(path)
88
88
  Puppet.debug "Service path #{path} does not exist"
89
89
  next
90
90
  end
@@ -97,8 +97,9 @@ Puppet::Type.type(:service).provide :init, :parent => :base do
97
97
  fullpath = File.join(path, name)
98
98
  next if name =~ /^\./
99
99
  next if exclude.include? name
100
- next if not FileTest.executable?(fullpath)
101
- next if not is_init?(fullpath)
100
+ next if Puppet::FileSystem.directory?(fullpath)
101
+ next unless Puppet::FileSystem.executable?(fullpath)
102
+ next unless is_init?(fullpath)
102
103
  instances << new(:name => name, :path => path, :hasstatus => true)
103
104
  end
104
105
  end
@@ -122,7 +123,7 @@ Puppet::Type.type(:service).provide :init, :parent => :base do
122
123
 
123
124
  def paths
124
125
  @paths ||= @resource[:path].find_all do |path|
125
- if File.directory?(path)
126
+ if Puppet::FileSystem.directory?(path)
126
127
  true
127
128
  else
128
129
  if Puppet::FileSystem.exist?(path)
@@ -315,7 +315,7 @@ class Puppet::Resource::Catalog < Puppet::Graph::SimpleGraph
315
315
  super()
316
316
  @name = name
317
317
  @catalog_uuid = SecureRandom.uuid
318
- @catalog_format = 1
318
+ @catalog_format = 2
319
319
  @metadata = {}
320
320
  @recursive_metadata = {}
321
321
  @classes = []
@@ -11,7 +11,7 @@ class Puppet::Resource
11
11
  include Puppet::Util::PsychSupport
12
12
 
13
13
  include Enumerable
14
- attr_accessor :file, :line, :catalog, :exported, :virtual, :strict
14
+ attr_accessor :file, :line, :catalog, :exported, :virtual, :strict, :kind
15
15
  attr_reader :type, :title, :parameters
16
16
 
17
17
  # @!attribute [rw] sensitive_parameters
@@ -29,11 +29,16 @@ class Puppet::Resource
29
29
  EMPTY_ARRAY = [].freeze
30
30
  EMPTY_HASH = {}.freeze
31
31
 
32
- ATTRIBUTES = [:file, :line, :exported].freeze
32
+ ATTRIBUTES = [:file, :line, :exported, :kind].freeze
33
33
  TYPE_CLASS = 'Class'.freeze
34
34
  TYPE_NODE = 'Node'.freeze
35
35
  TYPE_SITE = 'Site'.freeze
36
36
 
37
+ CLASS_STRING = 'class'.freeze
38
+ DEFINED_TYPE_STRING = 'defined_type'.freeze
39
+ COMPILABLE_TYPE_STRING = 'compilable_type'.freeze
40
+ UNKNOWN_TYPE_STRING = 'unknown'.freeze
41
+
37
42
  PCORE_TYPE_KEY = '__ptype'.freeze
38
43
  VALUE_KEY = 'value'.freeze
39
44
 
@@ -194,6 +199,18 @@ class Puppet::Resource
194
199
  resource_type.is_a?(Puppet::CompilableResourceType)
195
200
  end
196
201
 
202
+ def self.to_kind(resource_type)
203
+ if resource_type == CLASS_STRING
204
+ CLASS_STRING
205
+ elsif resource_type.is_a?(Puppet::Resource::Type) && resource_type.type == :definition
206
+ DEFINED_TYPE_STRING
207
+ elsif resource_type.is_a?(Puppet::CompilableResourceType)
208
+ COMPILABLE_TYPE_STRING
209
+ else
210
+ UNKNOWN_TYPE_STRING
211
+ end
212
+ end
213
+
197
214
  # Iterate over each param/value pair, as required for Enumerable.
198
215
  def each
199
216
  parameters.each { |p,v| yield p, v }
@@ -248,6 +265,7 @@ class Puppet::Resource
248
265
  src = type
249
266
  self.file = src.file
250
267
  self.line = src.line
268
+ self.kind = src.kind
251
269
  self.exported = src.exported
252
270
  self.virtual = src.virtual
253
271
  self.set_tags(src)
@@ -310,6 +328,7 @@ class Puppet::Resource
310
328
 
311
329
  rt = resource_type
312
330
 
331
+ self.kind = self.class.to_kind(rt) unless kind
313
332
  if strict? && rt.nil?
314
333
  if self.class?
315
334
  raise ArgumentError, _("Could not find declared class %{title}") % { title: title }
@@ -493,10 +512,24 @@ class Puppet::Resource
493
512
  ref
494
513
  end
495
514
 
496
- # Convert our resource to a RAL resource instance. Creates component
497
- # instances for resource types that don't exist.
515
+ # Convert our resource to a RAL resource instance. Creates component
516
+ # instances for resource types that are not of a compilable_type kind. In case
517
+ # the resource doesn’t exist and it’s compilable_type kind, raise an error.
518
+ # There are certain cases where a resource won't be in a catalog, such as
519
+ # when we create a resource directly by using Puppet::Resource.new(...), so we
520
+ # must check its kind before deciding whether the catalog format is of an older
521
+ # version or not.
498
522
  def to_ral
499
- typeklass = Puppet::Type.type(self.type) || Puppet::Type.type(:component)
523
+ if self.kind == COMPILABLE_TYPE_STRING
524
+ typeklass = Puppet::Type.type(self.type)
525
+ elsif self.catalog && self.catalog.catalog_format >= 2
526
+ typeklass = Puppet::Type.type(:component)
527
+ else
528
+ typeklass = Puppet::Type.type(self.type) || Puppet::Type.type(:component)
529
+ end
530
+
531
+ raise(Puppet::Error, "Resource type '#{self.type}' was not found") unless typeklass
532
+
500
533
  typeklass.new(self)
501
534
  end
502
535
 
@@ -51,8 +51,7 @@ class Puppet::SSL::SSLProvider
51
51
  # @raise (see #create_context)
52
52
  # @api private
53
53
  def create_system_context(cacerts:, path: Puppet[:ssl_trust_store])
54
- store = create_x509_store(cacerts, [], false)
55
- store.set_default_paths
54
+ store = create_x509_store(cacerts, [], false, include_system_store: true)
56
55
 
57
56
  if path
58
57
  stat = Puppet::FileSystem.stat(path)
@@ -94,19 +93,20 @@ class Puppet::SSL::SSLProvider
94
93
  # @param client_cert [OpenSSL::X509::Certificate] client's cert whose public
95
94
  # key matches the `private_key`
96
95
  # @param revocation [:chain, :leaf, false] revocation mode
96
+ # @param include_system_store [true, false] Also trust system CA
97
97
  # @return [Puppet::SSL::SSLContext] A context to use to create connections
98
98
  # @raise [Puppet::SSL::CertVerifyError] There was an issue with
99
99
  # one of the certs or CRLs.
100
100
  # @raise [Puppet::SSL::SSLError] There was an issue with the
101
101
  # `private_key`.
102
102
  # @api private
103
- def create_context(cacerts:, crls:, private_key:, client_cert:, revocation: Puppet[:certificate_revocation])
103
+ def create_context(cacerts:, crls:, private_key:, client_cert:, revocation: Puppet[:certificate_revocation], include_system_store: false)
104
104
  raise ArgumentError, _("CA certs are missing") unless cacerts
105
105
  raise ArgumentError, _("CRLs are missing") unless crls
106
106
  raise ArgumentError, _("Private key is missing") unless private_key
107
107
  raise ArgumentError, _("Client cert is missing") unless client_cert
108
108
 
109
- store = create_x509_store(cacerts, crls, revocation)
109
+ store = create_x509_store(cacerts, crls, revocation, include_system_store: include_system_store)
110
110
  client_chain = verify_cert_with_store(store, client_cert)
111
111
 
112
112
  if !private_key.is_a?(OpenSSL::PKey::RSA) && !private_key.is_a?(OpenSSL::PKey::EC)
@@ -134,12 +134,13 @@ class Puppet::SSL::SSLProvider
134
134
  # @param password [String, nil] If the private key is encrypted, decrypt
135
135
  # it using the password. If the key is encrypted, but a password is
136
136
  # not specified, then the key cannot be loaded.
137
+ # @param include_system_store [true, false] Also trust system CA
137
138
  # @return [Puppet::SSL::SSLContext] A context to use to create connections
138
139
  # @raise [Puppet::SSL::CertVerifyError] There was an issue with
139
140
  # one of the certs or CRLs.
140
141
  # @raise [Puppet::Error] There was an issue with one of the required components.
141
142
  # @api private
142
- def load_context(certname: Puppet[:certname], revocation: Puppet[:certificate_revocation], password: nil)
143
+ def load_context(certname: Puppet[:certname], revocation: Puppet[:certificate_revocation], password: nil, include_system_store: false)
143
144
  cert = Puppet::X509::CertProvider.new
144
145
  cacerts = cert.load_cacerts(required: true)
145
146
  crls = case revocation
@@ -151,7 +152,7 @@ class Puppet::SSL::SSLProvider
151
152
  private_key = cert.load_private_key(certname, required: true, password: password)
152
153
  client_cert = cert.load_client_cert(certname, required: true)
153
154
 
154
- create_context(cacerts: cacerts, crls: crls, private_key: private_key, client_cert: client_cert, revocation: revocation)
155
+ create_context(cacerts: cacerts, crls: crls, private_key: private_key, client_cert: client_cert, revocation: revocation, include_system_store: include_system_store)
155
156
  rescue OpenSSL::PKey::PKeyError => e
156
157
  raise Puppet::SSL::SSLError.new(_("Failed to load private key for host '%{name}': %{message}") % { name: certname, message: e.message }, e)
157
158
  end
@@ -186,7 +187,7 @@ class Puppet::SSL::SSLProvider
186
187
  end
187
188
  end
188
189
 
189
- def create_x509_store(roots, crls, revocation)
190
+ def create_x509_store(roots, crls, revocation, include_system_store: false)
190
191
  store = OpenSSL::X509::Store.new
191
192
  store.purpose = OpenSSL::X509::PURPOSE_ANY
192
193
  store.flags = default_flags | revocation_mode(revocation)
@@ -194,6 +195,8 @@ class Puppet::SSL::SSLProvider
194
195
  roots.each { |cert| store.add_cert(cert) }
195
196
  crls.each { |crl| store.add_crl(crl) }
196
197
 
198
+ store.set_default_paths if include_system_store
199
+
197
200
  store
198
201
  end
199
202
 
@@ -115,6 +115,12 @@ class Puppet::SSL::Verifier
115
115
  return false
116
116
  end
117
117
 
118
+ # ruby-openssl#74ef8c0cc56b840b772240f2ee2b0fc0aafa2743 now sets the
119
+ # store_context error when the cert is mismatched
120
+ when OpenSSL::X509::V_ERR_HOSTNAME_MISMATCH
121
+ @last_error = Puppet::SSL::CertMismatchError.new(peer_cert, @hostname)
122
+ return false
123
+
118
124
  when OpenSSL::X509::V_ERR_CRL_NOT_YET_VALID
119
125
  crl = store_context.current_crl
120
126
  if crl && crl.last_update && crl.last_update < Time.now + FIVE_MINUTES_AS_SECONDS
@@ -6,6 +6,26 @@ require 'puppet/util/yaml'
6
6
  # as calculating corrective_change).
7
7
  # @api private
8
8
  class Puppet::Transaction::Persistence
9
+
10
+ def self.allowed_classes
11
+ @allowed_classes ||= [
12
+ Symbol,
13
+ Time,
14
+ Regexp,
15
+ # URI is excluded, because it serializes all instance variables including the
16
+ # URI parser. Better to serialize the URL encoded representation.
17
+ SemanticPuppet::Version,
18
+ # SemanticPuppet::VersionRange has many nested classes and is unlikely to be
19
+ # used directly, so ignore it
20
+ Puppet::Pops::Time::Timestamp,
21
+ Puppet::Pops::Time::TimeData,
22
+ Puppet::Pops::Time::Timespan,
23
+ Puppet::Pops::Types::PBinaryType::Binary,
24
+ # Puppet::Pops::Types::PSensitiveType::Sensitive values are excluded from
25
+ # the persistence store, ignore it.
26
+ ].freeze
27
+ end
28
+
9
29
  def initialize
10
30
  @old_data = {}
11
31
  @new_data = {"resources" => {}}
@@ -62,7 +82,7 @@ class Puppet::Transaction::Persistence
62
82
  result = nil
63
83
  Puppet::Util.benchmark(:debug, _("Loaded transaction store file in %{seconds} seconds")) do
64
84
  begin
65
- result = Puppet::Util::Yaml.safe_load_file(filename, [Symbol, Time])
85
+ result = Puppet::Util::Yaml.safe_load_file(filename, self.class.allowed_classes)
66
86
  rescue Puppet::Util::Yaml::YamlLoadError => detail
67
87
  Puppet.log_exception(detail, _("Transaction store file %{filename} is corrupt (%{detail}); replacing") % { filename: filename, detail: detail })
68
88
 
@@ -87,17 +107,7 @@ class Puppet::Transaction::Persistence
87
107
 
88
108
  # Save data from internal class to persistence store on disk.
89
109
  def save
90
- converted_data = Puppet::Pops::Serialization::ToDataConverter.convert(
91
- @new_data, {
92
- symbol_as_string: false,
93
- local_reference: false,
94
- type_by_reference: true,
95
- force_symbol: true,
96
- silence_warnings: true,
97
- message_prefix: to_s
98
- }
99
- )
100
- Puppet::Util::Yaml.dump(converted_data, Puppet[:transactionstorefile])
110
+ Puppet::Util::Yaml.dump(@new_data, Puppet[:transactionstorefile])
101
111
  end
102
112
 
103
113
  # Use the catalog and run_mode to determine if persistence should be enabled or not
@@ -457,7 +457,7 @@ module Puppet
457
457
 
458
458
  exec { '/bin/echo root >> /usr/lib/cron/cron.allow':
459
459
  path => '/usr/bin:/usr/sbin:/bin',
460
- unless => 'grep root /usr/lib/cron/cron.allow 2>/dev/null',
460
+ unless => 'grep ^root$ /usr/lib/cron/cron.allow 2>/dev/null',
461
461
  }
462
462
 
463
463
  This would add `root` to the cron.allow file (on Solaris) unless
@@ -79,7 +79,7 @@ module Puppet
79
79
  return :absent unless stat
80
80
  ftype = stat.ftype
81
81
  # Don't even try to manage the content on directories or links
82
- return nil if ["directory","link"].include?(ftype)
82
+ return nil if ['directory', 'link', 'fifo', 'socket'].include?(ftype)
83
83
 
84
84
  begin
85
85
  resource.parameter(:checksum).sum_file(resource[:path])
@@ -66,7 +66,6 @@ module Puppet
66
66
  newproperty(:ensure, :parent => Puppet::Property::Ensure) do
67
67
  newvalue(:present, :event => :user_created) do
68
68
  provider.create
69
- @resource.generate
70
69
  end
71
70
 
72
71
  newvalue(:absent, :event => :user_removed) do
@@ -695,7 +694,6 @@ module Puppet
695
694
 
696
695
  def generate
697
696
  if !self[:purge_ssh_keys].empty?
698
- return [] if self[:ensure] == :present && !provider.exists?
699
697
  if Puppet::Type.type(:ssh_authorized_key).nil?
700
698
  warning _("Ssh_authorized_key type is not available. Cannot purge SSH keys.")
701
699
  else
@@ -744,6 +742,45 @@ module Puppet
744
742
  end
745
743
  raise ArgumentError, _("purge_ssh_keys must be true, false, or an array of file names, not %{value}") % { value: value.inspect }
746
744
  end
745
+
746
+ munge do |value|
747
+ # Resolve string, boolean and symbol forms of true and false to a
748
+ # single representation.
749
+ case value
750
+ when :false, false, "false"
751
+ []
752
+ when :true, true, "true"
753
+ home = homedir
754
+ home ? [ "#{home}/.ssh/authorized_keys" ] : []
755
+ else
756
+ # value can be a string or array - munge each value
757
+ [ value ].flatten.map do |entry|
758
+ authorized_keys_path(entry)
759
+ end.compact
760
+ end
761
+ end
762
+
763
+ private
764
+
765
+ def homedir
766
+ resource[:home] || Dir.home(resource[:name])
767
+ rescue ArgumentError
768
+ Puppet.debug("User '#{resource[:name]}' does not exist")
769
+ nil
770
+ end
771
+
772
+ def authorized_keys_path(entry)
773
+ return entry unless entry.match?(%r{^(?:~|%h)/})
774
+
775
+ # if user doesn't exist (yet), ignore nonexistent homedir
776
+ home = homedir
777
+ return nil unless home
778
+
779
+ # compiler freezes "value" so duplicate using a gsub, second mutating gsub! is then ok
780
+ entry = entry.gsub(%r{^~/}, "#{home}/")
781
+ entry.gsub!(%r{^%h/}, "#{home}/")
782
+ entry
783
+ end
747
784
  end
748
785
 
749
786
  newproperty(:loginclass, :required_features => :manages_loginclass) do
@@ -765,7 +802,7 @@ module Puppet
765
802
  # @see generate
766
803
  # @api private
767
804
  def find_unmanaged_keys
768
- munged_unmanaged_keys.
805
+ self[:purge_ssh_keys].
769
806
  select { |f| File.readable?(f) }.
770
807
  map { |f| unknown_keys_in_file(f) }.
771
808
  flatten.each do |res|
@@ -777,41 +814,6 @@ module Puppet
777
814
  end
778
815
  end
779
816
 
780
- def munged_unmanaged_keys
781
- value = self[:purge_ssh_keys]
782
-
783
- # Resolve string, boolean and symbol forms of true and false to a
784
- # single representation.
785
- test_sym = value.to_s.intern
786
- value = test_sym if [:true, :false].include? test_sym
787
-
788
- return [] if value == :false
789
-
790
- home = self[:home]
791
- begin
792
- home ||= provider.home
793
- rescue
794
- Puppet.debug("User '#{self[:name]}' does not exist")
795
- end
796
-
797
- if home.to_s.empty? || !Dir.exist?(home.to_s)
798
- if value == :true || [ value ].flatten.any? { |v| v.start_with?('~/', '%h/') }
799
- Puppet.debug("User '#{self[:name]}' has no home directory set to purge ssh keys from.")
800
- return []
801
- end
802
- end
803
-
804
- return [ "#{home}/.ssh/authorized_keys" ] if value == :true
805
-
806
- # value is an array - munge each value
807
- [ value ].flatten.map do |entry|
808
- # make sure frozen value is duplicated by using a gsub, second mutating gsub! is then ok
809
- entry = entry.gsub(/^~\//, "#{home}/")
810
- entry.gsub!(/^%h\//, "#{home}/")
811
- entry
812
- end
813
- end
814
-
815
817
  # Parse an ssh authorized keys file superficially, extract the comments
816
818
  # on the keys. These are considered names of possible ssh_authorized_keys
817
819
  # resources. Keys that are managed by the present catalog are ignored.
@@ -26,6 +26,23 @@ module Puppet::Util
26
26
  require 'json'
27
27
  end
28
28
 
29
+ # Load the content from a file as JSON if
30
+ # contents are in valid format. This method does not
31
+ # raise error but returns `nil` when invalid file is
32
+ # given.
33
+ def self.load_file_if_valid(filename, options = {})
34
+ load_file(filename, options)
35
+ rescue Puppet::Util::Json::ParseError, ArgumentError, Errno::ENOENT => detail
36
+ Puppet.debug("Could not retrieve JSON content from '#{filename}': #{detail.message}")
37
+ nil
38
+ end
39
+
40
+ # Load the content from a file as JSON.
41
+ def self.load_file(filename, options = {})
42
+ json = Puppet::FileSystem.read(filename, :encoding => 'utf-8')
43
+ load(json, options)
44
+ end
45
+
29
46
  # These methods do similar processing to the fallback implemented by MultiJson
30
47
  # when using the built-in JSON backend, to ensure consistent behavior
31
48
  # whether or not MultiJson can be loaded.
@@ -105,9 +105,14 @@ class Puppet::Util::Log
105
105
  def Log.level=(level)
106
106
  level = level.intern unless level.is_a?(Symbol)
107
107
 
108
- raise Puppet::DevError, _("Invalid loglevel %{level}") % { level: level } unless @levels.include?(level)
108
+ # loglevel is a 0-based index
109
+ loglevel = @levels.index(level)
110
+ raise Puppet::DevError, _("Invalid loglevel %{level}") % { level: level } unless loglevel
109
111
 
110
- @loglevel = @levels.index(level)
112
+ return if @loglevel == loglevel
113
+
114
+ # loglevel changed
115
+ @loglevel = loglevel
111
116
 
112
117
  # Enable or disable Facter debugging
113
118
  Puppet.runtime[:facter].debugging(level == :debug)
@@ -39,6 +39,12 @@ unless Puppet::Util::Platform.jruby_fips?
39
39
  end
40
40
  end
41
41
 
42
+ unless defined?(OpenSSL::X509::V_ERR_HOSTNAME_MISMATCH)
43
+ module OpenSSL::X509
44
+ OpenSSL::X509::V_ERR_HOSTNAME_MISMATCH = 0x3E
45
+ end
46
+ end
47
+
42
48
  class OpenSSL::SSL::SSLContext
43
49
  if DEFAULT_PARAMS[:options]
44
50
  DEFAULT_PARAMS[:options] |= OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3
@@ -64,8 +70,6 @@ unless Puppet::Util::Platform.jruby_fips?
64
70
  end
65
71
 
66
72
  if Puppet::Util::Platform.windows?
67
- require 'puppet/util/windows'
68
-
69
73
  class OpenSSL::X509::Store
70
74
  @puppet_certs_loaded = false
71
75
  alias __original_set_default_paths set_default_paths
@@ -1,6 +1,13 @@
1
+ # frozen_string_literal: true
1
2
  module Puppet::Util::Package
2
- def versioncmp(version_a, version_b)
3
+ def versioncmp(version_a, version_b, ignore_trailing_zeroes = false)
3
4
  vre = /[-.]|\d+|[^-.\d]+/
5
+
6
+ if ignore_trailing_zeroes
7
+ version_a = normalize(version_a)
8
+ version_b = normalize(version_b)
9
+ end
10
+
4
11
  ax = version_a.scan(vre)
5
12
  bx = version_b.scan(vre)
6
13
 
@@ -8,24 +15,26 @@ module Puppet::Util::Package
8
15
  a = ax.shift
9
16
  b = bx.shift
10
17
 
11
- if( a == b ) then next
12
- elsif (a == '-' && b == '-') then next
13
- elsif (a == '-') then return -1
14
- elsif (b == '-') then return 1
15
- elsif (a == '.' && b == '.') then next
16
- elsif (a == '.' ) then return -1
17
- elsif (b == '.' ) then return 1
18
- elsif (a =~ /^\d+$/ && b =~ /^\d+$/) then
19
- if( a =~ /^0/ or b =~ /^0/ ) then
20
- return a.to_s.upcase <=> b.to_s.upcase
21
- end
18
+ next if a == b
19
+ return -1 if a == '-'
20
+ return 1 if b == '-'
21
+ return -1 if a == '.'
22
+ return 1 if b == '.'
23
+ if a =~ /^\d+$/ && b =~ /^\d+$/
24
+ return a.to_s.upcase <=> b.to_s.upcase if a =~ /^0/ || b =~ /^0/
22
25
  return a.to_i <=> b.to_i
23
- else
24
- return a.upcase <=> b.upcase
25
26
  end
27
+ return a.upcase <=> b.upcase
26
28
  end
27
- version_a <=> version_b;
29
+ version_a <=> version_b
28
30
  end
29
-
30
31
  module_function :versioncmp
32
+
33
+ def self.normalize(version)
34
+ version = version.split('-')
35
+ version.first.sub!(/([\.0]+)$/, '')
36
+
37
+ version.join('-')
38
+ end
39
+ private_class_method :normalize
31
40
  end
@@ -13,11 +13,6 @@ module Puppet::Util::Windows
13
13
 
14
14
  FILE = Puppet::Util::Windows::File
15
15
 
16
- # integer value of the floor for timeouts when waiting for service pending states.
17
- # puppet will wait the length of dwWaitHint if it is longer than this value, but
18
- # no shorter
19
- DEFAULT_TIMEOUT = 30
20
-
21
16
  # Service error codes
22
17
  # https://docs.microsoft.com/en-us/windows/desktop/debug/system-error-codes--1000-1299-
23
18
  ERROR_SERVICE_DOES_NOT_EXIST = 0x00000424
@@ -9,6 +9,9 @@ module Puppet::Util::Windows
9
9
  module File; end
10
10
  module Registry
11
11
  end
12
+ module Service
13
+ DEFAULT_TIMEOUT = 30
14
+ end
12
15
  module SID
13
16
  class Principal; end
14
17
  end
@@ -24,7 +24,11 @@ module Puppet::Util::Yaml
24
24
  # @raise [YamlLoadException] If deserialization fails.
25
25
  # @return The parsed YAML, which can be Hash, Array or scalar types.
26
26
  def self.safe_load(yaml, allowed_classes = [], filename = nil)
27
- data = YAML.safe_load(yaml, allowed_classes, [], true, filename)
27
+ if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0')
28
+ data = YAML.safe_load(yaml, permitted_classes: allowed_classes, aliases: true, filename: filename)
29
+ else
30
+ data = YAML.safe_load(yaml, allowed_classes, [], true, filename)
31
+ end
28
32
  data = false if data.nil?
29
33
  data
30
34
  rescue ::Psych::DisallowedClass => detail
@@ -42,6 +46,17 @@ module Puppet::Util::Yaml
42
46
  safe_load(yaml, allowed_classes, filename)
43
47
  end
44
48
 
49
+ # Safely load the content from a file as YAML if
50
+ # contents are in valid format. This method does not
51
+ # raise error but returns `nil` when invalid file is
52
+ # given.
53
+ def self.safe_load_file_if_valid(filename, allowed_classes = [])
54
+ safe_load_file(filename, allowed_classes)
55
+ rescue YamlLoadError, ArgumentError, Errno::ENOENT => detail
56
+ Puppet.debug("Could not retrieve YAML content from '#{filename}': #{detail.message}")
57
+ nil
58
+ end
59
+
45
60
  # @deprecated Use {#safe_load_file} instead.
46
61
  def self.load_file(filename, default_value = false, strip_classes = false)
47
62
  Puppet.deprecation_warning(_("Puppet::Util::Yaml.load_file is deprecated. Use safe_load_file instead."))
@@ -57,7 +72,11 @@ module Puppet::Util::Yaml
57
72
  end
58
73
  data.to_ruby || default_value
59
74
  else
60
- yaml = YAML.load_file(filename)
75
+ if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.3.2')
76
+ yaml = YAML.unsafe_load_file(filename)
77
+ else
78
+ yaml = YAML.load_file(filename)
79
+ end
61
80
  yaml || default_value
62
81
  end
63
82
  rescue *YamlLoadExceptions => detail
data/lib/puppet/util.rb CHANGED
@@ -7,6 +7,7 @@ require 'uri'
7
7
  require 'pathname'
8
8
  require 'ostruct'
9
9
  require 'puppet/util/platform'
10
+ require 'puppet/util/windows'
10
11
  require 'puppet/util/symbolic_file_mode'
11
12
  require 'puppet/file_system/uniquefile'
12
13
  require 'securerandom'
@@ -22,8 +23,6 @@ module Util
22
23
  require 'puppet/util/posix'
23
24
  extend Puppet::Util::POSIX
24
25
 
25
- require 'puppet/util/windows/process' if Puppet::Util::Platform.windows?
26
-
27
26
  extend Puppet::Util::SymbolicFileMode
28
27
 
29
28
  def default_env