carrierwave_direct 1.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,7 +18,7 @@ module CarrierWaveDirect
18
18
  options[:filename] = filename_parts.join(".")
19
19
  end
20
20
  options[:filename] ||= "filename"
21
- valid_extension = uploader.extension_white_list.first if uploader.extension_white_list
21
+ valid_extension = uploader.extension_whitelist.first if uploader.extension_whitelist
22
22
  options[:extension] = options[:extension] ? options[:extension].gsub(".", "") : (valid_extension || "extension")
23
23
  key = options[:base].split("/")
24
24
  key.pop
@@ -2,7 +2,8 @@
2
2
 
3
3
  require "securerandom"
4
4
  require "carrierwave_direct/uploader/content_type"
5
- require "carrierwave_direct/uploader/direct_url"
5
+ require "carrierwave_direct/policies/aws_base64_sha1"
6
+ require "carrierwave_direct/policies/aws4_hmac_sha256"
6
7
 
7
8
  module CarrierWaveDirect
8
9
  module Uploader
@@ -24,7 +25,6 @@ module CarrierWaveDirect
24
25
  end
25
26
 
26
27
  include CarrierWaveDirect::Uploader::ContentType
27
- include CarrierWaveDirect::Uploader::DirectUrl
28
28
 
29
29
  #ensure that region returns something. Since sig v4 it is required in the signing key & credentials
30
30
  def region
@@ -36,40 +36,27 @@ module CarrierWaveDirect
36
36
  end
37
37
 
38
38
  def policy(options = {}, &block)
39
- options[:expiration] ||= upload_expiration
40
- options[:min_file_size] ||= min_file_size
41
- options[:max_file_size] ||= max_file_size
42
-
43
- @date ||= Time.now.utc.strftime("%Y%m%d")
44
- @timestamp ||= Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
45
- @policy ||= generate_policy(options, &block)
39
+ signing_policy.policy(options, &block)
46
40
  end
47
41
 
48
42
  def date
49
- @timestamp ||= Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
43
+ signing_policy.date
50
44
  end
51
45
 
52
46
  def algorithm
53
- 'AWS4-HMAC-SHA256'
47
+ signing_policy.algorithm
54
48
  end
55
49
 
56
50
  def credential
57
- @date ||= Time.now.utc.strftime("%Y%m%d")
58
- "#{aws_access_key_id}/#{@date}/#{region}/s3/aws4_request"
51
+ signing_policy.credential
59
52
  end
60
53
 
61
54
  def clear_policy!
62
- @policy = nil
63
- @date = nil
64
- @timestamp = nil
55
+ signing_policy.clear!
65
56
  end
66
57
 
67
58
  def signature
68
- OpenSSL::HMAC.hexdigest(
69
- 'sha256',
70
- signing_key,
71
- policy
72
- )
59
+ signing_policy.signature
73
60
  end
74
61
 
75
62
  def url_scheme_white_list
@@ -80,13 +67,22 @@ module CarrierWaveDirect
80
67
  false
81
68
  end
82
69
 
70
+ def signing_policy_class
71
+ @signing_policy_class ||= Policies::Aws4HmacSha256
72
+ end
73
+
74
+ def signing_policy_class=(signing_policy_class)
75
+ @signing_policy_class = signing_policy_class
76
+ end
77
+
83
78
  def key
84
79
  return @key if @key.present?
85
80
  if present?
86
81
  identifier = model.send("#{mounted_as}_identifier")
87
- self.key = "#{store_dir}/#{identifier}"
82
+ self.key = [store_dir, identifier].join("/")
88
83
  else
89
- @key = "#{store_dir}/#{guid}/#{FILENAME_WILDCARD}"
84
+ guid = SecureRandom.uuid
85
+ @key = [store_dir, guid, FILENAME_WILDCARD].join("/")
90
86
  end
91
87
  @key
92
88
  end
@@ -96,10 +92,6 @@ module CarrierWaveDirect
96
92
  update_version_keys(:with => @key)
97
93
  end
98
94
 
99
- def guid
100
- SecureRandom.uuid
101
- end
102
-
103
95
  def has_key?
104
96
  key !~ /#{Regexp.escape(FILENAME_WILDCARD)}\z/
105
97
  end
@@ -109,7 +101,7 @@ module CarrierWaveDirect
109
101
  end
110
102
 
111
103
  def extension_regexp
112
- allowed_file_types = extension_white_list
104
+ allowed_file_types = extension_whitelist
113
105
  extension_regexp = allowed_file_types.present? && allowed_file_types.any? ? "(#{allowed_file_types.join("|")})" : "\\w+"
114
106
  end
115
107
 
@@ -117,27 +109,37 @@ module CarrierWaveDirect
117
109
  unless has_key?
118
110
  # Use the attached models remote url to generate a new key otherwise return nil
119
111
  remote_url = model.send("remote_#{mounted_as}_url")
120
- remote_url ? key_from_file(CarrierWave::SanitizedFile.new(remote_url).filename) : return
112
+ if remote_url
113
+ key_from_file(CarrierWave::SanitizedFile.new(remote_url).filename)
114
+ else
115
+ return
116
+ end
121
117
  end
122
118
 
123
- key_path = key.split("/")
119
+ key_parts = key.split("/")
120
+ filename = key_parts.pop
121
+ guid = key_parts.pop
122
+
124
123
  filename_parts = []
125
- filename_parts.unshift(key_path.pop)
126
- unique_key = key_path.pop
127
- filename_parts.unshift(unique_key) if unique_key
124
+ filename_parts << guid if guid
125
+ filename_parts << filename
128
126
  filename_parts.join("/")
129
127
  end
130
128
 
131
- private
129
+ def direct_fog_url
130
+ CarrierWave::Storage::Fog::File.new(self, CarrierWave::Storage::Fog.new(self), nil).public_url
131
+ end
132
132
 
133
- def decoded_key
134
- URI.decode(URI.parse(url).path[1 .. -1])
133
+ def direct_fog_hash(policy_options = {})
134
+ signing_policy.direct_fog_hash(policy_options)
135
135
  end
136
136
 
137
- def key_from_file(fname)
137
+ private
138
+
139
+ def key_from_file(filename)
138
140
  new_key_parts = key.split("/")
139
141
  new_key_parts.pop
140
- new_key_parts << fname
142
+ new_key_parts << filename
141
143
  self.key = new_key_parts.join("/")
142
144
  end
143
145
 
@@ -155,45 +157,8 @@ module CarrierWaveDirect
155
157
  [for_file.chomp(extname), version_name].compact.join('_') << extname
156
158
  end
157
159
 
158
- def generate_policy(options)
159
- conditions = []
160
-
161
- conditions << ["starts-with", "$utf8", ""] if options[:enforce_utf8]
162
- conditions << ["starts-with", "$key", key.sub(/#{Regexp.escape(FILENAME_WILDCARD)}\z/, "")]
163
- conditions << {'X-Amz-Algorithm' => algorithm}
164
- conditions << {'X-Amz-Credential' => credential}
165
- conditions << {'X-Amz-Date' => date}
166
- conditions << ["starts-with", "$Content-Type", ""] if will_include_content_type
167
- conditions << {"bucket" => fog_directory}
168
- conditions << {"acl" => acl}
169
-
170
- if use_action_status
171
- conditions << {"success_action_status" => success_action_status}
172
- else
173
- conditions << {"success_action_redirect" => success_action_redirect}
174
- end
175
-
176
- conditions << ["content-length-range", options[:min_file_size], options[:max_file_size]]
177
-
178
- yield conditions if block_given?
179
-
180
- Base64.encode64(
181
- {
182
- 'expiration' => (Time.now + options[:expiration]).utc.iso8601,
183
- 'conditions' => conditions
184
- }.to_json
185
- ).gsub("\n","")
186
- end
187
-
188
- def signing_key(options = {})
189
- @date ||= Time.now.utc.strftime("%Y%m%d")
190
- #AWS Signature Version 4
191
- kDate = OpenSSL::HMAC.digest('sha256', "AWS4" + aws_secret_access_key, @date)
192
- kRegion = OpenSSL::HMAC.digest('sha256', kDate, region)
193
- kService = OpenSSL::HMAC.digest('sha256', kRegion, 's3')
194
- kSigning = OpenSSL::HMAC.digest('sha256', kService, "aws4_request")
195
-
196
- kSigning
160
+ def signing_policy
161
+ @signing_policy ||= signing_policy_class.new(self)
197
162
  end
198
163
  end
199
164
  end
@@ -23,7 +23,7 @@ module CarrierWaveDirect
23
23
  class FilenameFormatValidator < ::ActiveModel::EachValidator
24
24
  def validate_each(record, attribute, value)
25
25
  if record.send("has_#{attribute}_upload?") && record.send("#{attribute}_key") !~ record.send(attribute).key_regexp
26
- extensions = record.send(attribute).extension_white_list
26
+ extensions = record.send(attribute).extension_whitelist
27
27
  message = I18n.t("errors.messages.carrierwave_direct_filename_invalid")
28
28
 
29
29
  if extensions.present?
@@ -43,7 +43,7 @@ module CarrierWaveDirect
43
43
  url_scheme_white_list = uploader.url_scheme_white_list
44
44
 
45
45
  if (remote_net_url !~ URI.regexp(url_scheme_white_list) || remote_net_url !~ /#{uploader.extension_regexp}\z/)
46
- extensions = uploader.extension_white_list
46
+ extensions = uploader.extension_whitelist
47
47
 
48
48
  message = I18n.t("errors.messages.carrierwave_direct_filename_invalid")
49
49
 
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module CarrierwaveDirect
4
- VERSION = "1.0.0"
4
+ VERSION = "2.1.0"
5
5
  end
6
6
 
@@ -30,13 +30,14 @@ shared_examples_for 'hidden values form' do
30
30
  end
31
31
 
32
32
  it "should have a hidden field for '#{name}'" do
33
+ allow(direct_uploader.send(:signing_policy)).to receive(key).and_return(key.to_s)
33
34
  allow(direct_uploader).to receive(key).and_return(key.to_s)
34
35
  expect(subject).to have_input(
35
36
  :direct_uploader,
36
37
  key,
37
38
  :type => :hidden,
38
39
  :name => name,
39
- :value => key,
40
+ :value => direct_uploader.send(key),
40
41
  :required => false
41
42
  )
42
43
  end
@@ -83,7 +84,7 @@ describe CarrierWaveDirect::FormBuilder do
83
84
 
84
85
  # http://aws.amazon.com/articles/1434?_encoding=UTF8
85
86
  context "form" do
86
- let(:subject) {form_with_default_file_field}
87
+ subject { form_with_default_file_field }
87
88
  it_should_behave_like 'hidden values form'
88
89
 
89
90
  default_hidden_fields.each do |input|
@@ -95,13 +96,14 @@ describe CarrierWaveDirect::FormBuilder do
95
96
  end
96
97
 
97
98
  it "should have a hidden field for '#{name}'" do
99
+ allow(direct_uploader.send(:signing_policy)).to receive(key).and_return(key.to_s)
98
100
  allow(direct_uploader).to receive(key).and_return(key.to_s)
99
- expect(form_with_default_file_field).to have_input(
101
+ expect(subject).to have_input(
100
102
  :direct_uploader,
101
103
  key,
102
104
  :type => :hidden,
103
105
  :name => name,
104
- :value => key,
106
+ :value => direct_uploader.send(key),
105
107
  :required => false
106
108
  )
107
109
  end
@@ -116,20 +118,21 @@ describe CarrierWaveDirect::FormBuilder do
116
118
  end
117
119
 
118
120
  it "should have a hidden field for '#{name}'" do
121
+ allow(direct_uploader.send(:signing_policy)).to receive(key).and_return(key.to_s)
119
122
  allow(direct_uploader).to receive(key).and_return(key.to_s)
120
123
  expect(form_with_file_field_and_no_redirect).to have_input(
121
124
  :direct_uploader,
122
125
  key,
123
126
  :type => :hidden,
124
127
  :name => name,
125
- :value => key,
128
+ :value => direct_uploader.send(key),
126
129
  :required => false
127
130
  )
128
131
  end
129
132
  end
130
133
 
131
134
  it "should have an input for a file to upload" do
132
- expect(form_with_default_file_field).to have_input(
135
+ expect(subject).to have_input(
133
136
  :direct_uploader,
134
137
  :video,
135
138
  :type => :file,
@@ -142,7 +145,7 @@ describe CarrierWaveDirect::FormBuilder do
142
145
 
143
146
  describe "#content_type_select" do
144
147
  context "form" do
145
- let(:subject) do
148
+ subject do
146
149
  form do |f|
147
150
  f.content_type_select
148
151
  end
@@ -178,7 +181,7 @@ describe CarrierWaveDirect::FormBuilder do
178
181
 
179
182
  describe "#content_type_label" do
180
183
  context "form" do
181
- let(:subject) do
184
+ subject do
182
185
  form do |f|
183
186
  f.content_type_label
184
187
  end
data/spec/mount_spec.rb CHANGED
@@ -18,12 +18,12 @@ describe CarrierWaveDirect::Mount do
18
18
 
19
19
  describe "#has_video_upload?" do
20
20
  it "returns false when video does not have a key" do
21
- allow(subject.video).to receive(:has_key?).and_return(false)
21
+ subject.video.key = nil
22
22
  expect(subject).to_not have_video_upload
23
23
  end
24
24
 
25
25
  it "returns true when video has a key" do
26
- allow(subject.video).to receive(:has_key?).and_return(true)
26
+ subject.video.key = sample(:s3_key)
27
27
  expect(subject).to have_video_upload
28
28
  end
29
29
  end
@@ -104,8 +104,8 @@ describe CarrierWaveDirect::ActiveRecord do
104
104
  messages = I18n.t("errors.messages.carrierwave_direct_filename_invalid")
105
105
 
106
106
  if i18n_options
107
- if i18n_options[:extension_white_list]
108
- extensions = i18n_options[:extension_white_list].to_sentence
107
+ if i18n_options[:extension_whitelist]
108
+ extensions = i18n_options[:extension_whitelist].to_sentence
109
109
  messages += I18n.t("errors.messages.carrierwave_direct_allowed_extensions", :extensions => extensions)
110
110
  end
111
111
 
@@ -244,7 +244,7 @@ describe CarrierWaveDirect::ActiveRecord do
244
244
 
245
245
  context "where the uploader has an extension white list" do
246
246
  before do
247
- subject.video.stub(:extension_white_list).and_return(%w{avi mp4})
247
+ subject.video.stub(:extension_whitelist).and_return(%w{avi mp4})
248
248
  end
249
249
 
250
250
  context "and the uploaded file's extension is included in the list" do
@@ -302,7 +302,7 @@ describe CarrierWaveDirect::ActiveRecord do
302
302
  context "on create" do
303
303
  context "where the uploader has an extension white list" do
304
304
  before do
305
- subject.video.stub(:extension_white_list).and_return(%w{avi mp4})
305
+ subject.video.stub(:extension_whitelist).and_return(%w{avi mp4})
306
306
  end
307
307
 
308
308
  context "and the url's extension is included in the list" do
@@ -325,7 +325,7 @@ describe CarrierWaveDirect::ActiveRecord do
325
325
  end
326
326
 
327
327
  it_should_behave_like "a remote net url i18n error message" do
328
- let(:i18n_options) { {:extension_white_list => %w{avi mp4} } }
328
+ let(:i18n_options) { {:extension_whitelist => %w{avi mp4} } }
329
329
  end
330
330
 
331
331
  it "should include the white listed extensions in the error message" do
@@ -0,0 +1,243 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ require 'data/sample_data'
4
+
5
+ describe CarrierWaveDirect::Policies::Aws4HmacSha256 do
6
+ let(:subject) { described_class.new(uploader) }
7
+ let(:uploader) { DirectUploader.new }
8
+ let(:mounted_model) { MountedClass.new }
9
+ let(:mounted_subject) { DirectUploader.new(mounted_model, sample(:mounted_as)) }
10
+
11
+ describe "#direct_fog_hash" do
12
+ it "should return the policy hash" do
13
+ expect(subject.direct_fog_hash.keys).to eq([:key, :acl, :policy, :"X-Amz-Signature", :"X-Amz-Credential", :"X-Amz-Algorithm", :"X-Amz-Date", :uri])
14
+ expect(subject.direct_fog_hash[:acl]).to eq 'public-read'
15
+ expect(subject.direct_fog_hash[:key]).to match /\$\{filename\}/
16
+ expect(subject.direct_fog_hash[:"X-Amz-Algorithm"]).to eq "AWS4-HMAC-SHA256"
17
+ expect(subject.direct_fog_hash[:uri]).to eq "https://s3.amazonaws.com/AWS_FOG_DIRECTORY/"
18
+ end
19
+ end
20
+
21
+ # http://aws.amazon.com/articles/1434?_encoding=UTF8
22
+ #http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-UsingHTTPPOST.html
23
+ describe "#policy" do
24
+ def decoded_policy(options = {}, &block)
25
+ instance = options.delete(:subject) || subject
26
+ JSON.parse(Base64.decode64(instance.policy(options, &block)))
27
+ end
28
+
29
+ context "policy is given a block" do
30
+ it "should yield the options to the block" do
31
+ number = 0
32
+ subject.policy do |conditions|
33
+ number+=1
34
+ end
35
+ expect(number).to eq 1
36
+ end
37
+ it "should include new options in the conditions" do
38
+ policy = subject.policy do |conditions|
39
+ conditions << {"x-aws-storage-class" => "STANDARD"}
40
+ end
41
+ decoded = JSON.parse(Base64.decode64(policy))
42
+ expect(decoded['conditions'].last['x-aws-storage-class']).to eq "STANDARD"
43
+ end
44
+ end
45
+
46
+ it "should return Base64-encoded JSON" do
47
+ expect(decoded_policy).to be_a(Hash)
48
+ end
49
+
50
+ it "should not contain any new lines" do
51
+ expect(subject.policy).to_not include("\n")
52
+ end
53
+
54
+ it "should be cached" do
55
+ Timecop.freeze(Time.now) do
56
+ @policy_now = subject.policy
57
+ end
58
+ Timecop.freeze(1.second.from_now) do
59
+ @policy_later = subject.policy
60
+ end
61
+ expect(@policy_later).to eql @policy_now
62
+ end
63
+
64
+ context "expiration" do
65
+ def expiration(options = {})
66
+ decoded_policy(options)["expiration"]
67
+ end
68
+
69
+ # JSON times have no seconds, so accept upto one second inaccuracy
70
+ def have_expiration(expires_in = DirectUploader.upload_expiration)
71
+ be_within(1.second).of (Time.now + expires_in)
72
+ end
73
+
74
+ it "should be valid ISO8601 and not use default Time#to_json" do
75
+ Time.any_instance.stub(:to_json) { '"Invalid time"' } # JSON gem
76
+ Time.any_instance.stub(:as_json) { '"Invalid time"' } # Active Support
77
+ expect { Time.iso8601(expiration) }.to_not raise_error
78
+ end
79
+
80
+ it "should be #{DirectUploader.upload_expiration / 3600} hours from now" do
81
+ Timecop.freeze(Time.now) do
82
+ expect(Time.parse(expiration)).to have_expiration
83
+ end
84
+ end
85
+
86
+ it "should be encoded as a utc time" do
87
+ expect(Time.parse(expiration)).to be_utc
88
+ end
89
+
90
+ it "should be #{sample(:expiration) / 60 } minutes from now when passing {:expiration => #{sample(:expiration)}}" do
91
+ Timecop.freeze(Time.now) do
92
+ expect(Time.parse(expiration(:expiration => sample(:expiration)))).to have_expiration(sample(:expiration))
93
+ end
94
+ end
95
+ end
96
+
97
+ context "conditions" do
98
+ def conditions(options = {})
99
+ decoded_policy(options)["conditions"]
100
+ end
101
+
102
+ def have_condition(field, value = nil)
103
+ field.is_a?(Hash) ? include(field) : include(["starts-with", "$#{field}", value.to_s])
104
+ end
105
+
106
+ context "should include" do
107
+ it "'utf8' if enforce_ut8 is set" do
108
+ expect(conditions(enforce_utf8: true)).to have_condition(:utf8)
109
+ end
110
+
111
+ it "'utf8' if enforce_ut8 is set" do
112
+ expect(conditions).to_not have_condition(:utf8)
113
+ end
114
+
115
+ # S3 conditions
116
+ it "'key'" do
117
+ allow(mounted_subject).to receive(:key).and_return(sample(:s3_key))
118
+ expect(conditions(
119
+ :subject => mounted_subject
120
+ )).to have_condition(:key, sample(:s3_key))
121
+ end
122
+
123
+ it "'key' without FILENAME_WILDCARD" do
124
+ expect(conditions(
125
+ :subject => mounted_subject
126
+ )).to have_condition(:key, mounted_subject.key.sub("${filename}", ""))
127
+ end
128
+
129
+ it "'bucket'" do
130
+ expect(conditions).to have_condition("bucket" => uploader.fog_directory)
131
+ end
132
+
133
+ it "'acl'" do
134
+ expect(conditions).to have_condition("acl" => uploader.acl)
135
+ end
136
+
137
+ it "'success_action_redirect'" do
138
+ uploader.success_action_redirect = "http://example.com/some_url"
139
+ expect(conditions).to have_condition("success_action_redirect" => "http://example.com/some_url")
140
+ end
141
+
142
+ it "does not have 'content-type' when will_include_content_type is false" do
143
+ allow(uploader.class).to receive(:will_include_content_type).and_return(false)
144
+ expect(conditions).to_not have_condition('Content-Type')
145
+ end
146
+
147
+ it "has 'content-type' when will_include_content_type is true" do
148
+ allow(uploader.class).to receive(:will_include_content_type).and_return(true)
149
+ expect(conditions).to have_condition('Content-Type')
150
+ end
151
+
152
+ context 'when use_action_status is true' do
153
+ before(:all) do
154
+ DirectUploader.use_action_status = true
155
+ end
156
+
157
+ after(:all) do
158
+ DirectUploader.use_action_status = false
159
+ end
160
+
161
+ it "'success_action_status'" do
162
+ uploader.success_action_status = '200'
163
+ expect(conditions).to have_condition("success_action_status" => "200")
164
+ end
165
+
166
+ it "does not have 'success_action_redirect'" do
167
+ uploader.success_action_redirect = "http://example.com/some_url"
168
+ expect(conditions).to_not have_condition("success_action_redirect" => "http://example.com/some_url")
169
+ end
170
+ end
171
+
172
+ context "'content-length-range of'" do
173
+ def have_content_length_range(options = {})
174
+ include([
175
+ "content-length-range",
176
+ options[:min_file_size] || DirectUploader.min_file_size,
177
+ options[:max_file_size] || DirectUploader.max_file_size,
178
+ ])
179
+ end
180
+
181
+ it "#{DirectUploader.min_file_size} bytes" do
182
+ expect(conditions).to have_content_length_range
183
+ end
184
+
185
+ it "#{DirectUploader.max_file_size} bytes" do
186
+ expect(conditions).to have_content_length_range
187
+ end
188
+
189
+ it "#{sample(:min_file_size)} bytes when passing {:min_file_size => #{sample(:min_file_size)}}" do
190
+ expect(conditions(
191
+ :min_file_size => sample(:min_file_size)
192
+ )).to have_content_length_range(:min_file_size => sample(:min_file_size))
193
+ end
194
+
195
+ it "#{sample(:max_file_size)} bytes when passing {:max_file_size => #{sample(:max_file_size)}}" do
196
+ expect(conditions(
197
+ :max_file_size => sample(:max_file_size)
198
+ )).to have_content_length_range(:max_file_size => sample(:max_file_size))
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ describe "clear!" do
206
+ it "should reset the cached policy string" do
207
+ Timecop.freeze(Time.now) do
208
+ @policy_now = subject.policy
209
+ end
210
+ subject.clear!
211
+
212
+ Timecop.freeze(1.second.from_now) do
213
+ @policy_after_reset = subject.policy
214
+ end
215
+ expect(@policy_after_reset).not_to eql @policy_now
216
+ end
217
+ end
218
+
219
+ describe "#signature" do
220
+ it "should not contain any new lines" do
221
+ expect(subject.signature).to_not include("\n")
222
+ end
223
+
224
+ it "should return a HMAC hexdigest encoded 'sha256' hash of the secret key and policy document" do
225
+ expect(subject.signature).to eq OpenSSL::HMAC.hexdigest(
226
+ OpenSSL::Digest.new('sha256'),
227
+ subject.signing_key, subject.policy
228
+ )
229
+ end
230
+ end
231
+ #http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-UsingHTTPPOST.html
232
+ describe "#signature_key" do
233
+ it "should include correct signature_key elements" do
234
+ kDate = OpenSSL::HMAC.digest('sha256', "AWS4" + uploader.aws_secret_access_key, Time.now.utc.strftime("%Y%m%d"))
235
+ kRegion = OpenSSL::HMAC.digest('sha256', kDate, uploader.region)
236
+ kService = OpenSSL::HMAC.digest('sha256', kRegion, 's3')
237
+ kSigning = OpenSSL::HMAC.digest('sha256', kService, "aws4_request")
238
+
239
+ expect(subject.signing_key).to eq (kSigning)
240
+ end
241
+ end
242
+ end
243
+