cognac 0.1.2 → 0.2.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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/CHANGELOG.md +35 -0
- data/README.md +22 -11
- data/cognac.gemspec +3 -0
- data/lib/cognac.rb +8 -73
- data/lib/cognac/cloud_file.rb +22 -0
- data/lib/cognac/signature.rb +109 -0
- data/lib/cognac/version.rb +1 -1
- data/todo.txt +24 -0
- metadata +21 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69e426ccc7c2a724b60027cf34434b191c1eadc8
|
4
|
+
data.tar.gz: d2a161c47f4b9d9af11dc8aaa02f698a4b3d8a10
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2cff56b051bf47e4c84c5d7be25a404dc60801fda1e45d3988ea4fa7c55ae29dd52d3bf716073038d6bb79f8786b89247e34ce9f3394e32b20ed58e0d37d48f9
|
7
|
+
data.tar.gz: 41c06c0c4252e30948b327ac9efbcca69c6765d65b548b4ec0bba05cfd436b5f61b410cda1d62ec506e55b9afabfb5a177cd34a6253cb984f7fb82f1bffdeefb
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.2.
|
1
|
+
2.2.3
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
== Version 0.1.3 (July 1, 2015)
|
3
|
+
|
4
|
+
Old Interface
|
5
|
+
|
6
|
+
file_name = Cognac::CloudFile.generate(params[:filename])
|
7
|
+
resource_end_point = Cognac::CloudFile.resource_end_point(ENV["AWS_S3_BUCKET"], file_name)
|
8
|
+
|
9
|
+
New Interface
|
10
|
+
|
11
|
+
file = Cognac::CloudFile.new(params[:filename], bucket)
|
12
|
+
resource_end_point = file.resource_end_point
|
13
|
+
|
14
|
+
== In Progress
|
15
|
+
|
16
|
+
Old Interface
|
17
|
+
|
18
|
+
options = Cognac::Signature.generate_options_for_build_s3_upload_url(ENV["AWS_S3_BUCKET"], file_name, params[:content_type])
|
19
|
+
url = Cognac::Signature.build_s3_upload_url(resource_end_point, ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_ACCESS_KEY"], options)
|
20
|
+
|
21
|
+
New Interface
|
22
|
+
|
23
|
+
url = Cognac::CloudFile.upload_url(ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_ACCESS_KEY"], params[:content_type])
|
24
|
+
|
25
|
+
== Combining the Two
|
26
|
+
|
27
|
+
file = Cognac::CloudFile.new(params[:filename], bucket)
|
28
|
+
|
29
|
+
resource_end_point = file.resource_end_point
|
30
|
+
url = file.upload_url(ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_ACCESS_KEY"], params[:content_type])
|
31
|
+
|
32
|
+
render :json => {put_url: url, file_url: resource_end_point}
|
33
|
+
|
34
|
+
|
35
|
+
Separate the configuration values for object properties.
|
data/README.md
CHANGED
@@ -24,26 +24,37 @@ Or install it yourself as:
|
|
24
24
|
|
25
25
|
## Dependencies
|
26
26
|
|
27
|
-
Ruby 2.2.
|
27
|
+
Ruby 2.2.3
|
28
28
|
ActiveSupport 4.2.3
|
29
29
|
|
30
30
|
## Usage
|
31
31
|
|
32
|
+
Configure the AWS credentials in config/initializers/aws.rb:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
Cognac.configuration do |config|
|
36
|
+
config.secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
|
37
|
+
config.aws_access_key = ENV['AWS_ACCESS_KEY_ID']
|
38
|
+
config.aws_s3_bucket = ENV['AWS_S3_BUCKET']
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
32
42
|
You can use this gem in your Rails controller like this:
|
33
43
|
|
34
44
|
```ruby
|
35
45
|
def generate_signed_s3_url
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
url = Cognac::Signature.build_s3_upload_url(resource_end_point, ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_ACCESS_KEY"], options)
|
46
|
+
cloud_file = Cognac::CloudFile.new(params[:filename])
|
47
|
+
resource_end_point = cloud_file.resource_end_point
|
48
|
+
|
49
|
+
options = Cognac::Signature.generate_options_for_build_s3_upload_url(cloud_file.name, params[:content_type])
|
50
|
+
url = Cognac::Signature.build_s3_upload_url(resource_end_point, options)
|
42
51
|
|
43
|
-
render :json => {:
|
52
|
+
render :json => {put_url: url, file_url: resource_end_point}
|
44
53
|
end
|
45
54
|
```
|
46
55
|
|
56
|
+
For a sample Rails project that shows how to use this gem, check out: https://github.com/bparanj/s3-cors-upload-rails
|
57
|
+
|
47
58
|
## Development
|
48
59
|
|
49
60
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -55,12 +66,12 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
55
66
|
Run the tests:
|
56
67
|
|
57
68
|
```sh
|
58
|
-
$ruby -rminitest/pride test/
|
69
|
+
$ruby -rminitest/pride test/cloud_file_test.rb --verbose
|
70
|
+
$ruby -rminitest/pride test/signature_test.rb --verbose
|
59
71
|
```
|
60
72
|
|
61
73
|
Bug reports and pull requests are welcome on Bitbucket at https://bitbucket.org/bparanj/cognac.
|
62
74
|
|
63
75
|
## License
|
64
76
|
|
65
|
-
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
66
|
-
|
77
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/cognac.gemspec
CHANGED
@@ -19,7 +19,10 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
+
spec.required_ruby_version = '>= 2.2.2'
|
23
|
+
|
22
24
|
spec.add_dependency 'activesupport', '~> 4.2'
|
25
|
+
spec.add_dependency 'lyon', "~> 0.1"
|
23
26
|
|
24
27
|
spec.add_development_dependency "bundler", "~> 1.10"
|
25
28
|
spec.add_development_dependency "rake", "~> 10.0"
|
data/lib/cognac.rb
CHANGED
@@ -1,78 +1,13 @@
|
|
1
|
+
require "cognac/cloud_file"
|
2
|
+
require "cognac/signature"
|
1
3
|
require "cognac/version"
|
2
|
-
require '
|
3
|
-
require "erb"
|
4
|
+
require 'lyon'
|
4
5
|
|
5
6
|
module Cognac
|
6
|
-
|
7
|
-
class CloudFile
|
8
|
-
def self.generate(file_name)
|
9
|
-
"#{SecureRandom.hex(4).to_s}_#{file_name}"
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.resource_end_point(bucket, file_name)
|
13
|
-
"http://#{bucket}.s3.amazonaws.com/#{file_name}"
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# AWS Signature Version 2 : http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html
|
18
|
-
class Signature
|
19
|
-
NEW_LINE = "\n"
|
20
|
-
|
21
|
-
def self.build_string_to_sign(options = {})
|
22
|
-
options = {
|
23
|
-
http_verb: "PUT",
|
24
|
-
content_md5: nil,
|
25
|
-
content_type: nil,
|
26
|
-
date: an_hour_from_now,
|
27
|
-
amz_headers: [],
|
28
|
-
resource: ""
|
29
|
-
}.merge(options)
|
7
|
+
extend Lyon::Configuration
|
30
8
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
(options[:amz_headers].any? ? (options[:amz_headers].join(NEW_LINE) + NEW_LINE) : "") +
|
36
|
-
options[:resource]
|
37
|
-
end
|
38
|
-
|
39
|
-
def self.build_s3_rest_signature(secret_access_key, options = {})
|
40
|
-
s = build_string_to_sign(options).force_encoding("UTF-8")
|
41
|
-
Base64.encode64(OpenSSL::HMAC.digest("sha1", secret_access_key, s)).strip
|
42
|
-
end
|
43
|
-
|
44
|
-
def self.build_s3_upload_url(end_point, aws_access_key, secret_access_key, signature_options = {})
|
45
|
-
signature = encoded_signature(secret_access_key, signature_options)
|
46
|
-
expires = expiration(signature_options)
|
47
|
-
"#{end_point}?AWSAccessKeyId=#{aws_access_key}&Expires=#{expires}&Signature=#{signature}"
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.generate_options_for_build_s3_upload_url(bucket, file_name, content_type)
|
51
|
-
{
|
52
|
-
http_verb: "PUT",
|
53
|
-
date: 1.hours.from_now.to_i,
|
54
|
-
resource: "/#{bucket}/#{file_name}",
|
55
|
-
content_type: content_type
|
56
|
-
}
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def self.add_new_line(option)
|
62
|
-
option.to_s + NEW_LINE
|
63
|
-
end
|
64
|
-
|
65
|
-
def self.an_hour_from_now
|
66
|
-
1.hours.from_now.rfc822
|
67
|
-
end
|
68
|
-
|
69
|
-
def self.encoded_signature(secret_access_key, signature_options)
|
70
|
-
ERB::Util.url_encode(build_s3_rest_signature(secret_access_key, signature_options))
|
71
|
-
end
|
72
|
-
|
73
|
-
def self.expiration(signature_options)
|
74
|
-
signature_options[:date] || an_hour_from_now
|
75
|
-
end
|
76
|
-
|
77
|
-
end
|
9
|
+
# Amazon Credentials
|
10
|
+
define_setting :secret_access_key
|
11
|
+
define_setting :aws_access_key
|
12
|
+
define_setting :aws_s3_bucket
|
78
13
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module Cognac
|
4
|
+
class CloudFile
|
5
|
+
|
6
|
+
def initialize(file_name)
|
7
|
+
@file_name = file_name
|
8
|
+
end
|
9
|
+
|
10
|
+
# To avoid file collision, we prepend random string to the file_name
|
11
|
+
def name
|
12
|
+
"#{SecureRandom.hex(4).to_s}_#{@file_name}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def resource_end_point
|
16
|
+
raise 'AWS_S3_BUCKET Environment variable is not intialized. Refer README.md at https://bitbucket.org/bparanj/cognac' if Cognac.aws_s3_bucket.nil?
|
17
|
+
"http://#{Cognac.aws_s3_bucket}.s3.amazonaws.com/#{name}"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'active_support/core_ext/numeric/time'
|
2
|
+
require "erb"
|
3
|
+
|
4
|
+
module Cognac
|
5
|
+
|
6
|
+
# file = Cognac::CloudFile.new(params[:filename], bucket)
|
7
|
+
#
|
8
|
+
# resource_end_point = file.resource_end_point
|
9
|
+
# url = file.upload_url(ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_ACCESS_KEY"], params[:content_type])
|
10
|
+
|
11
|
+
# 1. Create cloudfile
|
12
|
+
# 2. Generate end point
|
13
|
+
# 3. Generate upload url
|
14
|
+
|
15
|
+
# Input
|
16
|
+
# - file_name
|
17
|
+
# - bucket
|
18
|
+
# - access_key_id
|
19
|
+
# - secret_access_key
|
20
|
+
# - content_type
|
21
|
+
#
|
22
|
+
# Output
|
23
|
+
# - Signed S3 Upload URL
|
24
|
+
|
25
|
+
# build_s3_upload_url (access_key, secret_access_key)
|
26
|
+
# - Encode signature
|
27
|
+
# - build s3 rest signature (secret_access_key)
|
28
|
+
# - build_string_to_sign (options)
|
29
|
+
# - base encode hmac digest (string to sign, secret_access_key)
|
30
|
+
# - End point
|
31
|
+
|
32
|
+
# AWS Signature Version 2 : http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html
|
33
|
+
class Signature
|
34
|
+
NEW_LINE = "\n"
|
35
|
+
SECURE_HASH_ALGORITHM = "sha1"
|
36
|
+
BLANK_STRING = ""
|
37
|
+
UTF8_ENCODING = "UTF-8"
|
38
|
+
HTTP_PUT = "PUT"
|
39
|
+
|
40
|
+
def initialize(options = {})
|
41
|
+
@secret_access_key = Cognac.secret_access_key
|
42
|
+
@aws_access_key = Cognac.aws_access_key
|
43
|
+
@options = options
|
44
|
+
@default = {
|
45
|
+
http_verb: HTTP_PUT,
|
46
|
+
content_md5: nil,
|
47
|
+
content_type: nil,
|
48
|
+
date: an_hour_from_now,
|
49
|
+
amz_headers: [],
|
50
|
+
resource: BLANK_STRING
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
# This is called in the controller
|
55
|
+
def build_s3_upload_url(end_point)
|
56
|
+
"#{end_point}?AWSAccessKeyId=#{@aws_access_key}&Expires=#{expiration}&Signature=#{encoded_signature}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def encoded_signature
|
60
|
+
ERB::Util.url_encode(build_s3_rest_signature)
|
61
|
+
end
|
62
|
+
|
63
|
+
def build_s3_rest_signature
|
64
|
+
string_to_sign = build_string_to_sign(@options).force_encoding(UTF8_ENCODING)
|
65
|
+
base_encoded_hmac_digest(string_to_sign)
|
66
|
+
end
|
67
|
+
|
68
|
+
def build_string_to_sign(options = {})
|
69
|
+
o = @default.merge(options)
|
70
|
+
|
71
|
+
add_new_line(o[:http_verb]) +
|
72
|
+
add_new_line(o[:content_md5]) +
|
73
|
+
add_new_line(o[:content_type]) +
|
74
|
+
add_new_line(o[:date]) +
|
75
|
+
(o[:amz_headers].any? ? (o[:amz_headers].join(NEW_LINE) + NEW_LINE) : BLANK_STRING) +
|
76
|
+
o[:resource]
|
77
|
+
end
|
78
|
+
|
79
|
+
def base_encoded_hmac_digest(string_to_sign)
|
80
|
+
Base64.encode64(OpenSSL::HMAC.digest(SECURE_HASH_ALGORITHM, @secret_access_key, string_to_sign)).strip
|
81
|
+
end
|
82
|
+
|
83
|
+
# This is called in the controller and passed in to the build_s3_upload_url method as a parameter
|
84
|
+
# Hide this method
|
85
|
+
def generate_options_for_build_s3_upload_url(file_name, content_type)
|
86
|
+
{
|
87
|
+
http_verb: HTTP_PUT,
|
88
|
+
date: 1.hours.from_now.to_i,
|
89
|
+
resource: "/#{Cognac.aws_s3_bucket}/#{file_name}",
|
90
|
+
content_type: content_type
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def add_new_line(option)
|
97
|
+
option.to_s + NEW_LINE
|
98
|
+
end
|
99
|
+
|
100
|
+
def an_hour_from_now
|
101
|
+
1.hours.from_now.rfc822
|
102
|
+
end
|
103
|
+
|
104
|
+
def expiration
|
105
|
+
@options[:date] || an_hour_from_now
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
data/lib/cognac/version.rb
CHANGED
data/todo.txt
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
1. Hide methods that are not used in the controller.
|
2
|
+
|
3
|
+
2.
|
4
|
+
|
5
|
+
# file = Cognac::CloudFile.new(params[:filename], bucket)
|
6
|
+
#
|
7
|
+
# resource_end_point = file.resource_end_point
|
8
|
+
# url = file.upload_url(ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_ACCESS_KEY"], params[:content_type])
|
9
|
+
|
10
|
+
# 1. Create cloudfile
|
11
|
+
# 2. Generate end point
|
12
|
+
# 3. Generate upload url
|
13
|
+
|
14
|
+
3. Use the latest version of Cognac gem with S3 uploader.
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cognac
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bala Paranj
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-08-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '4.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: lyon
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.1'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -79,6 +93,7 @@ files:
|
|
79
93
|
- ".ruby-gemset"
|
80
94
|
- ".ruby-version"
|
81
95
|
- ".travis.yml"
|
96
|
+
- CHANGELOG.md
|
82
97
|
- Gemfile
|
83
98
|
- LICENSE.txt
|
84
99
|
- README.md
|
@@ -87,7 +102,10 @@ files:
|
|
87
102
|
- bin/setup
|
88
103
|
- cognac.gemspec
|
89
104
|
- lib/cognac.rb
|
105
|
+
- lib/cognac/cloud_file.rb
|
106
|
+
- lib/cognac/signature.rb
|
90
107
|
- lib/cognac/version.rb
|
108
|
+
- todo.txt
|
91
109
|
homepage: http://www.rubyplus.com
|
92
110
|
licenses:
|
93
111
|
- MIT
|
@@ -100,7 +118,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
100
118
|
requirements:
|
101
119
|
- - ">="
|
102
120
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
121
|
+
version: 2.2.2
|
104
122
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
123
|
requirements:
|
106
124
|
- - ">="
|