carrierwave_direct 2.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.
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module CarrierwaveDirect
4
- VERSION = "2.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
@@ -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
@@ -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
+
@@ -0,0 +1,229 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ require 'data/sample_data'
4
+
5
+ describe CarrierWaveDirect::Policies::AwsBase64Sha1 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, :AWSAccessKeyId, :acl, :policy, :signature, :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[:uri]).to eq "https://s3.amazonaws.com/AWS_FOG_DIRECTORY/"
17
+ end
18
+ end
19
+
20
+ describe "#policy" do
21
+ def decoded_policy(options = {}, &block)
22
+ instance = options.delete(:subject) || subject
23
+ JSON.parse(Base64.decode64(instance.policy(options, &block)))
24
+ end
25
+
26
+ context "policy is given a block" do
27
+ it "should yield the options to the block" do
28
+ number = 0
29
+ subject.policy do |conditions|
30
+ number+=1
31
+ end
32
+ expect(number).to eq 1
33
+ end
34
+ it "should include new options in the conditions" do
35
+ policy = subject.policy do |conditions|
36
+ conditions << {"x-aws-storage-class" => "STANDARD"}
37
+ end
38
+ decoded = JSON.parse(Base64.decode64(policy))
39
+ expect(decoded['conditions'].last['x-aws-storage-class']).to eq "STANDARD"
40
+ end
41
+ end
42
+
43
+ it "should return Base64-encoded JSON" do
44
+ expect(decoded_policy).to be_a(Hash)
45
+ end
46
+
47
+ it "should not contain any new lines" do
48
+ expect(subject.policy).to_not include("\n")
49
+ end
50
+
51
+ it "should be cached" do
52
+ Timecop.freeze(Time.now) do
53
+ @policy_now = subject.policy
54
+ end
55
+ Timecop.freeze(1.second.from_now) do
56
+ @policy_later = subject.policy
57
+ end
58
+ expect(@policy_later).to eql @policy_now
59
+ end
60
+
61
+ context "expiration" do
62
+ def expiration(options = {})
63
+ decoded_policy(options)["expiration"]
64
+ end
65
+
66
+ # JSON times have no seconds, so accept upto one second inaccuracy
67
+ def have_expiration(expires_in = DirectUploader.upload_expiration)
68
+ be_within(1.second).of (Time.now + expires_in)
69
+ end
70
+
71
+ it "should be valid ISO8601 and not use default Time#to_json" do
72
+ Time.any_instance.stub(:to_json) { '"Invalid time"' } # JSON gem
73
+ Time.any_instance.stub(:as_json) { '"Invalid time"' } # Active Support
74
+ expect { Time.iso8601(expiration) }.to_not raise_error
75
+ end
76
+
77
+ it "should be #{DirectUploader.upload_expiration / 3600} hours from now" do
78
+ Timecop.freeze(Time.now) do
79
+ expect(Time.parse(expiration)).to have_expiration
80
+ end
81
+ end
82
+
83
+ it "should be encoded as a utc time" do
84
+ expect(Time.parse(expiration)).to be_utc
85
+ end
86
+
87
+ it "should be #{sample(:expiration) / 60 } minutes from now when passing {:expiration => #{sample(:expiration)}}" do
88
+ Timecop.freeze(Time.now) do
89
+ expect(Time.parse(expiration(:expiration => sample(:expiration)))).to have_expiration(sample(:expiration))
90
+ end
91
+ end
92
+ end
93
+
94
+ context "conditions" do
95
+ def conditions(options = {})
96
+ decoded_policy(options)["conditions"]
97
+ end
98
+
99
+ def have_condition(field, value = nil)
100
+ field.is_a?(Hash) ? include(field) : include(["starts-with", "$#{field}", value.to_s])
101
+ end
102
+
103
+ context "should include" do
104
+ it "'utf8' if enforce_ut8 is set" do
105
+ expect(conditions(enforce_utf8: true)).to have_condition(:utf8)
106
+ end
107
+
108
+ it "'utf8' if enforce_ut8 is set" do
109
+ expect(conditions).to_not have_condition(:utf8)
110
+ end
111
+
112
+ # S3 conditions
113
+ it "'key'" do
114
+ allow(mounted_subject).to receive(:key).and_return(sample(:s3_key))
115
+ expect(conditions(
116
+ :subject => mounted_subject
117
+ )).to have_condition(:key, sample(:s3_key))
118
+ end
119
+
120
+ it "'key' without FILENAME_WILDCARD" do
121
+ expect(conditions(
122
+ :subject => mounted_subject
123
+ )).to have_condition(:key, mounted_subject.key.sub("${filename}", ""))
124
+ end
125
+
126
+ it "'bucket'" do
127
+ expect(conditions).to have_condition("bucket" => uploader.fog_directory)
128
+ end
129
+
130
+ it "'acl'" do
131
+ expect(conditions).to have_condition("acl" => uploader.acl)
132
+ end
133
+
134
+ it "'success_action_redirect'" do
135
+ uploader.success_action_redirect = "http://example.com/some_url"
136
+ expect(conditions).to have_condition("success_action_redirect" => "http://example.com/some_url")
137
+ end
138
+
139
+ it "does not have 'content-type' when will_include_content_type is false" do
140
+ allow(uploader.class).to receive(:will_include_content_type).and_return(false)
141
+ expect(conditions).to_not have_condition('Content-Type')
142
+ end
143
+
144
+ it "has 'content-type' when will_include_content_type is true" do
145
+ allow(uploader.class).to receive(:will_include_content_type).and_return(true)
146
+ expect(conditions).to have_condition('Content-Type')
147
+ end
148
+
149
+ context 'when use_action_status is true' do
150
+ before(:all) do
151
+ DirectUploader.use_action_status = true
152
+ end
153
+
154
+ after(:all) do
155
+ DirectUploader.use_action_status = false
156
+ end
157
+
158
+ it "'success_action_status'" do
159
+ uploader.success_action_status = '200'
160
+ expect(conditions).to have_condition("success_action_status" => "200")
161
+ end
162
+
163
+ it "does not have 'success_action_redirect'" do
164
+ uploader.success_action_redirect = "http://example.com/some_url"
165
+ expect(conditions).to_not have_condition("success_action_redirect" => "http://example.com/some_url")
166
+ end
167
+ end
168
+
169
+ context "'content-length-range of'" do
170
+ def have_content_length_range(options = {})
171
+ include([
172
+ "content-length-range",
173
+ options[:min_file_size] || DirectUploader.min_file_size,
174
+ options[:max_file_size] || DirectUploader.max_file_size,
175
+ ])
176
+ end
177
+
178
+ it "#{DirectUploader.min_file_size} bytes" do
179
+ expect(conditions).to have_content_length_range
180
+ end
181
+
182
+ it "#{DirectUploader.max_file_size} bytes" do
183
+ expect(conditions).to have_content_length_range
184
+ end
185
+
186
+ it "#{sample(:min_file_size)} bytes when passing {:min_file_size => #{sample(:min_file_size)}}" do
187
+ expect(conditions(
188
+ :min_file_size => sample(:min_file_size)
189
+ )).to have_content_length_range(:min_file_size => sample(:min_file_size))
190
+ end
191
+
192
+ it "#{sample(:max_file_size)} bytes when passing {:max_file_size => #{sample(:max_file_size)}}" do
193
+ expect(conditions(
194
+ :max_file_size => sample(:max_file_size)
195
+ )).to have_content_length_range(:max_file_size => sample(:max_file_size))
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+
202
+ describe "clear!" do
203
+ it "should reset the cached policy string" do
204
+ Timecop.freeze(Time.now) do
205
+ @policy_now = subject.policy
206
+ end
207
+ subject.clear!
208
+
209
+ Timecop.freeze(1.second.from_now) do
210
+ @policy_after_reset = subject.policy
211
+ end
212
+ expect(@policy_after_reset).not_to eql @policy_now
213
+ end
214
+ end
215
+
216
+ describe "#signature" do
217
+ it "should not contain any new lines" do
218
+ expect(subject.signature).to_not include("\n")
219
+ end
220
+
221
+ it "should return a base64 encoded 'sha1' hash of the secret key and policy document" do
222
+ expect(Base64.decode64(subject.signature)).to eq OpenSSL::HMAC.digest(
223
+ OpenSSL::Digest.new('sha1'),
224
+ uploader.aws_secret_access_key, subject.policy
225
+ )
226
+ end
227
+ end
228
+ end
229
+