mixlib-authentication 2.0.0 → 3.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 30042f8e3ac0f6ca2d793fa73556e19d41146554c6673788e2c0d7225e9f3584
4
- data.tar.gz: bed096ad15f32dca36dddfe79deb4f7b9f54a8656849b68393ce900944f3cc5b
3
+ metadata.gz: ffd7c02d4d9d20fdf32da899c6409fc59caffea1183d4c0806b0c02a20054538
4
+ data.tar.gz: 5f30cd2c44cc6b8375d9a950f596e2b89031654d315a822088baca00c1300b49
5
5
  SHA512:
6
- metadata.gz: f2bc4eff96e238290c9d3ec9cfc490b62372b9f4b0668dae56055f88844c8b8b971183000e4189d5ecfd8a4cc8b902aee753f103fecb839ad1bf9b33716d1840
7
- data.tar.gz: 0a03fd4b0a2016b93b681ba5a174dcc49089bd2aab2c2601bad114970a0680fe091bd562a6de4a0918f24e52b3eb512159287a239fb0cf1654ca91c452e34a4f
6
+ metadata.gz: 288e2b5de5735c93ebfe885ec24ae267dc056bbdff205b8ff0a4ad94f7691598965db5051ffd853811574ada1aeb10775685b4a4a864a870aa08377def77a801
7
+ data.tar.gz: 1c49b5589da86a7fe73d0d0c9914a148f0c95a0ff4762a711224bfc7d1a47fb20f98c7a9c6ad2a3a0d45bb6c42a90e295f7d6b98a5a8af80a99c96aafe9ecdf5
@@ -1,6 +1,6 @@
1
1
  #
2
- # Author:: Christopher Brown (<cb@opscode.com>)
3
- # Copyright:: Copyright (c) 2009 Opscode, Inc.
2
+ # Author:: Christopher Brown (<cb@chef.io>)
3
+ # Copyright:: Copyright (c) 2009-2018 Chef Software, Inc.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,7 +18,7 @@
18
18
 
19
19
  module Mixlib
20
20
  module Authentication
21
- DEFAULT_SERVER_API_VERSION = "0"
21
+ DEFAULT_SERVER_API_VERSION = "0".freeze
22
22
 
23
23
  attr_accessor :logger
24
24
  module_function :logger, :logger=
@@ -36,7 +36,7 @@ module Mixlib
36
36
  require "mixlib/log"
37
37
  Mixlib::Authentication::Log.extend(Mixlib::Log)
38
38
  rescue LoadError
39
- require "mixlib/authentication/null_logger"
39
+ require_relative "authentication/null_logger"
40
40
  Mixlib::Authentication::Log.extend(Mixlib::Authentication::NullLogger)
41
41
  end
42
42
 
@@ -1,6 +1,6 @@
1
1
  #
2
- # Author:: Christopher Brown (<cb@opscode.com>)
3
- # Copyright:: Copyright (c) 2009 Opscode, Inc.
2
+ # Author:: Christopher Brown (<cb@chef.io>)
3
+ # Copyright:: Copyright (c) 2009-2018 Chef Software, Inc.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,7 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- require "mixlib/authentication"
19
+ require_relative "../authentication"
20
20
  require "openssl"
21
21
 
22
22
  module Mixlib
@@ -1,6 +1,6 @@
1
1
  #
2
- # Author:: Daniel DeLeo (<dan@opscode.com>)
3
- # Copyright:: Copyright (c) 2010 Opscode, Inc.
2
+ # Author:: Daniel DeLeo (<dan@chef.io>)
3
+ # Copyright:: Copyright (c) 2010-2018 Chef Software, Inc.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +16,13 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- require "mixlib/authentication"
19
+ require_relative "../authentication"
20
20
 
21
21
  module Mixlib
22
22
  module Authentication
23
23
  class HTTPAuthenticationRequest
24
24
 
25
- MANDATORY_HEADERS = [:x_ops_sign, :x_ops_userid, :x_ops_timestamp, :host, :x_ops_content_hash]
25
+ MANDATORY_HEADERS = %i{x_ops_sign x_ops_userid x_ops_timestamp host x_ops_content_hash}.freeze
26
26
 
27
27
  attr_reader :request
28
28
 
@@ -1,7 +1,7 @@
1
1
  #
2
- # Author:: Christopher Brown (<cb@opscode.com>)
3
- # Author:: Christopher Walters (<cw@opscode.com>)
4
- # Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
2
+ # Author:: Christopher Brown (<cb@chef.io>)
3
+ # Author:: Christopher Walters (<cw@chef.io>)
4
+ # Copyright:: Copyright (c) 2009-2018 Chef Software, Inc.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,9 +19,9 @@
19
19
 
20
20
  require "net/http"
21
21
  require "forwardable"
22
- require "mixlib/authentication"
23
- require "mixlib/authentication/http_authentication_request"
24
- require "mixlib/authentication/signedheaderauth"
22
+ require_relative "../authentication"
23
+ require_relative "http_authentication_request"
24
+ require_relative "signedheaderauth"
25
25
 
26
26
  module Mixlib
27
27
  module Authentication
@@ -203,7 +203,7 @@ module Mixlib
203
203
  # No file_param; we're running in Merb, or it's just not there..
204
204
  if file_param.nil?
205
205
  hash_param = request.params.values.find { |value| value.respond_to?(:has_key?) } # Hash responds to :has_key? .
206
- if !hash_param.nil?
206
+ unless hash_param.nil?
207
207
  file_param = hash_param.values.find { |value| value.respond_to?(:read) } # File/Tempfile responds to :read.
208
208
  end
209
209
  end
@@ -1,7 +1,7 @@
1
1
  #
2
- # Author:: Christopher Brown (<cb@opscode.com>)
3
- # Author:: Christopher Walters (<cw@opscode.com>)
4
- # Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
2
+ # Author:: Christopher Brown (<cb@chef.io>)
3
+ # Author:: Christopher Walters (<cw@chef.io>)
4
+ # Copyright:: Copyright (c) 2009-2018 Chef Software, Inc.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,8 +20,8 @@
20
20
  require "time"
21
21
  require "base64"
22
22
  require "openssl/digest"
23
- require "mixlib/authentication"
24
- require "mixlib/authentication/digester"
23
+ require_relative "../authentication"
24
+ require_relative "digester"
25
25
 
26
26
  module Mixlib
27
27
  module Authentication
@@ -34,7 +34,7 @@ module Mixlib
34
34
  "1.0" => "sha1",
35
35
  "1.1" => "sha1",
36
36
  "1.3" => "sha256",
37
- }.freeze()
37
+ }.freeze
38
38
 
39
39
  # Use of SUPPORTED_ALGORITHMS and SUPPORTED_VERSIONS is deprecated. Use
40
40
  # ALGORITHM_FOR_VERSION instead
@@ -74,15 +74,14 @@ module Mixlib
74
74
  # * `:host`: The host part of the URI
75
75
  def self.signing_object(args = {})
76
76
  SigningObject.new(args[:http_method],
77
- args[:path],
78
- args[:body],
79
- args[:host],
80
- args[:timestamp],
81
- args[:user_id],
82
- args[:file],
83
- args[:proto_version],
84
- args[:headers]
85
- )
77
+ args[:path],
78
+ args[:body],
79
+ args[:host],
80
+ args[:timestamp],
81
+ args[:user_id],
82
+ args[:file],
83
+ args[:proto_version],
84
+ args[:headers])
86
85
  end
87
86
 
88
87
  def algorithm
@@ -95,9 +94,28 @@ module Mixlib
95
94
 
96
95
  # Build the canonicalized request based on the method, other headers, etc.
97
96
  # compute the signature from the request, using the looked-up user secret
98
- # ====Parameters
99
- # private_key<OpenSSL::PKey::RSA>:: user's RSA private key.
100
- def sign(private_key, sign_algorithm = algorithm, sign_version = proto_version)
97
+ #
98
+ # @param rsa_key [OpenSSL::PKey::RSA] User's RSA key. If `use_ssh_agent` is
99
+ # true, this must have the public key portion populated. If `use_ssh_agent`
100
+ # is false, this must have the private key portion populated.
101
+ # @param use_ssh_agent [Boolean] If true, use ssh-agent for request signing.
102
+ def sign(rsa_key, sign_algorithm = algorithm, sign_version = proto_version, **opts)
103
+ # Backwards compat stuff.
104
+ if sign_algorithm.is_a?(Hash)
105
+ # Was called like sign(key, sign_algorithm: 'foo', other: 'bar')
106
+ opts.update(sign_algorithm)
107
+ opts[:sign_algorithm] ||= algorithm
108
+ opts[:sign_version] ||= sign_version
109
+ else
110
+ # Was called like sign(key, 'foo', '1.3', other: 'bar')
111
+ Mixlib::Authentication.logger.warn("Using deprecated positional arguments for sign(), please update to keyword arguments (from #{caller[1][/^(.*:\d+):in /, 1]})") unless sign_algorithm == algorithm
112
+ opts[:sign_algorithm] ||= sign_algorithm
113
+ opts[:sign_version] ||= sign_version
114
+ end
115
+ sign_algorithm = opts[:sign_algorithm]
116
+ sign_version = opts[:sign_version]
117
+ use_ssh_agent = opts[:use_ssh_agent]
118
+
101
119
  digest = validate_sign_version_digest!(sign_algorithm, sign_version)
102
120
  # Our multiline hash for authorization will be encoded in multiple header
103
121
  # lines - X-Ops-Authorization-1, ... (starts at 1, not 0!)
@@ -108,7 +126,7 @@ module Mixlib
108
126
  "X-Ops-Content-Hash" => hashed_body(digest),
109
127
  }
110
128
 
111
- signature = Base64.encode64(do_sign(private_key, digest, sign_algorithm, sign_version)).chomp
129
+ signature = Base64.encode64(do_sign(rsa_key, digest, sign_algorithm, sign_version, use_ssh_agent)).chomp
112
130
  signature_lines = signature.split(/\n/)
113
131
  signature_lines.each_index do |idx|
114
132
  key = "X-Ops-Authorization-#{idx + 1}"
@@ -156,7 +174,7 @@ module Mixlib
156
174
  # ====Parameters
157
175
  #
158
176
  def canonical_path
159
- p = path.gsub(/\/+/, "/")
177
+ p = path.gsub(%r{/+}, "/")
160
178
  p.length > 1 ? p.chomp("/") : p
161
179
  end
162
180
 
@@ -172,6 +190,7 @@ module Mixlib
172
190
  else
173
191
  @hashed_body_digest = digest
174
192
  end
193
+
175
194
  # Hash the file object if it was passed in, otherwise hash based on
176
195
  # the body.
177
196
  # TODO: tim 2009-12-28: It'd be nice to just remove this special case,
@@ -244,18 +263,75 @@ module Mixlib
244
263
  Mixlib::Authentication::Digester
245
264
  end
246
265
 
247
- # private
248
- def do_sign(private_key, digest, sign_algorithm, sign_version)
266
+ # Low-level RSA signature implementation used in {#sign}.
267
+ #
268
+ # @api private
269
+ # @param rsa_key [OpenSSL::PKey::RSA] User's RSA key. If `use_ssh_agent` is
270
+ # true, this must have the public key portion populated. If `use_ssh_agent`
271
+ # is false, this must have the private key portion populated.
272
+ # @param digest [Class] Sublcass of OpenSSL::Digest to use while signing.
273
+ # @param sign_algorithm [String] Hash algorithm to use while signing.
274
+ # @param sign_version [String] Version number of the signing protocol to use.
275
+ # @param use_ssh_agent [Boolean] If true, use ssh-agent for request signing.
276
+ # @return [String]
277
+ def do_sign(rsa_key, digest, sign_algorithm, sign_version, use_ssh_agent)
249
278
  string_to_sign = canonicalize_request(sign_algorithm, sign_version)
250
279
  Mixlib::Authentication.logger.trace "String to sign: '#{string_to_sign}'"
251
280
  case sign_version
252
281
  when "1.3"
253
- private_key.sign(digest.new, string_to_sign)
282
+ if use_ssh_agent
283
+ do_sign_ssh_agent(rsa_key, string_to_sign)
284
+ else
285
+ raise AuthenticationError, "RSA private key is required to sign requests, but a public key was provided" unless rsa_key.private?
286
+
287
+ rsa_key.sign(digest.new, string_to_sign)
288
+ end
254
289
  else
255
- private_key.private_encrypt(string_to_sign)
290
+ raise AuthenticationError, "Agent signing mode requires signing protocol version 1.3 or newer" if use_ssh_agent
291
+ raise AuthenticationError, "RSA private key is required to sign requests, but a public key was provided" unless rsa_key.private?
292
+
293
+ rsa_key.private_encrypt(string_to_sign)
256
294
  end
257
295
  end
258
296
 
297
+ # Low-level signing logic for using ssh-agent. This requires the user has
298
+ # already set up ssh-agent and used ssh-add to load in a (possibly encrypted)
299
+ # RSA private key. ssh-agent supports keys other than RSA, however they
300
+ # are not supported as Chef's protocol explicitly requires RSA keys/sigs.
301
+ #
302
+ # @api private
303
+ # @param rsa_key [OpenSSL::PKey::RSA] User's RSA public key.
304
+ # @param string_to_sign [String] String data to sign with the requested key.
305
+ # @return [String]
306
+ def do_sign_ssh_agent(rsa_key, string_to_sign)
307
+ # First try loading net-ssh as it is an optional dependency.
308
+ begin
309
+ require "net/ssh"
310
+ rescue LoadError => e
311
+ # ???: Since agent mode is explicitly enabled, should we even catch
312
+ # this in the first place? Might be cleaner to let the LoadError bubble.
313
+ raise AuthenticationError, "net-ssh gem is not available, unable to use ssh-agent signing: #{e.message}"
314
+ end
315
+
316
+ # Try to connect to ssh-agent.
317
+ begin
318
+ agent = Net::SSH::Authentication::Agent.connect
319
+ rescue Net::SSH::Authentication::AgentNotAvailable => e
320
+ raise AuthenticationError, "Could not connect to ssh-agent. Make sure the SSH_AUTH_SOCK environment variable is set and ssh-agent is running: #{e.message}"
321
+ end
322
+
323
+ begin
324
+ ssh2_signature = agent.sign(rsa_key.public_key, string_to_sign, Net::SSH::Authentication::Agent::SSH_AGENT_RSA_SHA2_256)
325
+ rescue Net::SSH::Authentication::AgentError => e
326
+ raise AuthenticationError, "Unable to sign request with ssh-agent. Make sure your key is loaded with ssh-add: #{e.class.name} #{e.message})"
327
+ end
328
+
329
+ # extract signature from SSH Agent response => skip first 20 bytes for RSA keys
330
+ # "\x00\x00\x00\frsa-sha2-256\x00\x00\x01\x00"
331
+ # (see http://api.libssh.org/rfc/PROTOCOL.agent for details)
332
+ ssh2_signature[20..-1]
333
+ end
334
+
259
335
  private :canonical_time, :canonical_path, :parse_signing_description, :digester, :canonicalize_user_id
260
336
 
261
337
  end
@@ -265,25 +341,25 @@ module Mixlib
265
341
  # generate a request signature. `SignedHeaderAuth.signing_object()`
266
342
  # provides a more convenient interface to the constructor.
267
343
  SigningObject = Struct.new(:http_method, :path, :body, :host,
268
- :timestamp, :user_id, :file, :proto_version,
269
- :headers) do
344
+ :timestamp, :user_id, :file, :proto_version,
345
+ :headers) do
270
346
 
271
- include SignedHeaderAuth
347
+ include SignedHeaderAuth
272
348
 
273
- def proto_version
274
- (self[:proto_version] || SignedHeaderAuth::DEFAULT_PROTO_VERSION).to_s
275
- end
349
+ def proto_version
350
+ (self[:proto_version] || SignedHeaderAuth::DEFAULT_PROTO_VERSION).to_s
351
+ end
276
352
 
277
- def server_api_version
278
- key = (self[:headers] || {}).keys.select do |k|
279
- k.casecmp("x-ops-server-api-version") == 0
280
- end.first
281
- if key
282
- self[:headers][key]
283
- else
284
- DEFAULT_SERVER_API_VERSION
353
+ def server_api_version
354
+ key = (self[:headers] || {}).keys.select do |k|
355
+ k.casecmp("x-ops-server-api-version") == 0
356
+ end.first
357
+ if key
358
+ self[:headers][key]
359
+ else
360
+ DEFAULT_SERVER_API_VERSION
361
+ end
285
362
  end
286
363
  end
287
- end
288
364
  end
289
365
  end
@@ -1,4 +1,5 @@
1
- # Copyright:: Copyright (c) 2010-2015 Chef Software, Inc.
1
+ #
2
+ # Copyright:: Copyright (c) 2010-2019, Chef Software Inc.
2
3
  # License:: Apache License, Version 2.0
3
4
  #
4
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +16,6 @@
15
16
 
16
17
  module Mixlib
17
18
  module Authentication
18
- VERSION = "2.0.0"
19
+ VERSION = "3.0.6".freeze
19
20
  end
20
21
  end
metadata CHANGED
@@ -1,96 +1,22 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mixlib-authentication
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chef Software, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-12 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: rspec-core
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '3.2'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '3.2'
27
- - !ruby/object:Gem::Dependency
28
- name: rspec-expectations
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '3.2'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '3.2'
41
- - !ruby/object:Gem::Dependency
42
- name: rspec-mocks
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '3.2'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '3.2'
55
- - !ruby/object:Gem::Dependency
56
- name: chefstyle
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: rake
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '11'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '11'
11
+ date: 2019-12-30 00:00:00.000000000 Z
12
+ dependencies: []
83
13
  description: Mixes in simple per-request authentication
84
14
  email: info@chef.io
85
15
  executables: []
86
16
  extensions: []
87
17
  extra_rdoc_files: []
88
18
  files:
89
- - Gemfile
90
19
  - LICENSE
91
- - NOTICE
92
- - README.md
93
- - Rakefile
94
20
  - lib/mixlib/authentication.rb
95
21
  - lib/mixlib/authentication/digester.rb
96
22
  - lib/mixlib/authentication/http_authentication_request.rb
@@ -98,13 +24,7 @@ files:
98
24
  - lib/mixlib/authentication/signatureverification.rb
99
25
  - lib/mixlib/authentication/signedheaderauth.rb
100
26
  - lib/mixlib/authentication/version.rb
101
- - mixlib-authentication.gemspec
102
- - spec/mixlib/authentication/digester_spec.rb
103
- - spec/mixlib/authentication/http_authentication_request_spec.rb
104
- - spec/mixlib/authentication/mixlib_authentication_spec.rb
105
- - spec/mixlib/authentication/mixlib_log_missing_spec.rb
106
- - spec/spec_helper.rb
107
- homepage: https://www.chef.io
27
+ homepage: https://github.com/chef/mixlib-authentication
108
28
  licenses:
109
29
  - Apache-2.0
110
30
  metadata: {}
@@ -116,15 +36,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
116
36
  requirements:
117
37
  - - ">="
118
38
  - !ruby/object:Gem::Version
119
- version: '0'
39
+ version: '2.4'
120
40
  required_rubygems_version: !ruby/object:Gem::Requirement
121
41
  requirements:
122
42
  - - ">="
123
43
  - !ruby/object:Gem::Version
124
44
  version: '0'
125
45
  requirements: []
126
- rubyforge_project:
127
- rubygems_version: 2.7.3
46
+ rubygems_version: 3.0.3
128
47
  signing_key:
129
48
  specification_version: 4
130
49
  summary: Mixes in simple per-request authentication