mixlib-authentication 1.4.1 → 1.4.2
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 +4 -4
- data/Gemfile +1 -0
- data/Rakefile +1 -1
- data/lib/mixlib/authentication.rb +12 -4
- data/lib/mixlib/authentication/digester.rb +1 -3
- data/lib/mixlib/authentication/http_authentication_request.rb +2 -1
- data/lib/mixlib/authentication/null_logger.rb +24 -0
- data/lib/mixlib/authentication/signatureverification.rb +12 -12
- data/lib/mixlib/authentication/signedheaderauth.rb +13 -11
- data/lib/mixlib/authentication/version.rb +1 -1
- data/mixlib-authentication.gemspec +1 -4
- data/spec/mixlib/authentication/digester_spec.rb +2 -2
- data/spec/mixlib/authentication/mixlib_authentication_spec.rb +1 -2
- data/spec/mixlib/authentication/mixlib_log_missing_spec.rb +55 -0
- data/spec/spec_helper.rb +0 -1
- metadata +7 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e493785efc5cf2caf7d1134378fd613f35712f1f
|
4
|
+
data.tar.gz: d855dabe2ebea778d9c37037e3f843c49acbecb9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '09e094e344bc46528877b69bb015c5d2386ab57da7f7f62edbbeda81dd338fae31194736f56e947d935b0a36306dac06d148b0dbddf5d11cd3249e3cfba65571'
|
7
|
+
data.tar.gz: 2f71c58ba5d85efbf1d00db61ca09abb57d0092d46e2f8d2fa578baf0c34d2e0ff86e36e1224b40e8d0e1ad8258ef717460dcb06385045d3c920f4cd5052a86b
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -16,12 +16,13 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
18
|
|
19
|
-
require "mixlib/log"
|
20
|
-
|
21
19
|
module Mixlib
|
22
20
|
module Authentication
|
23
21
|
DEFAULT_SERVER_API_VERSION = "0"
|
24
22
|
|
23
|
+
attr_accessor :logger
|
24
|
+
module_function :logger, :logger=
|
25
|
+
|
25
26
|
class AuthenticationError < StandardError
|
26
27
|
end
|
27
28
|
|
@@ -29,10 +30,17 @@ module Mixlib
|
|
29
30
|
end
|
30
31
|
|
31
32
|
class Log
|
32
|
-
extend Mixlib::Log
|
33
33
|
end
|
34
34
|
|
35
|
-
|
35
|
+
begin
|
36
|
+
require "mixlib/log"
|
37
|
+
Mixlib::Authentication::Log.extend(Mixlib::Log)
|
38
|
+
rescue LoadError
|
39
|
+
require "mixlib/authentication/null_logger"
|
40
|
+
Mixlib::Authentication::Log.extend(Mixlib::Authentication::NullLogger)
|
41
|
+
end
|
36
42
|
|
43
|
+
Mixlib::Authentication.logger = Mixlib::Authentication::Log
|
44
|
+
Mixlib::Authentication.logger.level = :error
|
37
45
|
end
|
38
46
|
end
|
@@ -27,9 +27,7 @@ module Mixlib
|
|
27
27
|
def hash_file(f, digest = OpenSSL::Digest::SHA1)
|
28
28
|
digester = digest.new
|
29
29
|
buf = ""
|
30
|
-
while f.read(16384, buf)
|
31
|
-
digester.update buf
|
32
|
-
end
|
30
|
+
digester.update buf while f.read(16384, buf)
|
33
31
|
::Base64.encode64(digester.digest).chomp
|
34
32
|
end
|
35
33
|
|
@@ -70,7 +70,8 @@ module Mixlib
|
|
70
70
|
|
71
71
|
def request_signature
|
72
72
|
unless @request_signature
|
73
|
-
@request_signature = headers.find_all { |h| h[0].to_s =~ /^x_ops_authorization_/ }
|
73
|
+
@request_signature = headers.find_all { |h| h[0].to_s =~ /^x_ops_authorization_/ }
|
74
|
+
.sort { |x, y| x.to_s[/\d+/].to_i <=> y.to_s[/\d+/].to_i }.map { |i| i[1] }.join("\n")
|
74
75
|
Mixlib::Authentication::Log.debug "Reconstituted (user-supplied) request signature: #{@request_signature}"
|
75
76
|
end
|
76
77
|
@request_signature
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Mixlib
|
2
|
+
module Authentication
|
3
|
+
module NullLogger
|
4
|
+
|
5
|
+
attr_accessor :level
|
6
|
+
|
7
|
+
%i{debug info warn error fatal}.each do |method_name|
|
8
|
+
class_eval(<<-METHOD_DEFN, __FILE__, __LINE__)
|
9
|
+
def #{method_name}(msg=nil, &block)
|
10
|
+
true
|
11
|
+
end
|
12
|
+
METHOD_DEFN
|
13
|
+
end
|
14
|
+
|
15
|
+
%i{debug? info? warn? error? fatal?}.each do |method_name|
|
16
|
+
class_eval(<<-METHOD_DEFN, __FILE__, __LINE__)
|
17
|
+
def #{method_name}
|
18
|
+
false
|
19
|
+
end
|
20
|
+
METHOD_DEFN
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -76,7 +76,7 @@ module Mixlib
|
|
76
76
|
# X-Ops-Content-Hash:
|
77
77
|
# X-Ops-Authorization-#{line_number}
|
78
78
|
def authenticate_request(user_secret, time_skew = (15 * 60))
|
79
|
-
Mixlib::Authentication
|
79
|
+
Mixlib::Authentication.logger.debug "Initializing header auth : #{request.inspect}"
|
80
80
|
|
81
81
|
@user_secret = user_secret
|
82
82
|
@allowed_time_skew = time_skew # in seconds
|
@@ -150,14 +150,14 @@ module Mixlib
|
|
150
150
|
end
|
151
151
|
|
152
152
|
# Keep the debug messages lined up so it's easy to scan them
|
153
|
-
Mixlib::Authentication
|
154
|
-
Mixlib::Authentication
|
155
|
-
Mixlib::Authentication
|
156
|
-
Mixlib::Authentication
|
153
|
+
Mixlib::Authentication.logger.debug("Verifying request signature:")
|
154
|
+
Mixlib::Authentication.logger.debug(" Expected Block is: '#{candidate_block}'")
|
155
|
+
Mixlib::Authentication.logger.debug("Decrypted block is: '#{request_decrypted_block}'")
|
156
|
+
Mixlib::Authentication.logger.debug("Signatures match? : '#{@valid_signature}'")
|
157
157
|
|
158
158
|
@valid_signature
|
159
159
|
rescue => e
|
160
|
-
Mixlib::Authentication
|
160
|
+
Mixlib::Authentication.logger.debug("Failed to verify request signature: #{e.class.name}: #{e.message}")
|
161
161
|
@valid_signature = false
|
162
162
|
end
|
163
163
|
|
@@ -169,9 +169,9 @@ module Mixlib
|
|
169
169
|
@valid_content_hash = (content_hash == hashed_body)
|
170
170
|
|
171
171
|
# Keep the debug messages lined up so it's easy to scan them
|
172
|
-
Mixlib::Authentication
|
173
|
-
Mixlib::Authentication
|
174
|
-
Mixlib::Authentication
|
172
|
+
Mixlib::Authentication.logger.debug("Expected content hash is: '#{hashed_body}'")
|
173
|
+
Mixlib::Authentication.logger.debug(" Request Content Hash is: '#{content_hash}'")
|
174
|
+
Mixlib::Authentication.logger.debug(" Hashes match?: #{@valid_content_hash}")
|
175
175
|
|
176
176
|
@valid_content_hash
|
177
177
|
end
|
@@ -211,11 +211,11 @@ module Mixlib
|
|
211
211
|
# Any file that's included in the request is hashed if it's there. Otherwise,
|
212
212
|
# we hash the body.
|
213
213
|
if file_param
|
214
|
-
Mixlib::Authentication
|
214
|
+
Mixlib::Authentication.logger.debug "Digesting file_param: '#{file_param.inspect}'"
|
215
215
|
@hashed_body = digester.hash_file(file_param, digest)
|
216
216
|
else
|
217
217
|
body = request.raw_post
|
218
|
-
Mixlib::Authentication
|
218
|
+
Mixlib::Authentication.logger.debug "Digesting body: '#{body}'"
|
219
219
|
@hashed_body = digester.hash_string(body, digest)
|
220
220
|
end
|
221
221
|
end
|
@@ -232,7 +232,7 @@ module Mixlib
|
|
232
232
|
def timestamp_within_bounds?(time1, time2)
|
233
233
|
time_diff = (time2 - time1).abs
|
234
234
|
is_allowed = (time_diff < @allowed_time_skew)
|
235
|
-
Mixlib::Authentication
|
235
|
+
Mixlib::Authentication.logger.debug "Request time difference: #{time_diff}, within #{@allowed_time_skew} seconds? : #{!!is_allowed}"
|
236
236
|
is_allowed
|
237
237
|
end
|
238
238
|
end
|
@@ -115,7 +115,7 @@ module Mixlib
|
|
115
115
|
header_hash[key] = signature_lines[idx]
|
116
116
|
end
|
117
117
|
|
118
|
-
Mixlib::Authentication
|
118
|
+
Mixlib::Authentication.logger.debug "Header hash: #{header_hash.inspect}"
|
119
119
|
|
120
120
|
header_hash
|
121
121
|
end
|
@@ -166,7 +166,8 @@ module Mixlib
|
|
166
166
|
# Hence, we're going to assume the one that is passed to sign is
|
167
167
|
# the correct one and needs to passed through all the functions
|
168
168
|
# that do any sort of digest.
|
169
|
-
|
169
|
+
@hashed_body_digest = nil unless defined?(@hashed_body_digest)
|
170
|
+
if !@hashed_body_digest.nil? && @hashed_body_digest != digest
|
170
171
|
raise "hashed_body must always be called with the same digest"
|
171
172
|
else
|
172
173
|
@hashed_body_digest = digest
|
@@ -176,10 +177,10 @@ module Mixlib
|
|
176
177
|
# TODO: tim 2009-12-28: It'd be nice to just remove this special case,
|
177
178
|
# always sign the entire request body, using the expanded multipart
|
178
179
|
# body in the case of a file being include.
|
179
|
-
@hashed_body ||= if
|
180
|
-
digester.hash_file(
|
180
|
+
@hashed_body ||= if file && file.respond_to?(:read)
|
181
|
+
digester.hash_file(file, digest)
|
181
182
|
else
|
182
|
-
digester.hash_string(
|
183
|
+
digester.hash_string(body, digest)
|
183
184
|
end
|
184
185
|
end
|
185
186
|
|
@@ -235,7 +236,7 @@ module Mixlib
|
|
235
236
|
memo[field_name.to_sym] = field_value.strip
|
236
237
|
memo
|
237
238
|
end
|
238
|
-
Mixlib::Authentication
|
239
|
+
Mixlib::Authentication.logger.debug "Parsed signing description: #{parts.inspect}"
|
239
240
|
parts
|
240
241
|
end
|
241
242
|
|
@@ -246,7 +247,7 @@ module Mixlib
|
|
246
247
|
# private
|
247
248
|
def do_sign(private_key, digest, sign_algorithm, sign_version)
|
248
249
|
string_to_sign = canonicalize_request(sign_algorithm, sign_version)
|
249
|
-
Mixlib::Authentication
|
250
|
+
Mixlib::Authentication.logger.debug "String to sign: '#{string_to_sign}'"
|
250
251
|
case sign_version
|
251
252
|
when "1.3"
|
252
253
|
private_key.sign(digest.new, string_to_sign)
|
@@ -263,18 +264,19 @@ module Mixlib
|
|
263
264
|
# A Struct-based value object that contains the necessary information to
|
264
265
|
# generate a request signature. `SignedHeaderAuth.signing_object()`
|
265
266
|
# provides a more convenient interface to the constructor.
|
266
|
-
|
267
|
+
SigningObject = Struct.new(:http_method, :path, :body, :host,
|
267
268
|
:timestamp, :user_id, :file, :proto_version,
|
268
|
-
:headers)
|
269
|
+
:headers) do
|
270
|
+
|
269
271
|
include SignedHeaderAuth
|
270
272
|
|
271
273
|
def proto_version
|
272
|
-
(self[:proto_version] || DEFAULT_PROTO_VERSION).to_s
|
274
|
+
(self[:proto_version] || SignedHeaderAuth::DEFAULT_PROTO_VERSION).to_s
|
273
275
|
end
|
274
276
|
|
275
277
|
def server_api_version
|
276
278
|
key = (self[:headers] || {}).keys.select do |k|
|
277
|
-
k.
|
279
|
+
k.casecmp("x-ops-server-api-version") == 0
|
278
280
|
end.first
|
279
281
|
if key
|
280
282
|
self[:headers][key]
|
@@ -12,14 +12,11 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.email = "info@chef.io"
|
13
13
|
s.homepage = "https://www.chef.io"
|
14
14
|
|
15
|
-
# Uncomment this to add a dependency
|
16
|
-
s.add_dependency "mixlib-log"
|
17
|
-
|
18
15
|
s.require_path = "lib"
|
19
16
|
s.files = %w{LICENSE README.md Gemfile Rakefile NOTICE} + Dir.glob("*.gemspec") +
|
20
17
|
Dir.glob("{lib,spec}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) }
|
21
18
|
|
22
19
|
%w{rspec-core rspec-expectations rspec-mocks}.each { |gem| s.add_development_dependency gem, "~> 3.2" }
|
23
20
|
s.add_development_dependency "chefstyle"
|
24
|
-
s.add_development_dependency "rake", "~>
|
21
|
+
s.add_development_dependency "rake", "~> 11"
|
25
22
|
end
|
@@ -7,14 +7,14 @@ describe Mixlib::Authentication::Digester do
|
|
7
7
|
let(:test_string) { "hello" }
|
8
8
|
let(:test_string_checksum) { "qvTGHdzF6KLavt4PO0gs2a6pQ00=" }
|
9
9
|
|
10
|
-
describe
|
10
|
+
describe "#hash_file" do
|
11
11
|
it "should default to use SHA1" do
|
12
12
|
expect(described_class.hash_file(StringIO.new(test_string))).to(
|
13
13
|
eq(test_string_checksum))
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
describe
|
17
|
+
describe "#hash_string" do
|
18
18
|
it "should default to use SHA1" do
|
19
19
|
expect(described_class.hash_string(test_string)).to(
|
20
20
|
eq(test_string_checksum))
|
@@ -63,8 +63,7 @@ class MockFile
|
|
63
63
|
end
|
64
64
|
|
65
65
|
# Uncomment this to get some more info from the methods we're testing.
|
66
|
-
#Mixlib::Authentication
|
67
|
-
#Mixlib::Authentication::Log.level :debug
|
66
|
+
#Mixlib::Authentication.logger.level = :debug
|
68
67
|
|
69
68
|
describe "Mixlib::Authentication::SignedHeaderAuth" do
|
70
69
|
|
@@ -0,0 +1,55 @@
|
|
1
|
+
describe "Mixlib::Authentication::Log" do
|
2
|
+
before do
|
3
|
+
Mixlib::Authentication.send(:remove_const, "DEFAULT_SERVER_API_VERSION")
|
4
|
+
Mixlib::Authentication.send(:remove_const, "Log")
|
5
|
+
end
|
6
|
+
|
7
|
+
context "without mixlib-log" do
|
8
|
+
before do
|
9
|
+
@mixlib_path = $LOAD_PATH.find { |p| p.match("mixlib-log") }
|
10
|
+
$LOAD_PATH.reject! { |p| p.match("mixlib-log") }
|
11
|
+
|
12
|
+
load "mixlib/authentication.rb"
|
13
|
+
end
|
14
|
+
|
15
|
+
after do
|
16
|
+
$LOAD_PATH.unshift(@mixlib_path)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "uses MixlibLogMissing" do
|
20
|
+
expect(Mixlib::Authentication::Log.singleton_class.included_modules)
|
21
|
+
.to include(Mixlib::Authentication::NullLogger)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "default log level is :error" do
|
25
|
+
expect(Mixlib::Authentication::Log.level).to eq(:error)
|
26
|
+
end
|
27
|
+
|
28
|
+
%w{debug info warn error fatal}.each do |level|
|
29
|
+
it "logs at level #{level}" do
|
30
|
+
expect(Mixlib::Authentication::Log).to receive(level).with("foo")
|
31
|
+
|
32
|
+
Mixlib::Authentication.logger.send(level, "foo")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "with mixlib-log" do
|
38
|
+
before do
|
39
|
+
load "mixlib/authentication.rb"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "uses Mixlib::Log" do
|
43
|
+
expect(Mixlib::Authentication::Log.singleton_class.included_modules)
|
44
|
+
.to include(Mixlib::Log)
|
45
|
+
end
|
46
|
+
|
47
|
+
%w{debug info warn error fatal}.each do |level|
|
48
|
+
it "forward #{level} to mixlib-log" do
|
49
|
+
expect(Mixlib::Authentication::Log.logger).to receive(level).with("foo")
|
50
|
+
|
51
|
+
Mixlib::Authentication.logger.send(level, "foo")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mixlib-authentication
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.2
|
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:
|
11
|
+
date: 2017-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: mixlib-log
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: rspec-core
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,14 +72,14 @@ dependencies:
|
|
86
72
|
requirements:
|
87
73
|
- - "~>"
|
88
74
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
75
|
+
version: '11'
|
90
76
|
type: :development
|
91
77
|
prerelease: false
|
92
78
|
version_requirements: !ruby/object:Gem::Requirement
|
93
79
|
requirements:
|
94
80
|
- - "~>"
|
95
81
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
82
|
+
version: '11'
|
97
83
|
description: Mixes in simple per-request authentication
|
98
84
|
email: info@chef.io
|
99
85
|
executables: []
|
@@ -108,6 +94,7 @@ files:
|
|
108
94
|
- lib/mixlib/authentication.rb
|
109
95
|
- lib/mixlib/authentication/digester.rb
|
110
96
|
- lib/mixlib/authentication/http_authentication_request.rb
|
97
|
+
- lib/mixlib/authentication/null_logger.rb
|
111
98
|
- lib/mixlib/authentication/signatureverification.rb
|
112
99
|
- lib/mixlib/authentication/signedheaderauth.rb
|
113
100
|
- lib/mixlib/authentication/version.rb
|
@@ -115,6 +102,7 @@ files:
|
|
115
102
|
- spec/mixlib/authentication/digester_spec.rb
|
116
103
|
- spec/mixlib/authentication/http_authentication_request_spec.rb
|
117
104
|
- spec/mixlib/authentication/mixlib_authentication_spec.rb
|
105
|
+
- spec/mixlib/authentication/mixlib_log_missing_spec.rb
|
118
106
|
- spec/spec_helper.rb
|
119
107
|
homepage: https://www.chef.io
|
120
108
|
licenses:
|
@@ -136,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
136
124
|
version: '0'
|
137
125
|
requirements: []
|
138
126
|
rubyforge_project:
|
139
|
-
rubygems_version: 2.
|
127
|
+
rubygems_version: 2.6.11
|
140
128
|
signing_key:
|
141
129
|
specification_version: 4
|
142
130
|
summary: Mixes in simple per-request authentication
|