qoobaa-d2s3 0.1.1
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.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.markdown +62 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/d2s3.gemspec +51 -0
- data/generators/d2s3/d2s3.rb +17 -0
- data/generators/d2s3/templates/initializers/d2s3.rb +5 -0
- data/lib/d2s3/configuration.rb +18 -0
- data/lib/d2s3/signature.rb +194 -0
- data/lib/d2s3/view_helpers.rb +50 -0
- data/lib/d2s3.rb +3 -0
- data/test/signature_test.rb +57 -0
- data/test/test_helper.rb +10 -0
- metadata +70 -0
data/.document
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Matthew Williams, Jakub Kuźma
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# **d2s3**
|
2
|
+
d2s3 (direct to s3) is a simple Ruby on Rails helper that generates an upload form that will take a given file and upload it directly to your S3 bucket, bypassing your server. This has various benefits, the biggest being that a large upload will not tie up your server from serving other requests. This plugin is based on the instructions from the following Amazon tutorial: [http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1434](http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1434/ "Browser Uploads to S3 using HTML POST Forms")
|
3
|
+
|
4
|
+
### Why?
|
5
|
+
This was built as a solution to a problem we had where images being uploaded to be processed by [Paperclip](http://thoughtbot.com/projects/paperclip "Thoughtbot - Paperclip") were consuming [Thin](http://code.macournoyer.com/thin/ "Thin - Another Web Server") servers so they were unable to process other requests.
|
6
|
+
|
7
|
+
#### An example workflow using d2s3
|
8
|
+
1. Upload file with the `s3_upload_form_tag` helper form tag
|
9
|
+
2. With the data returned from the GET HTTP request from Amazon, create a message using Amazon's SQS service with the appropriate information needed to later process the image with [Paperclip](http://thoughtbot.com/projects/paperclip "Thoughtbot - Paperclip")
|
10
|
+
3. Create an API end point for a background process to access that accepts information to process the image with Paperclip. In our case, it accepts the ID of a photo album and the path to the photo to be processed
|
11
|
+
4. Running a back end process that monitors the SQS queue for new images and processes them immediately
|
12
|
+
|
13
|
+
We don't have immediate processing of our images with this workflow, but it's very quick assuming how large the queue is and how many back end processes we have actually running through the queue.
|
14
|
+
|
15
|
+
### Example d2s3 usage
|
16
|
+
<%= s3_upload_form_tag :key => 'uploads',
|
17
|
+
:redirect => image_processing_url,
|
18
|
+
:acl => 'public-read',
|
19
|
+
:max_filesize => 5.megabytes,
|
20
|
+
:submit_button => '<input type="submit" value="Upload" class="button" id="upload-button">',
|
21
|
+
:form => {:style => 'display: inline;'} %>
|
22
|
+
|
23
|
+
The above helper will generate the following similar HTML form, generating all of the appropriate field keys, policy, and signature based on your Amazon Web Services YAML configuration file. The form parameter also accepts a class and id for further customization.
|
24
|
+
|
25
|
+
<form action="https://YOUR_S3_BUCKET.s3.amazonaws.com/" method="post" enctype="multipart/form-data" style="display: inline;">
|
26
|
+
<input type="hidden" name="key" value="uploads/${filename}">
|
27
|
+
<input type="hidden" name="AWSAccessKeyId" value="YOUR_AWS_ACCESS_KEY">
|
28
|
+
<input type="hidden" name="acl" value="public-read">
|
29
|
+
<input type="hidden" name="success_action_redirect" value="/image_processing_url">
|
30
|
+
<input type="hidden" name="policy" value="YOUR_POLICY_DOCUMENT_BASE64_ENCODED">
|
31
|
+
<input type="hidden" name="signature" value="YOUR_CALCULATED_SIGNATURE">
|
32
|
+
<input name="file" type="file"><input type="submit" value="Upload" class="button" id="upload-button">
|
33
|
+
</form>
|
34
|
+
|
35
|
+
### Return HTTP GET request from Amazon made to the redirect you declared
|
36
|
+
Parameters: {"bucket"=>"BUCKET_NAME",
|
37
|
+
"etag"=>"ETAG_HASH",
|
38
|
+
"action"=>"YOUR_REDIRECT_URL",
|
39
|
+
"controller"=>"CONTROLLER",
|
40
|
+
"key"=>"PATH/FILENAME.EXTENSION"}
|
41
|
+
|
42
|
+
### Options:
|
43
|
+
* **:redirect**
|
44
|
+
* Directs the form where the GET request from Amazon should be made once the HTTP POST is successful
|
45
|
+
* **:acl**
|
46
|
+
* Accepts either 'public-read' or 'private'. If blank, it defaults to 'public-read'
|
47
|
+
* **:expiration_date**
|
48
|
+
* Accepts time in the form of "3.hours" or "25.minutes". If blank, it defaults to a 10 hour window before the policy on the upload expires
|
49
|
+
* **:max_filesize**
|
50
|
+
* Accepts a max file size in the format of "5.megabytes". If blank, it defaults to 1.megabyte
|
51
|
+
* **:submit_button**
|
52
|
+
* Accepts any text to represent the submit button for the form. This allows for a very custom submit button. If blank, it defaults to `<input type="submit" value="Upload">`
|
53
|
+
* **:form => {:id => '', :class => '', :style => ''}**
|
54
|
+
* Accepts a hash of :class, :id, and :style to add customization to the form as a whole
|
55
|
+
|
56
|
+
|
57
|
+
### **TODO**
|
58
|
+
* write more tests
|
59
|
+
|
60
|
+
_**Matthew Williams, Jakub Kuźma, 2009**_
|
61
|
+
|
62
|
+
Thanks to the [s3-swf-upload](http://github.com/elcgit/s3-swf-upload-plugin/tree/master "s3-swf-upload GitHub Project Page") plugin which code was borrowed from to make this project happen.
|
data/Rakefile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#coding: UTF-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake'
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'jeweler'
|
8
|
+
Jeweler::Tasks.new do |gem|
|
9
|
+
gem.name = "d2s3"
|
10
|
+
gem.summary = %Q{direct to s3}
|
11
|
+
gem.email = "qoobaa@gmail.com"
|
12
|
+
gem.homepage = "http://github.com/qoobaa/d2s3"
|
13
|
+
gem.authors = ["Matthew Williams", "Jakub Kuźma"]
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake/testtask'
|
22
|
+
Rake::TestTask.new(:test) do |test|
|
23
|
+
test.libs << 'lib' << 'test'
|
24
|
+
test.pattern = 'test/**/*_test.rb'
|
25
|
+
test.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'rcov/rcovtask'
|
30
|
+
Rcov::RcovTask.new do |test|
|
31
|
+
test.libs << 'test'
|
32
|
+
test.pattern = 'test/**/*_test.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
rescue LoadError
|
36
|
+
task :rcov do
|
37
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
task :default => :test
|
43
|
+
|
44
|
+
require 'rake/rdoctask'
|
45
|
+
Rake::RDocTask.new do |rdoc|
|
46
|
+
if File.exist?('VERSION.yml')
|
47
|
+
config = YAML.load(File.read('VERSION.yml'))
|
48
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
49
|
+
else
|
50
|
+
version = ""
|
51
|
+
end
|
52
|
+
|
53
|
+
rdoc.rdoc_dir = 'rdoc'
|
54
|
+
rdoc.title = "d2s3 #{version}"
|
55
|
+
rdoc.rdoc_files.include('README*')
|
56
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
57
|
+
end
|
58
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
data/d2s3.gemspec
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{d2s3}
|
5
|
+
s.version = "0.1.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Matthew Williams", "Jakub Kuźma"]
|
9
|
+
s.date = %q{2009-06-16}
|
10
|
+
s.email = %q{qoobaa@gmail.com}
|
11
|
+
s.extra_rdoc_files = [
|
12
|
+
"LICENSE",
|
13
|
+
"README.markdown"
|
14
|
+
]
|
15
|
+
s.files = [
|
16
|
+
".document",
|
17
|
+
".gitignore",
|
18
|
+
"LICENSE",
|
19
|
+
"README.markdown",
|
20
|
+
"Rakefile",
|
21
|
+
"VERSION",
|
22
|
+
"d2s3.gemspec",
|
23
|
+
"generators/d2s3/d2s3.rb",
|
24
|
+
"generators/d2s3/templates/initializers/d2s3.rb",
|
25
|
+
"lib/d2s3.rb",
|
26
|
+
"lib/d2s3/configuration.rb",
|
27
|
+
"lib/d2s3/signature.rb",
|
28
|
+
"lib/d2s3/view_helpers.rb",
|
29
|
+
"test/signature_test.rb",
|
30
|
+
"test/test_helper.rb"
|
31
|
+
]
|
32
|
+
s.homepage = %q{http://github.com/qoobaa/d2s3}
|
33
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
34
|
+
s.require_paths = ["lib"]
|
35
|
+
s.rubygems_version = %q{1.3.4}
|
36
|
+
s.summary = %q{direct to s3}
|
37
|
+
s.test_files = [
|
38
|
+
"test/signature_test.rb",
|
39
|
+
"test/test_helper.rb"
|
40
|
+
]
|
41
|
+
|
42
|
+
if s.respond_to? :specification_version then
|
43
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
44
|
+
s.specification_version = 3
|
45
|
+
|
46
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
47
|
+
else
|
48
|
+
end
|
49
|
+
else
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
class D2S3Generator < Rails::Generator::Base
|
4
|
+
def manifest
|
5
|
+
record do |m|
|
6
|
+
m.directory "config"
|
7
|
+
m.directory "config/initializers"
|
8
|
+
m.file "initializers/d2s3.rb", "config/initializers/d2s3.rb"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def banner
|
15
|
+
"Usage: #{$0} d2s3"
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "singleton"
|
2
|
+
|
3
|
+
module D2S3
|
4
|
+
class Configuration
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
ATTRIBUTES = [:access_key_id, :secret_access_key, :bucket]
|
8
|
+
|
9
|
+
attr_accessor *ATTRIBUTES
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.configuration
|
13
|
+
if block_given?
|
14
|
+
yield Configuration.instance
|
15
|
+
end
|
16
|
+
Configuration.instance
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
module D2S3
|
2
|
+
module Signature
|
3
|
+
HEXCASE = false # hex output format. false - lowercase; true - uppercase
|
4
|
+
B64PAD = "=" # base-64 pad character. "=" for strict RFC compliance
|
5
|
+
CHRSH = 8 # bits per input character. 8 - ASCII; 16 - Unicode
|
6
|
+
|
7
|
+
def hex_sha1(s)
|
8
|
+
return binb2hex(core_sha1(str2binb(s), s.length * CHRSH))
|
9
|
+
end
|
10
|
+
|
11
|
+
def b64_hmac_sha1(key, data)
|
12
|
+
return binb2b64(core_hmac_sha1(key, data))
|
13
|
+
end
|
14
|
+
|
15
|
+
# 32-bit left shift
|
16
|
+
def js_shl(num, count)
|
17
|
+
v = (num << count) & 0xffffffff
|
18
|
+
v > 2**31 ? v - 2**32 : v
|
19
|
+
end
|
20
|
+
|
21
|
+
# 32-bit zero-fill right shift (>>>)
|
22
|
+
def js_shr_zf(num, count)
|
23
|
+
num >> count & (2**(32-count)-1)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Calculate the SHA-1 of an array of big-endian words, and a bit length
|
27
|
+
def core_sha1(x, len)
|
28
|
+
# append padding
|
29
|
+
x[len >> 5] ||= 0
|
30
|
+
x[len >> 5] |= 0x80 << (24 - len % 32)
|
31
|
+
x[((len + 64 >> 9) << 4) + 15] = len
|
32
|
+
|
33
|
+
w = Array.new(80, 0)
|
34
|
+
a = 1732584193
|
35
|
+
b = -271733879
|
36
|
+
c = -1732584194
|
37
|
+
d = 271733878
|
38
|
+
e = -1009589776
|
39
|
+
|
40
|
+
#for(var i = 0; i < x.length; i += 16)
|
41
|
+
i = 0
|
42
|
+
while(i < x.length)
|
43
|
+
olda = a
|
44
|
+
oldb = b
|
45
|
+
oldc = c
|
46
|
+
oldd = d
|
47
|
+
olde = e
|
48
|
+
|
49
|
+
#for(var j = 0; j < 80; j++)
|
50
|
+
j = 0
|
51
|
+
while(j < 80)
|
52
|
+
if(j < 16)
|
53
|
+
w[j] = x[i + j] || 0
|
54
|
+
else
|
55
|
+
w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1)
|
56
|
+
end
|
57
|
+
|
58
|
+
t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
|
59
|
+
safe_add(safe_add(e, w[j]), sha1_kt(j)))
|
60
|
+
e = d
|
61
|
+
d = c
|
62
|
+
c = rol(b, 30)
|
63
|
+
b = a
|
64
|
+
a = t
|
65
|
+
j += 1
|
66
|
+
end
|
67
|
+
|
68
|
+
a = safe_add(a, olda)
|
69
|
+
b = safe_add(b, oldb)
|
70
|
+
c = safe_add(c, oldc)
|
71
|
+
d = safe_add(d, oldd)
|
72
|
+
e = safe_add(e, olde)
|
73
|
+
i += 16
|
74
|
+
end
|
75
|
+
return [a, b, c, d, e]
|
76
|
+
end
|
77
|
+
|
78
|
+
# Perform the appropriate triplet combination function for the current
|
79
|
+
# iteration
|
80
|
+
def sha1_ft(t, b, c, d)
|
81
|
+
return (b & c) | ((~b) & d) if(t < 20)
|
82
|
+
return b ^ c ^ d if(t < 40)
|
83
|
+
return (b & c) | (b & d) | (c & d) if(t < 60)
|
84
|
+
return b ^ c ^ d;
|
85
|
+
end
|
86
|
+
|
87
|
+
# Determine the appropriate additive constant for the current iteration
|
88
|
+
def sha1_kt(t)
|
89
|
+
return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
|
90
|
+
(t < 60) ? -1894007588 : -899497514
|
91
|
+
end
|
92
|
+
|
93
|
+
# Calculate the HMAC-SHA1 of a key and some data
|
94
|
+
def core_hmac_sha1(key, data)
|
95
|
+
bkey = str2binb(key)
|
96
|
+
if(bkey.length > 16)
|
97
|
+
bkey = core_sha1(bkey, key.length * CHRSH)
|
98
|
+
end
|
99
|
+
|
100
|
+
ipad = Array.new(16, 0)
|
101
|
+
opad = Array.new(16, 0)
|
102
|
+
#for(var i = 0; i < 16; i++)
|
103
|
+
i = 0
|
104
|
+
while(i < 16)
|
105
|
+
ipad[i] = (bkey[i] || 0) ^ 0x36363636
|
106
|
+
opad[i] = (bkey[i] || 0) ^ 0x5C5C5C5C
|
107
|
+
i += 1
|
108
|
+
end
|
109
|
+
|
110
|
+
hash = core_sha1((ipad + str2binb(data)), 512 + data.length * CHRSH)
|
111
|
+
return core_sha1((opad + hash), 512 + 160)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Add integers, wrapping at 2^32. This uses 16-bit operations internally
|
115
|
+
# to work around bugs in some JS interpreters.
|
116
|
+
def safe_add(x, y)
|
117
|
+
v = (x+y) % (2**32)
|
118
|
+
return v > 2**31 ? v- 2**32 : v
|
119
|
+
end
|
120
|
+
|
121
|
+
# Bitwise rotate a 32-bit number to the left.
|
122
|
+
def rol(num, cnt)
|
123
|
+
#return (num << cnt) | (num >>> (32 - cnt))
|
124
|
+
return (js_shl(num, cnt)) | (js_shr_zf(num, 32 - cnt))
|
125
|
+
end
|
126
|
+
|
127
|
+
# Convert an 8-bit or 16-bit string to an array of big-endian words
|
128
|
+
# In 8-bit function, characters >255 have their hi-byte silently ignored.
|
129
|
+
def str2binb(str)
|
130
|
+
bin = []
|
131
|
+
mask = (1 << CHRSH) - 1
|
132
|
+
#for(var i = 0; i < str.length * CHRSH; i += CHRSH)
|
133
|
+
i = 0
|
134
|
+
while(i < str.length * CHRSH)
|
135
|
+
bin[i>>5] ||= 0
|
136
|
+
byte = str.respond_to?(:getbyte) ? str.getbyte(i / CHRSH) : str[i / CHRSH]
|
137
|
+
bin[i>>5] |= (byte & mask) << (32 - CHRSH - i%32)
|
138
|
+
i += CHRSH
|
139
|
+
end
|
140
|
+
return bin
|
141
|
+
end
|
142
|
+
|
143
|
+
# Convert an array of big-endian words to a string
|
144
|
+
# function binb2str(bin)
|
145
|
+
# {
|
146
|
+
# var str = "";
|
147
|
+
# var mask = (1 << CHRSH) - 1;
|
148
|
+
# for(var i = 0; i < bin.length * 32; i += CHRSH)
|
149
|
+
# str += String.fromCharCode((bin[i>>5] >>> (32 - CHRSH - i%32)) & mask);
|
150
|
+
# return str;
|
151
|
+
# }
|
152
|
+
#
|
153
|
+
|
154
|
+
# Convert an array of big-endian words to a hex string.
|
155
|
+
def binb2hex(binarray)
|
156
|
+
hex_tab = HEXCASE ? "0123456789ABCDEF" : "0123456789abcdef"
|
157
|
+
str = ""
|
158
|
+
#for(var i = 0; i < binarray.length * 4; i++)
|
159
|
+
i = 0
|
160
|
+
while(i < binarray.length * 4)
|
161
|
+
str += hex_tab[(binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF].chr +
|
162
|
+
hex_tab[(binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF].chr
|
163
|
+
i += 1
|
164
|
+
end
|
165
|
+
return str;
|
166
|
+
end
|
167
|
+
|
168
|
+
# Convert an array of big-endian words to a base-64 string
|
169
|
+
def binb2b64(binarray)
|
170
|
+
tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
171
|
+
str = ""
|
172
|
+
|
173
|
+
#for(var i = 0; i < binarray.length * 4; i += 3)
|
174
|
+
i = 0
|
175
|
+
while(i < binarray.length * 4)
|
176
|
+
triplet = (((binarray[i >> 2].to_i >> 8 * (3 - i %4)) & 0xFF) << 16) |
|
177
|
+
(((binarray[i+1 >> 2].to_i >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) |
|
178
|
+
((binarray[i+2 >> 2].to_i >> 8 * (3 - (i+2)%4)) & 0xFF)
|
179
|
+
#for(var j = 0; j < 4; j++)
|
180
|
+
j = 0
|
181
|
+
while(j < 4)
|
182
|
+
if(i * 8 + j * 6 > binarray.length * 32)
|
183
|
+
str += B64PAD
|
184
|
+
else
|
185
|
+
str += tab[(triplet >> 6*(3-j)) & 0x3F].chr
|
186
|
+
end
|
187
|
+
j += 1
|
188
|
+
end
|
189
|
+
i += 3
|
190
|
+
end
|
191
|
+
return str
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module D2S3
|
4
|
+
module ViewHelpers
|
5
|
+
include D2S3::Signature
|
6
|
+
|
7
|
+
def s3_upload_form_tag(options = {})
|
8
|
+
bucket = configuration.bucket
|
9
|
+
access_key_id = configuration.access_key_id
|
10
|
+
protocol = options[:protocol] || "http"
|
11
|
+
key = options[:key] || ''
|
12
|
+
redirect = options[:redirect] || '/'
|
13
|
+
acl = options[:acl] || 'public-read'
|
14
|
+
expiration_date = (options[:expiration_date] || 10.hours).from_now.strftime('%Y-%m-%dT%H:%M:%S.000Z')
|
15
|
+
max_filesize = options[:max_filesize] || 1.megabyte
|
16
|
+
submit_button = options[:submit_button] || '<input type="submit" value="#{I18n.t("button.upload")}">'
|
17
|
+
|
18
|
+
options[:form] ||= {}
|
19
|
+
options[:form][:id] ||= 'upload-form'
|
20
|
+
options[:form][:class] ||= 'upload-form'
|
21
|
+
|
22
|
+
policy = Base64.encode64(
|
23
|
+
%({"expiration": "#{expiration_date}",
|
24
|
+
"conditions": [
|
25
|
+
{"bucket": "#{bucket}"},
|
26
|
+
["starts-with", "$key", "#{key}"],
|
27
|
+
{"acl": "#{acl}"},
|
28
|
+
{"success_action_redirect": "#{redirect}"},
|
29
|
+
["content-length-range", 0, #{max_filesize}]
|
30
|
+
]
|
31
|
+
})).gsub(/\n|\r/, '')
|
32
|
+
|
33
|
+
signature = b64_hmac_sha1(D2S3::S3Config.secret_access_key, policy)
|
34
|
+
out = ""
|
35
|
+
out << %(
|
36
|
+
<form action="#{protocol}://#{bucket}.s3.amazonaws.com/" method="post" enctype="multipart/form-data" id="#{options[:form][:id]}" class="#{options[:form][:class]}" style="#{options[:form][:style]}">
|
37
|
+
<input type="hidden" name="key" value="#{key}/${filename}">
|
38
|
+
<input type="hidden" name="AWSAccessKeyId" value="#{access_key_id}">
|
39
|
+
<input type="hidden" name="acl" value="#{acl}">
|
40
|
+
<input type="hidden" name="success_action_redirect" value="#{redirect}">
|
41
|
+
<input type="hidden" name="policy" value="#{policy}">
|
42
|
+
<input type="hidden" name="signature" value="#{signature}">
|
43
|
+
<input name="file" type="file">#{submit_button}
|
44
|
+
</form>
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
ActionView::Base.send(:include, D2S3::ViewHelpers) if defined?(ActionView::Base)
|
data/lib/d2s3.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
include D2S3::Signature
|
4
|
+
|
5
|
+
class SignatureTest < Test::Unit::TestCase
|
6
|
+
should "return correct core_sha1 values" do
|
7
|
+
str = "abc"
|
8
|
+
assert [-519653305, -1566383753, -2070791546, -753729183, -204968198], core_sha1(str2binb(str), str.length)
|
9
|
+
end
|
10
|
+
|
11
|
+
should "return correct str2binb values" do
|
12
|
+
assert [1718578976, 1650553376, 1751480608, 1952998770, 1694498816], str2binb('foo bar hey there')
|
13
|
+
end
|
14
|
+
|
15
|
+
should "return correct hex_sha1 value" do
|
16
|
+
assert "a9993e364706816aba3e25717850c26c9cd0d89d", hex_sha1("abc")
|
17
|
+
end
|
18
|
+
|
19
|
+
should "return correct b64_hmac_sha1 value" do
|
20
|
+
assert "frFXMR9cNoJdsSPnjebZpBhUKzI=", b64_hmac_sha1("foo", "abc")
|
21
|
+
end
|
22
|
+
|
23
|
+
context "with num=1732584193 and cnt=5" do
|
24
|
+
setup do
|
25
|
+
@num = 1732584193
|
26
|
+
@cnt = 5
|
27
|
+
end
|
28
|
+
|
29
|
+
should "return correct roll value" do
|
30
|
+
assert -391880660, rol(@num, @cnt)
|
31
|
+
end
|
32
|
+
|
33
|
+
should "return correct safe_add value" do
|
34
|
+
assert -519653305, safe_add(2042729798, @num)
|
35
|
+
end
|
36
|
+
|
37
|
+
should "return correct js_shl value" do
|
38
|
+
assert -391880672, js_shl(@num, @cnt)
|
39
|
+
end
|
40
|
+
|
41
|
+
should "return correct js_shr_zf value" do
|
42
|
+
assert 12, js_shr_zf(@num, 32 - @cnt)
|
43
|
+
end
|
44
|
+
|
45
|
+
should "return correct sha1_ft value" do
|
46
|
+
assert -1732584194, sha1_ft(0, -271733879, -1732584194, 271733878)
|
47
|
+
end
|
48
|
+
|
49
|
+
should "return correct sha1_kt value" do
|
50
|
+
assert 1518500249, sha1_kt(0)
|
51
|
+
end
|
52
|
+
|
53
|
+
should "return correct safe_add value" do
|
54
|
+
assert 286718899, safe_add(safe_add(rol(@num, @cnt), sha1_ft(0, -271733879, -1732584194, 271733878)), safe_add(safe_add(-1009589776, 1902273280), sha1_kt(0)))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: qoobaa-d2s3
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matthew Williams
|
8
|
+
- "Jakub Ku\xC5\xBAma"
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2009-06-16 00:00:00 -07:00
|
14
|
+
default_executable:
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description:
|
18
|
+
email: qoobaa@gmail.com
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files:
|
24
|
+
- LICENSE
|
25
|
+
- README.markdown
|
26
|
+
files:
|
27
|
+
- .document
|
28
|
+
- .gitignore
|
29
|
+
- LICENSE
|
30
|
+
- README.markdown
|
31
|
+
- Rakefile
|
32
|
+
- VERSION
|
33
|
+
- d2s3.gemspec
|
34
|
+
- generators/d2s3/d2s3.rb
|
35
|
+
- generators/d2s3/templates/initializers/d2s3.rb
|
36
|
+
- lib/d2s3.rb
|
37
|
+
- lib/d2s3/configuration.rb
|
38
|
+
- lib/d2s3/signature.rb
|
39
|
+
- lib/d2s3/view_helpers.rb
|
40
|
+
- test/signature_test.rb
|
41
|
+
- test/test_helper.rb
|
42
|
+
has_rdoc: false
|
43
|
+
homepage: http://github.com/qoobaa/d2s3
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options:
|
46
|
+
- --charset=UTF-8
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
version:
|
61
|
+
requirements: []
|
62
|
+
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.2.0
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: direct to s3
|
68
|
+
test_files:
|
69
|
+
- test/signature_test.rb
|
70
|
+
- test/test_helper.rb
|