mixlib-authentication 1.3.0 → 1.4.0.rc.0

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.
@@ -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