mixlib-authentication 1.4.1 → 1.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|