mixlib-authentication 1.3.0 → 1.4.0.rc.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4af0553c29d4d0ee53f281ab38ef8ed7097ba8f9
4
+ data.tar.gz: 198e0ae73d73b4d5c3f39e61dd30bab6822c1031
5
+ SHA512:
6
+ metadata.gz: 5613f35e48e2142c25614b39c114b9ed7412a5039eea64141bed4087408c51f4242a11b954a9d0393a85b1ecdd850dc505d2267c21eb8c3737af6a795873d4c8
7
+ data.tar.gz: 071de33850bcf8120424ef0ee26e4d6013fd96fabff92c10dd69caf2da521b890d8433809080b8e86a0022f111c83edb6e47c334ed44517bb3811db856c6e8a0
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+ gemspec
3
+
4
+ group(:development) do
5
+ gem 'pry'
6
+ end
data/Rakefile CHANGED
@@ -1,5 +1,5 @@
1
1
  require 'rubygems'
2
- require 'rake/gempackagetask'
2
+ require 'rubygems/package_task'
3
3
  require 'rubygems/specification'
4
4
  require 'date'
5
5
  require 'rspec/core/rake_task'
@@ -16,12 +16,12 @@ task :default => :spec
16
16
  desc "Run specs"
17
17
  RSpec::Core::RakeTask.new do |t|
18
18
  t.pattern = 'spec/**/*_spec.rb'
19
- t.rspec_opts = %w(-fs --color)
19
+ t.rspec_opts = %w(--format documentation --color)
20
20
  end
21
21
 
22
22
  gem_spec = eval(File.read("mixlib-authentication.gemspec"))
23
23
 
24
- Rake::GemPackageTask.new(gem_spec) do |pkg|
24
+ Gem::PackageTask.new(gem_spec) do |pkg|
25
25
  pkg.gem_spec = gem_spec
26
26
  end
27
27
 
@@ -20,6 +20,8 @@ require 'mixlib/log'
20
20
 
21
21
  module Mixlib
22
22
  module Authentication
23
+ DEFAULT_SERVER_API_VERSION = '0'
24
+
23
25
  class AuthenticationError < StandardError
24
26
  end
25
27
 
@@ -21,11 +21,10 @@ require 'mixlib/authentication'
21
21
  module Mixlib
22
22
  module Authentication
23
23
  class Digester
24
-
25
24
  class << self
26
-
27
- def hash_file(f)
28
- digester = Digest::SHA1.new
25
+
26
+ def hash_file(digest, f)
27
+ digester = digest.new
29
28
  buf = ""
30
29
  while f.read(16384, buf)
31
30
  digester.update buf
@@ -34,15 +33,15 @@ module Mixlib
34
33
  end
35
34
 
36
35
  # Digests a string, base64's and chomps the end
37
- #
36
+ #
38
37
  # ====Parameters
39
- #
40
- def hash_string(str)
41
- ::Base64.encode64(Digest::SHA1.digest(str)).chomp
38
+ #
39
+ def hash_string(digest, str)
40
+ ::Base64.encode64(digest.digest(str)).chomp
42
41
  end
43
-
42
+
44
43
  end
45
-
44
+
46
45
  end
47
46
  end
48
47
  end
@@ -64,6 +64,10 @@ module Mixlib
64
64
  headers[:x_ops_content_hash].chomp
65
65
  end
66
66
 
67
+ def server_api_version
68
+ (headers[:x_ops_server_api_version] || DEFAULT_SERVER_API_VERSION).chomp
69
+ end
70
+
67
71
  def request_signature
68
72
  unless @request_signature
69
73
  @request_signature = headers.find_all { |h| h[0].to_s =~ /^x_ops_authorization_/ }.sort { |x,y| x.to_s <=> y.to_s}.map { |i| i[1] }.join("\n")
@@ -80,8 +84,6 @@ module Mixlib
80
84
  raise MissingAuthenticationHeader, "missing required authentication header(s) '#{missing_headers.join("', '")}'"
81
85
  end
82
86
  end
83
-
84
-
85
87
  end
86
88
  end
87
89
  end
@@ -48,6 +48,8 @@ module Mixlib
48
48
 
49
49
  def_delegator :@auth_request, :request
50
50
 
51
+ def_delegator :@auth_request, :server_api_version
52
+
51
53
  include Mixlib::Authentication::SignedHeaderAuth
52
54
 
53
55
  def initialize(request=nil)
@@ -138,8 +140,15 @@ module Mixlib
138
140
 
139
141
  def verify_signature(algorithm, version)
140
142
  candidate_block = canonicalize_request(algorithm, version)
141
- request_decrypted_block = @user_secret.public_decrypt(Base64.decode64(request_signature))
142
- @valid_signature = (request_decrypted_block == candidate_block)
143
+ signature = Base64.decode64(request_signature)
144
+ @valid_signature = case version
145
+ when '1.3'
146
+ digest = validate_sign_version_digest!(algorithm, version)
147
+ @user_secret.verify(digest.new, signature, candidate_block)
148
+ else
149
+ request_decrypted_block = @user_secret.public_decrypt(signature)
150
+ (request_decrypted_block == candidate_block)
151
+ end
143
152
 
144
153
  # Keep the debug messages lined up so it's easy to scan them
145
154
  Mixlib::Authentication::Log.debug("Verifying request signature:")
@@ -171,7 +180,7 @@ module Mixlib
171
180
 
172
181
  # The request signature is based on any file attached, if any. Otherwise
173
182
  # it's based on the body of the request.
174
- def hashed_body
183
+ def hashed_body(digest=Digest::SHA1)
175
184
  unless @hashed_body
176
185
  # TODO: tim: 2009-112-28: It'd be nice to remove this special case, and
177
186
  # always hash the entire request body. In the file case it would just be
@@ -205,11 +214,11 @@ module Mixlib
205
214
  # we hash the body.
206
215
  if file_param
207
216
  Mixlib::Authentication::Log.debug "Digesting file_param: '#{file_param.inspect}'"
208
- @hashed_body = digester.hash_file(file_param)
217
+ @hashed_body = digester.hash_file(digest, file_param)
209
218
  else
210
219
  body = request.raw_post
211
220
  Mixlib::Authentication::Log.debug "Digesting body: '#{body}'"
212
- @hashed_body = digester.hash_string(body)
221
+ @hashed_body = digester.hash_string(digest, body)
213
222
  end
214
223
  end
215
224
  @hashed_body
@@ -19,7 +19,7 @@
19
19
 
20
20
  require 'time'
21
21
  require 'base64'
22
- require 'digest/sha1'
22
+ require 'openssl/digest'
23
23
  require 'mixlib/authentication'
24
24
  require 'mixlib/authentication/digester'
25
25
 
@@ -29,13 +29,21 @@ module Mixlib
29
29
  module SignedHeaderAuth
30
30
 
31
31
  NULL_ARG = Object.new
32
+
33
+ ALGORITHM_FOR_VERSION = {
34
+ '1.0' => 'sha1',
35
+ '1.1' => 'sha1',
36
+ '1.3' => 'sha256',
37
+ }.freeze()
38
+
39
+ # Use of SUPPORTED_ALGORITHMS and SUPPORTED_VERSIONS is deprecated. Use
40
+ # ALGORITHM_FOR_VERSION instead
32
41
  SUPPORTED_ALGORITHMS = ['sha1'].freeze
33
42
  SUPPORTED_VERSIONS = ['1.0', '1.1'].freeze
34
43
 
35
44
  DEFAULT_SIGN_ALGORITHM = 'sha1'.freeze
36
45
  DEFAULT_PROTO_VERSION = '1.0'.freeze
37
46
 
38
-
39
47
  # === signing_object
40
48
  # This is the intended interface for signing requests with the
41
49
  # Opscode/Chef signed header protocol. This wraps the constructor for a
@@ -65,11 +73,20 @@ module Mixlib
65
73
  # These parameters are accepted but not used in the computation of the signature.
66
74
  # * `:host`: The host part of the URI
67
75
  def self.signing_object(args={ })
68
- SigningObject.new(args[:http_method], args[:path], args[:body], args[:host], args[:timestamp], args[:user_id], args[:file], args[:proto_version])
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
+ )
69
86
  end
70
87
 
71
88
  def algorithm
72
- DEFAULT_SIGN_ALGORITHM
89
+ ALGORITHM_FOR_VERSION[proto_version] || DEFAULT_SIGN_ALGORITHM
73
90
  end
74
91
 
75
92
  def proto_version
@@ -81,28 +98,50 @@ module Mixlib
81
98
  # ====Parameters
82
99
  # private_key<OpenSSL::PKey::RSA>:: user's RSA private key.
83
100
  def sign(private_key, sign_algorithm=algorithm, sign_version=proto_version)
101
+ digest = validate_sign_version_digest!(sign_algorithm, sign_version)
84
102
  # Our multiline hash for authorization will be encoded in multiple header
85
103
  # lines - X-Ops-Authorization-1, ... (starts at 1, not 0!)
86
104
  header_hash = {
87
105
  "X-Ops-Sign" => "algorithm=#{sign_algorithm};version=#{sign_version};",
88
106
  "X-Ops-Userid" => user_id,
89
107
  "X-Ops-Timestamp" => canonical_time,
90
- "X-Ops-Content-Hash" => hashed_body,
108
+ "X-Ops-Content-Hash" => hashed_body(digest),
91
109
  }
92
110
 
93
- string_to_sign = canonicalize_request(sign_algorithm, sign_version)
94
- signature = Base64.encode64(private_key.private_encrypt(string_to_sign)).chomp
111
+ signature = Base64.encode64(do_sign(private_key, digest, sign_algorithm, sign_version)).chomp
95
112
  signature_lines = signature.split(/\n/)
96
113
  signature_lines.each_index do |idx|
97
114
  key = "X-Ops-Authorization-#{idx + 1}"
98
115
  header_hash[key] = signature_lines[idx]
99
116
  end
100
117
 
101
- Mixlib::Authentication::Log.debug "String to sign: '#{string_to_sign}'\nHeader hash: #{header_hash.inspect}"
118
+ Mixlib::Authentication::Log.debug "Header hash: #{header_hash.inspect}"
102
119
 
103
120
  header_hash
104
121
  end
105
122
 
123
+ def validate_sign_version_digest!(sign_algorithm, sign_version)
124
+ if ALGORITHM_FOR_VERSION[sign_version].nil?
125
+ raise AuthenticationError,
126
+ "Unsupported version '#{sign_version}'"
127
+ end
128
+
129
+ if ALGORITHM_FOR_VERSION[sign_version] != sign_algorithm
130
+ raise AuthenticationError,
131
+ "Unsupported algorithm #{sign_algorithm} for version '#{sign_version}'"
132
+ end
133
+
134
+ case sign_algorithm
135
+ when 'sha1'
136
+ OpenSSL::Digest::SHA1
137
+ when 'sha256'
138
+ OpenSSL::Digest::SHA256
139
+ else
140
+ # This case should never happen
141
+ raise "Unknown algorithm #{sign_algorithm}"
142
+ end
143
+ end
144
+
106
145
  # Build the canonicalized time based on utc & iso8601
107
146
  #
108
147
  # ====Parameters
@@ -121,13 +160,27 @@ module Mixlib
121
160
  p.length > 1 ? p.chomp('/') : p
122
161
  end
123
162
 
124
- def hashed_body
163
+ def hashed_body(digest=OpenSSL::Digest::SHA1)
164
+ # This is weird. sign() is called with the digest type and signing
165
+ # version. These are also expected to be properties of the object.
166
+ # Hence, we're going to assume the one that is passed to sign is
167
+ # the correct one and needs to passed through all the functions
168
+ # that do any sort of digest.
169
+ if @hashed_body_digest != nil && @hashed_body_digest != digest
170
+ raise "hashed_body must always be called with the same digest"
171
+ else
172
+ @hashed_body_digest = digest
173
+ end
125
174
  # Hash the file object if it was passed in, otherwise hash based on
126
175
  # the body.
127
176
  # TODO: tim 2009-12-28: It'd be nice to just remove this special case,
128
177
  # always sign the entire request body, using the expanded multipart
129
178
  # body in the case of a file being include.
130
- @hashed_body ||= (self.file && self.file.respond_to?(:read)) ? digester.hash_file(self.file) : digester.hash_string(self.body)
179
+ @hashed_body ||= if self.file && self.file.respond_to?(:read)
180
+ digester.hash_file(digest, self.file)
181
+ else
182
+ digester.hash_string(digest, self.body)
183
+ end
131
184
  end
132
185
 
133
186
  # Takes HTTP request method & headers and creates a canonical form
@@ -137,21 +190,37 @@ module Mixlib
137
190
  #
138
191
  #
139
192
  def canonicalize_request(sign_algorithm=algorithm, sign_version=proto_version)
140
- unless SUPPORTED_ALGORITHMS.include?(sign_algorithm) && SUPPORTED_VERSIONS.include?(sign_version)
141
- raise AuthenticationError, "Bad algorithm '#{sign_algorithm}' (allowed: #{SUPPORTED_ALGORITHMS.inspect}) or version '#{sign_version}' (allowed: #{SUPPORTED_VERSIONS.inspect})"
193
+ digest = validate_sign_version_digest!(sign_algorithm, sign_version)
194
+ canonical_x_ops_user_id = canonicalize_user_id(user_id, sign_version, digest)
195
+ case sign_version
196
+ when "1.3"
197
+ [
198
+ "Method:#{http_method.to_s.upcase}",
199
+ "Path:#{canonical_path}",
200
+ "X-Ops-Content-Hash:#{hashed_body(digest)}",
201
+ "X-Ops-Sign:version=#{sign_version}",
202
+ "X-Ops-Timestamp:#{canonical_time}",
203
+ "X-Ops-UserId:#{canonical_x_ops_user_id}",
204
+ "X-Ops-Server-API-Version:#{server_api_version}",
205
+ ].join("\n")
206
+ else
207
+ [
208
+ "Method:#{http_method.to_s.upcase}",
209
+ "Hashed Path:#{digester.hash_string(digest, canonical_path)}",
210
+ "X-Ops-Content-Hash:#{hashed_body(digest)}",
211
+ "X-Ops-Timestamp:#{canonical_time}",
212
+ "X-Ops-UserId:#{canonical_x_ops_user_id}"
213
+ ].join("\n")
142
214
  end
143
-
144
- canonical_x_ops_user_id = canonicalize_user_id(user_id, sign_version)
145
- "Method:#{http_method.to_s.upcase}\nHashed Path:#{digester.hash_string(canonical_path)}\nX-Ops-Content-Hash:#{hashed_body}\nX-Ops-Timestamp:#{canonical_time}\nX-Ops-UserId:#{canonical_x_ops_user_id}"
146
215
  end
147
216
 
148
- def canonicalize_user_id(user_id, proto_version)
217
+ def canonicalize_user_id(user_id, proto_version, digest=OpenSSL::Digest::SHA1)
149
218
  case proto_version
150
219
  when "1.1"
151
- digester.hash_string(user_id)
152
- when "1.0"
153
- user_id
220
+ # and 1.2 if that ever gets implemented
221
+ digester.hash_string(digest, user_id)
154
222
  else
223
+ # versions 1.0 and 1.3
155
224
  user_id
156
225
  end
157
226
  end
@@ -174,6 +243,18 @@ module Mixlib
174
243
  Mixlib::Authentication::Digester
175
244
  end
176
245
 
246
+ # private
247
+ def do_sign(private_key, digest, sign_algorithm, sign_version)
248
+ string_to_sign = canonicalize_request(sign_algorithm, sign_version)
249
+ Mixlib::Authentication::Log.debug "String to sign: '#{string_to_sign}'"
250
+ case sign_version
251
+ when '1.3'
252
+ private_key.sign(digest.new, string_to_sign)
253
+ else
254
+ private_key.private_encrypt(string_to_sign)
255
+ end
256
+ end
257
+
177
258
  private :canonical_time, :canonical_path, :parse_signing_description, :digester, :canonicalize_user_id
178
259
 
179
260
  end
@@ -182,13 +263,25 @@ module Mixlib
182
263
  # A Struct-based value object that contains the necessary information to
183
264
  # generate a request signature. `SignedHeaderAuth.signing_object()`
184
265
  # provides a more convenient interface to the constructor.
185
- class SigningObject < Struct.new(:http_method, :path, :body, :host, :timestamp, :user_id, :file, :proto_version)
266
+ class SigningObject < Struct.new(:http_method, :path, :body, :host,
267
+ :timestamp, :user_id, :file, :proto_version,
268
+ :headers)
186
269
  include SignedHeaderAuth
187
270
 
188
271
  def proto_version
189
272
  (self[:proto_version] or DEFAULT_PROTO_VERSION).to_s
190
273
  end
191
- end
192
274
 
275
+ def server_api_version
276
+ key = (self[:headers] || {}).keys.select do |k|
277
+ k.downcase == 'x-ops-server-api-version'
278
+ end.first
279
+ if key
280
+ self[:headers][key]
281
+ else
282
+ DEFAULT_SERVER_API_VERSION
283
+ end
284
+ end
285
+ end
193
286
  end
194
287
  end
@@ -0,0 +1,21 @@
1
+ # Copyright:: Copyright (c) 2010-2015 Chef Software, Inc.
2
+ # License:: Apache License, Version 2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+ module Mixlib
18
+ module Authentication
19
+ VERSION = '1.4.0.rc.0'
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/lib')
2
+ require 'mixlib/authentication/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "mixlib-authentication"
6
+ s.version = Mixlib::Authentication::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.has_rdoc = true
9
+ s.extra_rdoc_files = ["README.rdoc", "LICENSE", 'NOTICE']
10
+ s.summary = "Mixes in simple per-request authentication"
11
+ s.description = s.summary
12
+ s.author = "Opscode, Inc."
13
+ s.email = "info@opscode.com"
14
+ s.homepage = "http://www.opscode.com"
15
+
16
+ # Uncomment this to add a dependency
17
+ s.add_dependency "mixlib-log"
18
+
19
+ s.require_path = 'lib'
20
+ s.files = %w(LICENSE README.rdoc Gemfile Rakefile NOTICE) + Dir.glob("*.gemspec") +
21
+ Dir.glob("{lib,spec}/**/*", File::FNM_DOTMATCH).reject {|f| File.directory?(f) }
22
+
23
+ %w(rspec-core rspec-expectations rspec-mocks).each { |gem| s.add_dependency gem, "~> 3.2" }
24
+ s.add_development_dependency "rake", "~> 10.4"
25
+ end
@@ -76,41 +76,41 @@ describe Mixlib::Authentication::HTTPAuthenticationRequest do
76
76
  :x_ops_authorization_4=>"IWPZDHSiPcw//AYNgW1CCDptt+UFuaFYbtqZegcBd2n/jzcWODA7zL4KWEUy",
77
77
  :x_ops_authorization_5=>"9q4rlh/+1tBReg60QdsmDRsw/cdO1GZrKtuCwbuD4+nbRdVBKv72rqHX9cu0",
78
78
  :x_ops_authorization_6=>"utju9jzczCyB+sSAQWrxSsXB/b8vV2qs0l4VD2ML+w=="}
79
- @http_authentication_request.headers.should == expected
79
+ expect(@http_authentication_request.headers).to eq(expected)
80
80
  end
81
81
 
82
82
  it "raises an error when not all required headers are given" do
83
83
  @merb_headers.delete("HTTP_X_OPS_SIGN")
84
84
  exception = Mixlib::Authentication::MissingAuthenticationHeader
85
- lambda{ Mixlib::Authentication::HTTPAuthenticationRequest.new(@request) }.should raise_error(exception)
85
+ expect{ Mixlib::Authentication::HTTPAuthenticationRequest.new(@request) }.to raise_error(exception)
86
86
  end
87
87
 
88
88
  it "extracts the path from the request" do
89
- @http_authentication_request.path.should == '/nodes'
89
+ expect(@http_authentication_request.path).to eq('/nodes')
90
90
  end
91
91
 
92
92
  it "extracts the request method from the request" do
93
- @http_authentication_request.http_method.should == 'POST'
93
+ expect(@http_authentication_request.http_method).to eq('POST')
94
94
  end
95
95
 
96
96
  it "extracts the signing description from the request headers" do
97
- @http_authentication_request.signing_description.should == 'version=1.0'
97
+ expect(@http_authentication_request.signing_description).to eq('version=1.0')
98
98
  end
99
99
 
100
100
  it "extracts the user_id from the request headers" do
101
- @http_authentication_request.user_id.should == 'spec-user'
101
+ expect(@http_authentication_request.user_id).to eq('spec-user')
102
102
  end
103
103
 
104
104
  it "extracts the timestamp from the request headers" do
105
- @http_authentication_request.timestamp.should == "2009-01-01T12:00:00Z"
105
+ expect(@http_authentication_request.timestamp).to eq("2009-01-01T12:00:00Z")
106
106
  end
107
107
 
108
108
  it "extracts the host from the request headers" do
109
- @http_authentication_request.host.should == "127.0.0.1"
109
+ expect(@http_authentication_request.host).to eq("127.0.0.1")
110
110
  end
111
111
 
112
112
  it "extracts the content hash from the request headers" do
113
- @http_authentication_request.content_hash.should == "DFteJZPVv6WKdQmMqZUQUumUyRs="
113
+ expect(@http_authentication_request.content_hash).to eq("DFteJZPVv6WKdQmMqZUQUumUyRs=")
114
114
  end
115
115
 
116
116
  it "rebuilds the request signature from the headers" do
@@ -122,7 +122,11 @@ IWPZDHSiPcw//AYNgW1CCDptt+UFuaFYbtqZegcBd2n/jzcWODA7zL4KWEUy
122
122
  9q4rlh/+1tBReg60QdsmDRsw/cdO1GZrKtuCwbuD4+nbRdVBKv72rqHX9cu0
123
123
  utju9jzczCyB+sSAQWrxSsXB/b8vV2qs0l4VD2ML+w==
124
124
  SIG
125
- @http_authentication_request.request_signature.should == expected.chomp
125
+ expect(@http_authentication_request.request_signature).to eq(expected.chomp)
126
+ end
127
+
128
+ it "defaults to server api version 0" do
129
+ expect(@http_authentication_request.server_api_version).to eq('0')
126
130
  end
127
131
 
128
132
  end
@@ -72,52 +72,65 @@ describe "Mixlib::Authentication::SignedHeaderAuth" do
72
72
 
73
73
  it "should generate the correct string to sign and signature, version 1.0 (default)" do
74
74
 
75
- V1_0_SIGNING_OBJECT.canonicalize_request.should == V1_0_CANONICAL_REQUEST
75
+ expect(V1_0_SIGNING_OBJECT.canonicalize_request).to eq(V1_0_CANONICAL_REQUEST)
76
76
 
77
77
  # If you need to regenerate the constants in this test spec, print out
78
78
  # the results of res.inspect and copy them as appropriate into the
79
79
  # the constants in this file.
80
- V1_0_SIGNING_OBJECT.sign(PRIVATE_KEY).should == EXPECTED_SIGN_RESULT_V1_0
80
+ expect(V1_0_SIGNING_OBJECT.sign(PRIVATE_KEY)).to eq(EXPECTED_SIGN_RESULT_V1_0)
81
81
  end
82
82
 
83
83
  it "should generate the correct string to sign and signature, version 1.1" do
84
- V1_1_SIGNING_OBJECT.proto_version.should == "1.1"
85
- V1_1_SIGNING_OBJECT.canonicalize_request.should == V1_1_CANONICAL_REQUEST
84
+ expect(V1_1_SIGNING_OBJECT.proto_version).to eq("1.1")
85
+ expect(V1_1_SIGNING_OBJECT.canonicalize_request).to eq(V1_1_CANONICAL_REQUEST)
86
86
 
87
87
  # If you need to regenerate the constants in this test spec, print out
88
88
  # the results of res.inspect and copy them as appropriate into the
89
89
  # the constants in this file.
90
- V1_1_SIGNING_OBJECT.sign(PRIVATE_KEY).should == EXPECTED_SIGN_RESULT_V1_1
90
+ expect(V1_1_SIGNING_OBJECT.sign(PRIVATE_KEY)).to eq(EXPECTED_SIGN_RESULT_V1_1)
91
91
  end
92
92
 
93
+ it "should generate the correct string to sign and signature for version 1.3 with SHA256" do
94
+ expect(V1_3_SHA256_SIGNING_OBJECT.proto_version).to eq("1.3")
95
+ expect(V1_3_SHA256_SIGNING_OBJECT.algorithm).to eq("sha256")
96
+ expect(V1_3_SHA256_SIGNING_OBJECT.server_api_version).to eq("1")
97
+ expect(V1_3_SHA256_SIGNING_OBJECT.canonicalize_request).to eq(V1_3_SHA256_CANONICAL_REQUEST)
98
+
99
+ # If you need to regenerate the constants in this test spec, print out
100
+ # the results of res.inspect and copy them as appropriate into the
101
+ # the constants in this file.
102
+ expect(V1_3_SHA256_SIGNING_OBJECT.sign(PRIVATE_KEY)).to eq(EXPECTED_SIGN_RESULT_V1_3_SHA256)
103
+ end
104
+
105
+
93
106
  it "should generate the correct string to sign and signature for non-default proto version when used as a mixin" do
94
107
  algorithm = 'sha1'
95
108
  version = '1.1'
96
109
 
97
110
  V1_1_SIGNING_OBJECT.proto_version = "1.0"
98
- V1_1_SIGNING_OBJECT.proto_version.should == "1.0"
99
- V1_1_SIGNING_OBJECT.canonicalize_request(algorithm, version).should == V1_1_CANONICAL_REQUEST
111
+ expect(V1_1_SIGNING_OBJECT.proto_version).to eq("1.0")
112
+ expect(V1_1_SIGNING_OBJECT.canonicalize_request(algorithm, version)).to eq(V1_1_CANONICAL_REQUEST)
100
113
 
101
114
  # If you need to regenerate the constants in this test spec, print out
102
115
  # the results of res.inspect and copy them as appropriate into the
103
116
  # the constants in this file.
104
- V1_1_SIGNING_OBJECT.sign(PRIVATE_KEY, algorithm, version).should == EXPECTED_SIGN_RESULT_V1_1
117
+ expect(V1_1_SIGNING_OBJECT.sign(PRIVATE_KEY, algorithm, version)).to eq(EXPECTED_SIGN_RESULT_V1_1)
105
118
  end
106
119
 
107
120
  it "should not choke when signing a request for a long user id with version 1.1" do
108
- lambda { LONG_SIGNING_OBJECT.sign(PRIVATE_KEY, 'sha1', '1.1') }.should_not raise_error
121
+ expect { LONG_SIGNING_OBJECT.sign(PRIVATE_KEY, 'sha1', '1.1') }.not_to raise_error
109
122
  end
110
123
 
111
124
  it "should choke when signing a request for a long user id with version 1.0" do
112
- lambda { LONG_SIGNING_OBJECT.sign(PRIVATE_KEY, 'sha1', '1.0') }.should raise_error
125
+ expect { LONG_SIGNING_OBJECT.sign(PRIVATE_KEY, 'sha1', '1.0') }.to raise_error(OpenSSL::PKey::RSAError)
113
126
  end
114
127
 
115
128
  it "should choke when signing a request with a bad version" do
116
- lambda { V1_1_SIGNING_OBJECT.sign(PRIVATE_KEY, 'sha1', 'poo') }.should raise_error
129
+ expect { V1_1_SIGNING_OBJECT.sign(PRIVATE_KEY, 'sha1', 'poo') }.to raise_error(Mixlib::Authentication::AuthenticationError)
117
130
  end
118
131
 
119
132
  it "should choke when signing a request with a bad algorithm" do
120
- lambda { V1_1_SIGNING_OBJECT.sign(PRIVATE_KEY, 'sha_poo', '1.1') }.should raise_error
133
+ expect { V1_1_SIGNING_OBJECT.sign(PRIVATE_KEY, 'sha_poo', '1.1') }.to raise_error(Mixlib::Authentication::AuthenticationError)
121
134
  end
122
135
 
123
136
  end
@@ -128,17 +141,30 @@ describe "Mixlib::Authentication::SignatureVerification" do
128
141
  @user_private_key = PRIVATE_KEY
129
142
  end
130
143
 
131
- it "should authenticate a File-containing request - Merb" do
144
+ it "should authenticate a File-containing request V1.1 - Merb" do
132
145
  request_params = MERB_REQUEST_PARAMS.clone
133
146
  request_params["file"] =
134
147
  { "size"=>MockFile.length, "content_type"=>"application/octet-stream", "filename"=>"zsh.tar.gz", "tempfile"=>MockFile.new }
135
148
 
136
149
  mock_request = MockRequest.new(PATH, request_params, MERB_HEADERS_V1_1, "")
137
- Time.should_receive(:now).at_least(:once).and_return(TIMESTAMP_OBJ)
150
+ expect(Time).to receive(:now).at_least(:once).and_return(TIMESTAMP_OBJ)
138
151
 
139
152
  service = Mixlib::Authentication::SignatureVerification.new
140
153
  res = service.authenticate_user_request(mock_request, @user_private_key)
141
- res.should_not be_nil
154
+ expect(res).not_to be_nil
155
+ end
156
+
157
+ it "should authenticate a File-containing request V1.3 SHA256 - Merb" do
158
+ request_params = MERB_REQUEST_PARAMS.clone
159
+ request_params["file"] =
160
+ { "size"=>MockFile.length, "content_type"=>"application/octet-stream", "filename"=>"zsh.tar.gz", "tempfile"=>MockFile.new }
161
+
162
+ mock_request = MockRequest.new(PATH, request_params, MERB_HEADERS_V1_3_SHA256, "")
163
+ expect(Time).to receive(:now).at_least(:once).and_return(TIMESTAMP_OBJ)
164
+
165
+ service = Mixlib::Authentication::SignatureVerification.new
166
+ res = service.authenticate_user_request(mock_request, @user_private_key)
167
+ expect(res).not_to be_nil
142
168
  end
143
169
 
144
170
  it "should authenticate a File-containing request from a v1.0 client - Passenger" do
@@ -146,29 +172,38 @@ describe "Mixlib::Authentication::SignatureVerification" do
146
172
  request_params["tarball"] = MockFile.new
147
173
 
148
174
  mock_request = MockRequest.new(PATH, request_params, PASSENGER_HEADERS_V1_0, "")
149
- Time.should_receive(:now).at_least(:once).and_return(TIMESTAMP_OBJ)
175
+ expect(Time).to receive(:now).at_least(:once).and_return(TIMESTAMP_OBJ)
150
176
 
151
177
  auth_req = Mixlib::Authentication::SignatureVerification.new
152
178
  res = auth_req.authenticate_user_request(mock_request, @user_private_key)
153
- res.should_not be_nil
179
+ expect(res).not_to be_nil
180
+ end
181
+
182
+ it "should authenticate a normal (post body) request v1.3 SHA256 - Merb" do
183
+ mock_request = MockRequest.new(PATH, MERB_REQUEST_PARAMS, MERB_HEADERS_V1_3_SHA256, BODY)
184
+ expect(Time).to receive(:now).at_least(:once).and_return(TIMESTAMP_OBJ)
185
+
186
+ service = Mixlib::Authentication::SignatureVerification.new
187
+ res = service.authenticate_user_request(mock_request, @user_private_key)
188
+ expect(res).not_to be_nil
154
189
  end
155
190
 
156
- it "should authenticate a normal (post body) request - Merb" do
191
+ it "should authenticate a normal (post body) request v1.1 - Merb" do
157
192
  mock_request = MockRequest.new(PATH, MERB_REQUEST_PARAMS, MERB_HEADERS_V1_1, BODY)
158
- Time.should_receive(:now).at_least(:once).and_return(TIMESTAMP_OBJ)
193
+ expect(Time).to receive(:now).at_least(:once).and_return(TIMESTAMP_OBJ)
159
194
 
160
195
  service = Mixlib::Authentication::SignatureVerification.new
161
196
  res = service.authenticate_user_request(mock_request, @user_private_key)
162
- res.should_not be_nil
197
+ expect(res).not_to be_nil
163
198
  end
164
199
 
165
200
  it "should authenticate a normal (post body) request from a v1.0 client - Merb" do
166
201
  mock_request = MockRequest.new(PATH, MERB_REQUEST_PARAMS, MERB_HEADERS_V1_0, BODY)
167
- Time.should_receive(:now).at_least(:once).and_return(TIMESTAMP_OBJ)
202
+ expect(Time).to receive(:now).at_least(:once).and_return(TIMESTAMP_OBJ)
168
203
 
169
204
  service = Mixlib::Authentication::SignatureVerification.new
170
205
  res = service.authenticate_user_request(mock_request, @user_private_key)
171
- res.should_not be_nil
206
+ expect(res).not_to be_nil
172
207
  end
173
208
 
174
209
  it "shouldn't authenticate if an Authorization header is missing" do
@@ -176,15 +211,16 @@ describe "Mixlib::Authentication::SignatureVerification" do
176
211
  headers.delete("HTTP_X_OPS_SIGN")
177
212
 
178
213
  mock_request = MockRequest.new(PATH, MERB_REQUEST_PARAMS, headers, BODY)
179
- Time.stub!(:now).and_return(TIMESTAMP_OBJ)
214
+ allow(Time).to receive(:now).and_return(TIMESTAMP_OBJ)
215
+ #Time.stub!(:now).and_return(TIMESTAMP_OBJ)
180
216
 
181
217
  auth_req = Mixlib::Authentication::SignatureVerification.new
182
- lambda {auth_req.authenticate_user_request(mock_request, @user_private_key)}.should raise_error(Mixlib::Authentication::AuthenticationError)
218
+ expect {auth_req.authenticate_user_request(mock_request, @user_private_key)}.to raise_error(Mixlib::Authentication::AuthenticationError)
183
219
 
184
- auth_req.should_not be_a_valid_request
185
- auth_req.should_not be_a_valid_timestamp
186
- auth_req.should_not be_a_valid_signature
187
- auth_req.should_not be_a_valid_content_hash
220
+ expect(auth_req).not_to be_a_valid_request
221
+ expect(auth_req).not_to be_a_valid_timestamp
222
+ expect(auth_req).not_to be_a_valid_signature
223
+ expect(auth_req).not_to be_a_valid_content_hash
188
224
  end
189
225
 
190
226
 
@@ -193,52 +229,67 @@ describe "Mixlib::Authentication::SignatureVerification" do
193
229
  headers["HTTP_X_OPS_CONTENT_HASH"] += "_"
194
230
 
195
231
  mock_request = MockRequest.new(PATH, MERB_REQUEST_PARAMS, headers, BODY)
196
- Time.should_receive(:now).at_least(:once).and_return(TIMESTAMP_OBJ)
232
+ expect(Time).to receive(:now).at_least(:once).and_return(TIMESTAMP_OBJ)
197
233
 
198
234
  auth_req = Mixlib::Authentication::SignatureVerification.new
199
235
  res = auth_req.authenticate_user_request(mock_request, @user_private_key)
200
- res.should be_nil
236
+ expect(res).to be_nil
201
237
 
202
- auth_req.should_not be_a_valid_request
203
- auth_req.should be_a_valid_timestamp
204
- auth_req.should be_a_valid_signature
205
- auth_req.should_not be_a_valid_content_hash
238
+ expect(auth_req).not_to be_a_valid_request
239
+ expect(auth_req).to be_a_valid_timestamp
240
+ expect(auth_req).to be_a_valid_signature
241
+ expect(auth_req).not_to be_a_valid_content_hash
206
242
  end
207
243
 
208
244
  it "shouldn't authenticate if the timestamp is not within bounds" do
209
245
  mock_request = MockRequest.new(PATH, MERB_REQUEST_PARAMS, MERB_HEADERS_V1_1, BODY)
210
- Time.should_receive(:now).at_least(:once).and_return(TIMESTAMP_OBJ - 1000)
246
+ expect(Time).to receive(:now).at_least(:once).and_return(TIMESTAMP_OBJ - 1000)
211
247
 
212
248
  auth_req = Mixlib::Authentication::SignatureVerification.new
213
249
  res = auth_req.authenticate_user_request(mock_request, @user_private_key)
214
- res.should be_nil
215
- auth_req.should_not be_a_valid_request
216
- auth_req.should_not be_a_valid_timestamp
217
- auth_req.should be_a_valid_signature
218
- auth_req.should be_a_valid_content_hash
250
+ expect(res).to be_nil
251
+ expect(auth_req).not_to be_a_valid_request
252
+ expect(auth_req).not_to be_a_valid_timestamp
253
+ expect(auth_req).to be_a_valid_signature
254
+ expect(auth_req).to be_a_valid_content_hash
219
255
  end
220
256
 
221
257
  it "shouldn't authenticate if the signature is wrong" do
222
258
  headers = MERB_HEADERS_V1_1.dup
223
259
  headers["HTTP_X_OPS_AUTHORIZATION_1"] = "epicfail"
224
260
  mock_request = MockRequest.new(PATH, MERB_REQUEST_PARAMS, headers, BODY)
225
- Time.should_receive(:now).at_least(:once).and_return(TIMESTAMP_OBJ)
261
+ expect(Time).to receive(:now).at_least(:once).and_return(TIMESTAMP_OBJ)
226
262
 
227
263
  auth_req = Mixlib::Authentication::SignatureVerification.new
228
264
  res = auth_req.authenticate_user_request(mock_request, @user_private_key)
229
- res.should be_nil
230
- auth_req.should_not be_a_valid_request
231
- auth_req.should_not be_a_valid_signature
232
- auth_req.should be_a_valid_timestamp
233
- auth_req.should be_a_valid_content_hash
265
+ expect(res).to be_nil
266
+ expect(auth_req).not_to be_a_valid_request
267
+ expect(auth_req).not_to be_a_valid_signature
268
+ expect(auth_req).to be_a_valid_timestamp
269
+ expect(auth_req).to be_a_valid_content_hash
234
270
  end
235
271
 
272
+ it "shouldn't authenticate if the signature is wrong for v1.3 SHA256" do
273
+ headers = MERB_HEADERS_V1_3_SHA256.dup
274
+ headers["HTTP_X_OPS_AUTHORIZATION_1"] = "epicfail"
275
+ mock_request = MockRequest.new(PATH, MERB_REQUEST_PARAMS, headers, BODY)
276
+ expect(Time).to receive(:now).at_least(:once).and_return(TIMESTAMP_OBJ)
277
+
278
+ auth_req = Mixlib::Authentication::SignatureVerification.new
279
+ res = auth_req.authenticate_user_request(mock_request, @user_private_key)
280
+ expect(res).to be_nil
281
+ expect(auth_req).not_to be_a_valid_request
282
+ expect(auth_req).not_to be_a_valid_signature
283
+ expect(auth_req).to be_a_valid_timestamp
284
+ expect(auth_req).to be_a_valid_content_hash
285
+ end
236
286
  end
237
287
 
238
288
  USER_ID = "spec-user"
239
289
  DIGESTED_USER_ID = Base64.encode64(Digest::SHA1.new.digest(USER_ID)).chomp
240
290
  BODY = "Spec Body"
241
291
  HASHED_BODY = "DFteJZPVv6WKdQmMqZUQUumUyRs=" # Base64.encode64(Digest::SHA1.digest("Spec Body")).chomp
292
+ HASHED_BODY_SHA256 = "hDlKNZhIhgso3Fs0S0pZwJ0xyBWtR1RBaeHs1DrzOho="
242
293
  TIMESTAMP_ISO8601 = "2009-01-01T12:00:00Z"
243
294
  TIMESTAMP_OBJ = Time.parse("Thu Jan 01 12:00:00 -0000 2009")
244
295
  PATH = "/organizations/clownco"
@@ -263,6 +314,20 @@ V1_1_ARGS = {
263
314
  :proto_version => 1.1
264
315
  }
265
316
 
317
+ V1_3_ARGS_SHA256 = {
318
+ :body => BODY,
319
+ :user_id => USER_ID,
320
+ :http_method => :post,
321
+ :timestamp => TIMESTAMP_ISO8601, # fixed timestamp so we get back the same answer each time.
322
+ :file => MockFile.new,
323
+ :path => PATH,
324
+ :proto_version => '1.3',
325
+ :headers => {
326
+ 'X-OpS-SeRvEr-ApI-VerSiOn' => '1'
327
+ }
328
+ # This defaults to sha256
329
+ }
330
+
266
331
  LONG_PATH_LONG_USER_ARGS = {
267
332
  :body => BODY,
268
333
  :user_id => "A" * 200,
@@ -276,6 +341,8 @@ REQUESTING_ACTOR_ID = "c0f8a68c52bffa1020222a56b23cccfa"
276
341
 
277
342
  # Content hash is ???TODO
278
343
  X_OPS_CONTENT_HASH = "DFteJZPVv6WKdQmMqZUQUumUyRs="
344
+ X_OPS_CONTENT_HASH_SHA256 = "hDlKNZhIhgso3Fs0S0pZwJ0xyBWtR1RBaeHs1DrzOho="
345
+
279
346
  X_OPS_AUTHORIZATION_LINES_V1_0 = [
280
347
  "jVHrNniWzpbez/eGWjFnO6lINRIuKOg40ZTIQudcFe47Z9e/HvrszfVXlKG4",
281
348
  "NMzYZgyooSvU85qkIUmKuCqgG2AIlvYa2Q/2ctrMhoaHhLOCWWoqYNMaEqPc",
@@ -294,6 +361,14 @@ X_OPS_AUTHORIZATION_LINES = [
294
361
  "FDlbAG7H8Dmvo+wBxmtNkszhzbBnEYtuwQqT8nM/8A=="
295
362
  ]
296
363
 
364
+ X_OPS_AUTHORIZATION_LINES_V1_3_SHA256 = [
365
+ "FZOmXAyOBAZQV/uw188iBljBJXOm+m8xQ/8KTGLkgGwZNcRFxk1m953XjE3W",
366
+ "VGy1dFT76KeaNWmPCNtDmprfH2na5UZFtfLIKrPv7xm80V+lzEzTd9WBwsfP",
367
+ "42dZ9N+V9I5SVfcL/lWrrlpdybfceJC5jOcP5tzfJXWUITwb6Z3Erg3DU3Uh",
368
+ "H9h9E0qWlYGqmiNCVrBnpe6Si1gU/Jl+rXlRSNbLJ4GlArAPuL976iTYJTzE",
369
+ "MmbLUIm3JRYi00Yb01IUCCKdI90vUq1HHNtlTEu93YZfQaJwRxXlGkCNwIJe",
370
+ "fy49QzaCIEu1XiOx5Jn+4GmkrZch/RrK9VzQWXgs+w=="
371
+ ]
297
372
  # We expect Mixlib::Authentication::SignedHeaderAuth#sign to return this
298
373
  # if passed the BODY above, based on version
299
374
 
@@ -323,6 +398,19 @@ EXPECTED_SIGN_RESULT_V1_1 = {
323
398
  "X-Ops-Timestamp"=>TIMESTAMP_ISO8601
324
399
  }
325
400
 
401
+ EXPECTED_SIGN_RESULT_V1_3_SHA256 = {
402
+ "X-Ops-Content-Hash"=>X_OPS_CONTENT_HASH_SHA256,
403
+ "X-Ops-Userid"=>USER_ID,
404
+ "X-Ops-Sign"=>"algorithm=sha256;version=1.3;",
405
+ "X-Ops-Authorization-1"=>X_OPS_AUTHORIZATION_LINES_V1_3_SHA256[0],
406
+ "X-Ops-Authorization-2"=>X_OPS_AUTHORIZATION_LINES_V1_3_SHA256[1],
407
+ "X-Ops-Authorization-3"=>X_OPS_AUTHORIZATION_LINES_V1_3_SHA256[2],
408
+ "X-Ops-Authorization-4"=>X_OPS_AUTHORIZATION_LINES_V1_3_SHA256[3],
409
+ "X-Ops-Authorization-5"=>X_OPS_AUTHORIZATION_LINES_V1_3_SHA256[4],
410
+ "X-Ops-Authorization-6"=>X_OPS_AUTHORIZATION_LINES_V1_3_SHA256[5],
411
+ "X-Ops-Timestamp"=>TIMESTAMP_ISO8601
412
+ }
413
+
326
414
  OTHER_HEADERS = {
327
415
  # An arbitrary sampling of non-HTTP_* headers are in here to
328
416
  # exercise that code path.
@@ -339,6 +427,23 @@ MERB_REQUEST_PARAMS = {
339
427
  "organization_id"=>"local-test-org", "requesting_actor_id"=>REQUESTING_ACTOR_ID,
340
428
  }
341
429
 
430
+ MERB_HEADERS_V1_3_SHA256 = {
431
+ # These are used by signatureverification.
432
+ "HTTP_HOST"=>"127.0.0.1",
433
+ "HTTP_X_OPS_SIGN"=>"algorithm=sha256;version=1.3;",
434
+ "HTTP_X_OPS_REQUESTID"=>"127.0.0.1 1258566194.85386",
435
+ "HTTP_X_OPS_TIMESTAMP"=>TIMESTAMP_ISO8601,
436
+ "HTTP_X_OPS_CONTENT_HASH"=>X_OPS_CONTENT_HASH_SHA256,
437
+ "HTTP_X_OPS_USERID"=>USER_ID,
438
+ "HTTP_X_OPS_SERVER_API_VERSION"=>"1",
439
+ "HTTP_X_OPS_AUTHORIZATION_1"=>X_OPS_AUTHORIZATION_LINES_V1_3_SHA256[0],
440
+ "HTTP_X_OPS_AUTHORIZATION_2"=>X_OPS_AUTHORIZATION_LINES_V1_3_SHA256[1],
441
+ "HTTP_X_OPS_AUTHORIZATION_3"=>X_OPS_AUTHORIZATION_LINES_V1_3_SHA256[2],
442
+ "HTTP_X_OPS_AUTHORIZATION_4"=>X_OPS_AUTHORIZATION_LINES_V1_3_SHA256[3],
443
+ "HTTP_X_OPS_AUTHORIZATION_5"=>X_OPS_AUTHORIZATION_LINES_V1_3_SHA256[4],
444
+ "HTTP_X_OPS_AUTHORIZATION_6"=>X_OPS_AUTHORIZATION_LINES_V1_3_SHA256[5],
445
+ }.merge(OTHER_HEADERS)
446
+
342
447
  # Tis is what will be in request.env for the Merb case.
343
448
  MERB_HEADERS_V1_1 = {
344
449
  # These are used by signatureverification.
@@ -477,6 +582,18 @@ X-Ops-UserId:#{DIGESTED_USER_ID}
477
582
  EOS
478
583
  V1_1_CANONICAL_REQUEST = V1_1_CANONICAL_REQUEST_DATA.chomp
479
584
 
585
+ V1_3_SHA256_CANONICAL_REQUEST_DATA = <<EOS
586
+ Method:POST
587
+ Path:#{PATH}
588
+ X-Ops-Content-Hash:#{HASHED_BODY_SHA256}
589
+ X-Ops-Sign:version=1.3
590
+ X-Ops-Timestamp:#{TIMESTAMP_ISO8601}
591
+ X-Ops-UserId:#{USER_ID}
592
+ X-Ops-Server-API-Version:1
593
+ EOS
594
+ V1_3_SHA256_CANONICAL_REQUEST = V1_3_SHA256_CANONICAL_REQUEST_DATA.chomp
595
+
596
+ V1_3_SHA256_SIGNING_OBJECT = Mixlib::Authentication::SignedHeaderAuth.signing_object(V1_3_ARGS_SHA256)
480
597
  V1_1_SIGNING_OBJECT = Mixlib::Authentication::SignedHeaderAuth.signing_object(V1_1_ARGS)
481
598
  V1_0_SIGNING_OBJECT = Mixlib::Authentication::SignedHeaderAuth.signing_object(V1_0_ARGS)
482
599
  LONG_SIGNING_OBJECT = Mixlib::Authentication::SignedHeaderAuth.signing_object(LONG_PATH_LONG_USER_ARGS)
metadata CHANGED
@@ -1,91 +1,131 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: mixlib-authentication
3
- version: !ruby/object:Gem::Version
4
- hash: 27
5
- prerelease:
6
- segments:
7
- - 1
8
- - 3
9
- - 0
10
- version: 1.3.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.4.0.rc.0
11
5
  platform: ruby
12
- authors:
6
+ authors:
13
7
  - Opscode, Inc.
14
8
  autorequire:
15
9
  bindir: bin
16
10
  cert_chain: []
17
-
18
- date: 2012-08-06 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
11
+ date: 2015-12-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
21
14
  name: mixlib-log
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
22
21
  prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
- none: false
25
- requirements:
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
26
24
  - - ">="
27
- - !ruby/object:Gem::Version
28
- hash: 3
29
- segments:
30
- - 0
31
- version: "0"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec-core
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.2'
34
+ type: :runtime
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-expectations
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.2'
48
+ type: :runtime
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: rspec-mocks
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.2'
32
62
  type: :runtime
33
- version_requirements: *id001
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.4'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.4'
34
83
  description: Mixes in simple per-request authentication
35
84
  email: info@opscode.com
36
85
  executables: []
37
-
38
86
  extensions: []
39
-
40
- extra_rdoc_files:
87
+ extra_rdoc_files:
41
88
  - README.rdoc
42
89
  - LICENSE
43
90
  - NOTICE
44
- files:
91
+ files:
92
+ - Gemfile
45
93
  - LICENSE
94
+ - NOTICE
46
95
  - README.rdoc
47
96
  - Rakefile
48
- - NOTICE
49
- - lib/mixlib/authentication/signedheaderauth.rb
97
+ - lib/mixlib/authentication.rb
50
98
  - lib/mixlib/authentication/digester.rb
51
99
  - lib/mixlib/authentication/http_authentication_request.rb
52
100
  - lib/mixlib/authentication/signatureverification.rb
53
- - lib/mixlib/authentication.rb
54
- - spec/spec_helper.rb
55
- - spec/mixlib/authentication/mixlib_authentication_spec.rb
101
+ - lib/mixlib/authentication/signedheaderauth.rb
102
+ - lib/mixlib/authentication/version.rb
103
+ - mixlib-authentication.gemspec
56
104
  - spec/mixlib/authentication/http_authentication_request_spec.rb
105
+ - spec/mixlib/authentication/mixlib_authentication_spec.rb
106
+ - spec/spec_helper.rb
57
107
  homepage: http://www.opscode.com
58
108
  licenses: []
59
-
109
+ metadata: {}
60
110
  post_install_message:
61
111
  rdoc_options: []
62
-
63
- require_paths:
112
+ require_paths:
64
113
  - lib
65
- required_ruby_version: !ruby/object:Gem::Requirement
66
- none: false
67
- requirements:
68
- - - ">="
69
- - !ruby/object:Gem::Version
70
- hash: 3
71
- segments:
72
- - 0
73
- version: "0"
74
- required_rubygems_version: !ruby/object:Gem::Requirement
75
- none: false
76
- requirements:
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
77
116
  - - ">="
78
- - !ruby/object:Gem::Version
79
- hash: 3
80
- segments:
81
- - 0
82
- version: "0"
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">"
122
+ - !ruby/object:Gem::Version
123
+ version: 1.3.1
83
124
  requirements: []
84
-
85
125
  rubyforge_project:
86
- rubygems_version: 1.8.15
126
+ rubygems_version: 2.5.0
87
127
  signing_key:
88
- specification_version: 3
128
+ specification_version: 4
89
129
  summary: Mixes in simple per-request authentication
90
130
  test_files: []
91
-
131
+ has_rdoc: true