carrierwave_direct 1.0.0 → 2.1.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.
@@ -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
+