cloudfront-signer 2.2.0 → 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +9 -0
- data/ChangeLog.markdown +3 -1
- data/Gemfile +2 -2
- data/LICENSE +2 -1
- data/README.markdown +55 -43
- data/Rakefile +3 -4
- data/cloudfront-signer.gemspec +22 -17
- data/lib/cloudfront-signer.rb +77 -56
- data/lib/cloudfront-signer/version.rb +3 -3
- data/lib/generators/cloudfront/install/install_generator.rb +5 -5
- data/lib/generators/cloudfront/install/templates/cloudfront-signer.rb +2 -2
- data/spec/signer_spec.rb +53 -57
- data/spec/spec_helper.rb +6 -6
- metadata +24 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af2fd7402de3304dcebf5c73545b34f34a4bd793
|
4
|
+
data.tar.gz: 1758268e87d18ecaca43588ca03b34a0bd00e7a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61d635f44898c5ad71c2dad0a75c475f0307c8dd7f16b7e14e58ee789552c60e312ebe8dbf9e5cfe926db34356fc208ba57b4270b5f106dcdd8e03990e89b0b7
|
7
|
+
data.tar.gz: 779f86267e2e7027a28ce2ff11629349f325cac5d608065b914cc72b9fc42407d8fa99c7ad77414820ccfcfc840d1b2cb254de7691c1677d1c06608eefca40b8
|
data/.travis.yml
ADDED
data/ChangeLog.markdown
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
### 2.2.1 / 2015-04-29
|
2
|
+
* Fixes policy generation when specifying an ip_range. See https://github.com/leonelgalan/cloudfront-signer/commit/18b19cc2f833850f360a92f4e244358601bba5ec#commitcomment-11249140 for details.
|
3
|
+
|
1
4
|
### 2.2.0 / 2015-04-29
|
2
5
|
* Accepted merge request from https://github.com/leonelgalan - `sign_params` method returns raw params to be used in urls or cookies.
|
3
6
|
|
@@ -7,4 +10,3 @@
|
|
7
10
|
### 2.1.1 / 2013-10-31
|
8
11
|
* Added changelog file
|
9
12
|
* Aceppted merge request from https://github.com/bullfight, Refactored configuration to allow for key to be passed in directly.
|
10
|
-
|
data/Gemfile
CHANGED
data/LICENSE
CHANGED
data/README.markdown
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
# cloudfront-signer
|
1
|
+
# cloudfront-signer [![Build Status](https://travis-ci.org/leonelgalan/cloudfront-signer.svg)](https://travis-ci.org/leonelgalan/cloudfront-signer) [![Code Climate](https://codeclimate.com/github/leonelgalan/cloudfront-signer/badges/gpa.svg)](https://codeclimate.com/github/leonelgalan/cloudfront-signer) [![Test Coverage](https://codeclimate.com/github/leonelgalan/cloudfront-signer/badges/coverage.svg)](https://codeclimate.com/github/leonelgalan/cloudfront-signer/coverage) [![Gem Version](https://badge.fury.io/rb/cloudfront-signer.svg)](http://badge.fury.io/rb/cloudfront-signer) [![Dependency Status](https://gemnasium.com/leonelgalan/cloudfront-signer.svg)](https://gemnasium.com/leonelgalan/cloudfront-signer)
|
2
2
|
|
3
3
|
See the [ChangeLog](https://github.com/58bits/cloudfront-signer/blob/master/ChangeLog.markdown) for details of this release.
|
4
4
|
|
5
|
-
|
5
|
+
See Amazon docs for [Serving Private Content through CloudFront](http://docs.amazonwebservices.com/AmazonCloudFront/latest/DeveloperGuide/index.html?PrivateContent.html)
|
6
6
|
|
7
|
-
|
7
|
+
A fork and rewrite started by [Anthony Bouch](https://github.com/58bits) of Dylan Vaughn's [aws_cf_signer](https://github.com/stlondemand/aws_cf_signer).
|
8
8
|
|
9
9
|
This version uses all class methods and a configure method to set options.
|
10
10
|
|
@@ -12,47 +12,60 @@ Seperate helper methods exist for safe signing of urls and stream paths, each of
|
|
12
12
|
|
13
13
|
## Installation
|
14
14
|
|
15
|
-
The original gem was published as `aws_cf_signer`. Use `gem install aws_cf_signer` to install that version.
|
16
|
-
|
17
15
|
This gem has been publised as `cloudfront-signer`. Use `gem install cloudfront-signer` to install this gem.
|
18
16
|
|
19
17
|
Alternatively, place a copy of cloudfront-signer.rb (and the cloundfront-signer sub directory) in your lib directory.
|
20
18
|
|
21
19
|
In either case the signing class must be configured - supplying the path to a signing key, or supplying the signing key directly as a string along with the `key_pair_id`. Create the initializer by running:
|
22
20
|
|
23
|
-
```
|
24
|
-
bundle exec rails
|
21
|
+
```sh
|
22
|
+
bundle exec rails generate cloudfront:install
|
25
23
|
```
|
26
24
|
|
27
|
-
and customizing the resulting
|
25
|
+
and customizing the resulting _config/initializers/cloudfront-signer.rb_ file.
|
28
26
|
|
29
|
-
### Generated
|
27
|
+
### Generated _cloudfront-signer.rb_
|
30
28
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
29
|
+
```ruby
|
30
|
+
AWS::CF::Signer.configure do |config|
|
31
|
+
config.key_path = '/path/to/keyfile.pem'
|
32
|
+
# config.key = ENV.fetch('PRIVATE_KEY') # key_path not required if key supplied directly
|
33
|
+
config.key_pair_id = 'XXYYZZ'
|
34
|
+
config.default_expires = 3600
|
35
|
+
end
|
36
|
+
```
|
37
37
|
|
38
38
|
## Usage
|
39
39
|
|
40
40
|
Call the class `sign_url` or `sign_path` method with optional policy settings.
|
41
41
|
|
42
|
-
|
42
|
+
```ruby
|
43
|
+
AWS::CF::Signer.sign_url 'http://mydomain/path/to/my/content'
|
44
|
+
```
|
43
45
|
|
44
46
|
or
|
45
47
|
|
46
|
-
|
48
|
+
```ruby
|
49
|
+
AWS::CF::Signer.sign_url 'http://mydomain/path/to/my/content', expires: Time.now + 600
|
50
|
+
```
|
47
51
|
|
48
52
|
Streaming paths can be signed with the `sign_path` method.
|
49
53
|
|
50
|
-
|
54
|
+
```ruby
|
55
|
+
AWS::CF::Signer.sign_path 'path/to/my/content'
|
56
|
+
```
|
51
57
|
|
52
58
|
or
|
53
59
|
|
54
|
-
|
60
|
+
```ruby
|
61
|
+
AWS::CF::Signer.sign_path 'path/to/my/content', expires: Time.now + 600
|
62
|
+
```
|
55
63
|
|
64
|
+
Raw parameters can be get with the `signed_params` method. See [commit message](https://github.com/leonelgalan/cloudfront-signer/commit/fedcc3182e32133e4bd0ad0b79c0106168896c91) for additional details.
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
AWS::CF::Signer.sign_params 'path/to/my/content'
|
68
|
+
```
|
56
69
|
|
57
70
|
Both `sign_url` and `sign_path` have _safe_ versions that HTML encode the result allowing signed paths or urls to be placed in HTML markup. The 'non'-safe versions can be used for placing signed urls or paths in JavaScript blocks or Flash params.
|
58
71
|
|
@@ -61,25 +74,30 @@ Both `sign_url` and `sign_path` have _safe_ versions that HTML encode the result
|
|
61
74
|
|
62
75
|
See Example Custom Policy 1 at above AWS doc link
|
63
76
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
77
|
+
```ruby
|
78
|
+
url = AWS::CF::Signer.sign_url 'http://d604721fxaaqy9.cloudfront.net/training/orientation.avi',
|
79
|
+
expires: 'Sat, 14 Nov 2009 22:20:00 GMT',
|
80
|
+
resource: 'http://d604721fxaaqy9.cloudfront.net/training/*',
|
81
|
+
ip_range: '145.168.143.0/24'
|
82
|
+
)
|
83
|
+
```
|
69
84
|
|
70
85
|
See Example Custom Policy 2 at above AWS doc link
|
71
86
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
87
|
+
```ruby
|
88
|
+
AWS::CF::Signer.sign_url 'http://d84l721fxaaqy9.cloudfront.net/downloads/pictures.tgz',
|
89
|
+
starting: 'Thu, 30 Apr 2009 06:43:10 GMT',
|
90
|
+
expires: 'Fri, 16 Oct 2009 06:31:56 GMT',
|
91
|
+
resource: 'http://*',
|
92
|
+
ip_range: '216.98.35.1/32'
|
93
|
+
```
|
78
94
|
|
79
95
|
You can also pass in a path to a policy file. This will supersede any other policy options
|
80
96
|
|
81
|
-
|
82
|
-
|
97
|
+
```ruby
|
98
|
+
AWS::CF::Signer.sign_url 'http://d84l721fxaaqy9.cloudfront.net/downloads/pictures.tgz',
|
99
|
+
policy_file: '/path/to/policy/file.txt'
|
100
|
+
```
|
83
101
|
|
84
102
|
## Patches/Pull Requests
|
85
103
|
|
@@ -90,18 +108,12 @@ You can also pass in a path to a policy file. This will supersede any other poli
|
|
90
108
|
* Send me a pull request. Bonus points for topic branches.
|
91
109
|
|
92
110
|
## Attributions
|
111
|
+
Hat tip to [Anthony Bouch](https://github.com/58bits) for contributing to Dylan's effort. Only reading both gem's code I was able to figure out the signing needed for the newly introduced signed cookies.
|
93
112
|
|
94
|
-
|
95
|
-
|
96
|
-
* [http://stackoverflow.com/questions/2632457/create-signed-urls-for-cloudfront-with-ruby](http://stackoverflow.com/questions/2632457/create-signed-urls-for-cloudfront-with-ruby)
|
97
|
-
* [http://stackoverflow.com/users/315829/ben-wiseley](http://stackoverflow.com/users/315829/ben-wiseley)
|
98
|
-
* [http://stackoverflow.com/users/267804/blaz-lipuscek](http://stackoverflow.com/users/267804/blaz-lipuscek)
|
99
|
-
* [http://stackoverflow.com/users/327914/manuel-m](http://stackoverflow.com/users/327914/manuel-m)
|
100
|
-
|
101
|
-
Note: Dylan blazed a trail here - however, after several attempts, I was unable to contact Dylan in order to suggest that we combine our efforts to produce a single gem - hence the re-write and new gem here.
|
113
|
+
> Dylan blazed a trail here - however, after several attempts, I was unable to contact Dylan in order to suggest that we combine our efforts to produce a single gem - hence the re-write and new gem here. - **Anthony Bouch**
|
102
114
|
|
115
|
+
Parts of signing code taken from a question on [Stack Overflow](http://stackoverflow.com/questions/2632457/create-signed-urls-for-cloudfront-with-ruby) asked by [Ben Wiseley](http://stackoverflow.com/users/315829/ben-wiseley), and answered by [Blaz Lipuscek](http://stackoverflow.com/users/267804/blaz-lipuscek) and [Manual M](http://stackoverflow.com/users/327914/manuel-m).
|
103
116
|
|
104
|
-
License
|
105
|
-
-------
|
117
|
+
## License
|
106
118
|
|
107
|
-
cloudfront-signer is distributed under the MIT License, portions copyright ©
|
119
|
+
cloudfront-signer is distributed under the MIT License, portions copyright © 2015 Dylan Vaughn, STL, Anthony Bouch, Leonel Galán
|
data/Rakefile
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
2
|
|
3
|
-
require
|
3
|
+
require 'rspec/core/rake_task'
|
4
4
|
|
5
5
|
RSpec::Core::RakeTask.new(:spec) do |t|
|
6
6
|
t.pattern = 'spec/**/*_spec.rb'
|
7
|
-
t.rspec_opts = [
|
7
|
+
t.rspec_opts = ['--colour', '--format', 'nested']
|
8
8
|
end
|
9
9
|
|
10
|
-
task :
|
11
|
-
|
10
|
+
task default: :spec
|
data/cloudfront-signer.gemspec
CHANGED
@@ -1,22 +1,27 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
require
|
2
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'cloudfront-signer/version'
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
|
-
s.name
|
7
|
-
s.version
|
8
|
-
s.authors
|
9
|
-
s.email
|
10
|
-
s.homepage
|
11
|
-
s.summary
|
12
|
-
|
6
|
+
s.name = 'cloudfront-signer'
|
7
|
+
s.version = AWS::CF::VERSION
|
8
|
+
s.authors = ['Anthony Bouch', 'Leonel Galan']
|
9
|
+
s.email = ['tony@58bits.com', 'leonelgalan@gmail.com']
|
10
|
+
s.homepage = 'http://github.com/leonelgalan/cloudfront-signer'
|
11
|
+
s.summary = 'A gem to sign url and stream paths for Amazon CloudFront ' \
|
12
|
+
'private content.'
|
13
|
+
s.description = 'A gem to sign url and stream paths for Amazon CloudFront ' \
|
14
|
+
'private content. Includes specific signing methods for ' \
|
15
|
+
"both url and streaming paths, including html 'safe' " \
|
16
|
+
'escaped versions of each.'
|
17
|
+
s.license = 'MIT'
|
13
18
|
|
14
|
-
s.rubyforge_project =
|
15
|
-
|
16
|
-
s.add_development_dependency
|
17
|
-
|
18
|
-
s.
|
19
|
-
s.
|
20
|
-
|
21
|
-
s.require_paths = [
|
19
|
+
s.rubyforge_project = 'cloudfront-signer'
|
20
|
+
s.add_development_dependency 'rspec'
|
21
|
+
s.add_development_dependency 'codeclimate-test-reporter'
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n")
|
25
|
+
.map { |f| File.basename f }
|
26
|
+
s.require_paths = ['lib']
|
22
27
|
end
|
data/lib/cloudfront-signer.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'openssl'
|
4
4
|
require 'time'
|
5
5
|
require 'base64'
|
6
|
-
require
|
6
|
+
require 'cloudfront-signer/version'
|
7
7
|
require 'json'
|
8
8
|
|
9
9
|
module AWS
|
@@ -11,9 +11,8 @@ module AWS
|
|
11
11
|
class Signer
|
12
12
|
# Public non-inheritable class accessors
|
13
13
|
class << self
|
14
|
-
|
15
|
-
#
|
16
|
-
# been inferred from the key_path
|
14
|
+
# Public: Provides a configuration option to set the key_pair_id if it
|
15
|
+
# has not been inferred from the key_path
|
17
16
|
#
|
18
17
|
# Examples
|
19
18
|
#
|
@@ -34,12 +33,16 @@ module AWS
|
|
34
33
|
#
|
35
34
|
# Returns nothing.
|
36
35
|
def key_path=(path)
|
37
|
-
|
36
|
+
unless File.exist?(path)
|
37
|
+
fail ArgumentError,
|
38
|
+
"The signing key could not be found at #{path}"
|
39
|
+
end
|
38
40
|
@key_path = path
|
39
|
-
self.key=
|
41
|
+
self.key = File.readlines(path).join('')
|
40
42
|
end
|
41
43
|
|
42
|
-
# Public: Provides a configuration option to set the key directly as a
|
44
|
+
# Public: Provides a configuration option to set the key directly as a
|
45
|
+
# string e.g. as an ENV var
|
43
46
|
#
|
44
47
|
# Examples
|
45
48
|
#
|
@@ -54,12 +57,10 @@ module AWS
|
|
54
57
|
# Public: Provides an accessor to the key_path
|
55
58
|
#
|
56
59
|
# Returns a String value indicating the current setting
|
57
|
-
|
58
|
-
@key_path
|
59
|
-
end
|
60
|
-
|
60
|
+
attr_reader :key_path
|
61
61
|
|
62
|
-
# Public: Provides a configuration option that sets the default_expires
|
62
|
+
# Public: Provides a configuration option that sets the default_expires
|
63
|
+
# in milliseconds
|
63
64
|
#
|
64
65
|
# Examples
|
65
66
|
#
|
@@ -68,9 +69,7 @@ module AWS
|
|
68
69
|
# end
|
69
70
|
#
|
70
71
|
# Returns nothing.
|
71
|
-
|
72
|
-
@default_expires = value
|
73
|
-
end
|
72
|
+
attr_writer :default_expires
|
74
73
|
|
75
74
|
# Public: Provides an accessor to the default_expires value
|
76
75
|
#
|
@@ -79,7 +78,6 @@ module AWS
|
|
79
78
|
@default_expires ||= 3600
|
80
79
|
end
|
81
80
|
|
82
|
-
|
83
81
|
private
|
84
82
|
|
85
83
|
# Private: Provides an accessor to the RSA key value
|
@@ -104,16 +102,20 @@ module AWS
|
|
104
102
|
#
|
105
103
|
# Returns nothing.
|
106
104
|
def self.configure
|
107
|
-
|
108
105
|
yield self if block_given?
|
109
106
|
|
110
|
-
|
107
|
+
unless key_path || private_key
|
108
|
+
fail ArgumentError,
|
109
|
+
'You must supply the path to a PEM format RSA key pair.'
|
110
|
+
end
|
111
111
|
|
112
112
|
unless @key_pair_id
|
113
|
-
@key_pair_id = extract_key_pair_id(
|
114
|
-
|
113
|
+
@key_pair_id = extract_key_pair_id(key_path)
|
114
|
+
fail ArgumentError,
|
115
|
+
'The Cloudfront signing key id could not be inferred from ' \
|
116
|
+
"#{key_path}. Please supply the key pair id as a " \
|
117
|
+
'configuration argument.' unless @key_pair_id
|
115
118
|
end
|
116
|
-
|
117
119
|
end
|
118
120
|
|
119
121
|
# Public: Provides a configuration check method which tests to see
|
@@ -121,55 +123,58 @@ module AWS
|
|
121
123
|
#
|
122
124
|
# Returns a Boolean value indicating that settings are present.
|
123
125
|
def self.is_configured?
|
124
|
-
(
|
126
|
+
(key_pair_id.nil? || private_key.nil?) ? false : true
|
125
127
|
end
|
126
128
|
|
127
|
-
# Public: Sign a url - encoding any spaces in the url before signing.
|
128
|
-
# stipulates that signed URLs must not contain spaces (as
|
129
|
-
# paths/filenames which CAN contain spaces).
|
129
|
+
# Public: Sign a url - encoding any spaces in the url before signing.
|
130
|
+
# CloudFront stipulates that signed URLs must not contain spaces (as
|
131
|
+
# opposed to stream paths/filenames which CAN contain spaces).
|
130
132
|
#
|
131
133
|
# Returns a String
|
132
134
|
def self.sign_url(subject, policy_options = {})
|
133
|
-
|
135
|
+
sign subject, { remove_spaces: true }, policy_options
|
134
136
|
end
|
135
137
|
|
136
|
-
|
137
|
-
|
138
138
|
# Public: Sign a url (as above) and HTML encode the result.
|
139
139
|
#
|
140
140
|
# Returns a String
|
141
141
|
def self.sign_url_safe(subject, policy_options = {})
|
142
|
-
|
142
|
+
sign subject, { remove_spaces: true, html_escape: true }, policy_options
|
143
143
|
end
|
144
144
|
|
145
|
-
# Public: Sign a stream path part or filename (spaces are allowed in
|
146
|
-
# and so are not removed).
|
145
|
+
# Public: Sign a stream path part or filename (spaces are allowed in
|
146
|
+
# stream paths and so are not removed).
|
147
147
|
#
|
148
148
|
# Returns a String
|
149
|
-
def self.sign_path(subject, policy_options ={})
|
150
|
-
|
149
|
+
def self.sign_path(subject, policy_options = {})
|
150
|
+
sign subject, { remove_spaces: false }, policy_options
|
151
151
|
end
|
152
152
|
|
153
153
|
# Public: Sign a stream path or filename and HTML encode the result.
|
154
154
|
#
|
155
155
|
# Returns a String
|
156
|
-
def self.sign_path_safe(subject, policy_options ={})
|
157
|
-
|
156
|
+
def self.sign_path_safe(subject, policy_options = {})
|
157
|
+
sign subject,
|
158
|
+
{ remove_spaces: false, html_escape: true },
|
159
|
+
policy_options
|
158
160
|
end
|
159
161
|
|
160
|
-
# Public: Builds a signed url or stream resource name with optional
|
161
|
-
# policy options
|
162
|
+
# Public: Builds a signed url or stream resource name with optional
|
163
|
+
# configuration and policy options
|
162
164
|
#
|
163
165
|
# Returns a String
|
164
166
|
def self.sign(subject, configuration_options = {}, policy_options = {})
|
165
|
-
# If the url or stream path already has a query string parameter -
|
167
|
+
# If the url or stream path already has a query string parameter -
|
168
|
+
# append to that.
|
166
169
|
separator = subject =~ /\?/ ? '&' : '?'
|
167
170
|
|
168
|
-
if configuration_options[:remove_spaces]
|
169
|
-
subject.gsub!(/\s/, "%20")
|
170
|
-
end
|
171
|
+
subject.gsub!(/\s/, '%20') if configuration_options[:remove_spaces]
|
171
172
|
|
172
|
-
result = subject +
|
173
|
+
result = subject +
|
174
|
+
separator +
|
175
|
+
signed_params(subject, policy_options).collect do |key, value|
|
176
|
+
"#{key}=#{value}"
|
177
|
+
end.join('&')
|
173
178
|
|
174
179
|
if configuration_options[:html_escape]
|
175
180
|
return html_encode(result)
|
@@ -178,8 +183,8 @@ module AWS
|
|
178
183
|
end
|
179
184
|
end
|
180
185
|
|
181
|
-
# Public: Sign a subject url or stream resource name with optional policy
|
182
|
-
# It returns raw params to be used in urls or cookies
|
186
|
+
# Public: Sign a subject url or stream resource name with optional policy
|
187
|
+
# options. It returns raw params to be used in urls or cookies
|
183
188
|
#
|
184
189
|
# Returns a Hash
|
185
190
|
def self.signed_params(subject, policy_options = {})
|
@@ -189,12 +194,13 @@ module AWS
|
|
189
194
|
policy = IO.read(policy_options[:policy_file])
|
190
195
|
result['Policy'] = encode_policy(policy)
|
191
196
|
else
|
192
|
-
policy_options[:expires] = epoch_time(policy_options[:expires] ||
|
197
|
+
policy_options[:expires] = epoch_time(policy_options[:expires] ||
|
198
|
+
Time.now + default_expires)
|
193
199
|
|
194
200
|
if policy_options.keys.size <= 1
|
195
201
|
# Canned Policy - shorter URL
|
196
202
|
expires_at = policy_options[:expires]
|
197
|
-
policy = %
|
203
|
+
policy = %{{"Statement":[{"Resource":"#{subject}","Condition":{"DateLessThan":{"AWS:EpochTime":#{expires_at}}}}]}}
|
198
204
|
result['Expires'] = expires_at
|
199
205
|
else
|
200
206
|
# Custom Policy
|
@@ -208,13 +214,23 @@ module AWS
|
|
208
214
|
'Key-Pair-Id' => @key_pair_id
|
209
215
|
end
|
210
216
|
|
211
|
-
# Private helper methods
|
212
217
|
private
|
213
218
|
|
214
219
|
def self.generate_custom_policy(resource, options)
|
215
|
-
conditions = {
|
216
|
-
|
217
|
-
|
220
|
+
conditions = {
|
221
|
+
'DateLessThan' => {
|
222
|
+
'AWS:EpochTime' => epoch_time(options[:expires])
|
223
|
+
}
|
224
|
+
}
|
225
|
+
|
226
|
+
conditions['DateGreaterThan'] = {
|
227
|
+
'AWS:EpochTime' => epoch_time(options[:starting])
|
228
|
+
} if options[:starting]
|
229
|
+
|
230
|
+
conditions['IpAddress'] = {
|
231
|
+
'AWS:SourceIp' => options[:ip_range]
|
232
|
+
} if options[:ip_range]
|
233
|
+
|
218
234
|
{
|
219
235
|
'Statement' => [{
|
220
236
|
'Resource' => resource,
|
@@ -228,28 +244,33 @@ module AWS
|
|
228
244
|
when String then Time.parse(timelike).to_i
|
229
245
|
when Time then timelike.to_i
|
230
246
|
when Fixnum then timelike
|
231
|
-
else
|
247
|
+
else fail ArgumentError,
|
248
|
+
'Invalid argument - String, Fixnum or Time required - ' \
|
249
|
+
"#{timelike.class} passed."
|
232
250
|
end
|
233
251
|
end
|
234
252
|
|
235
253
|
def self.encode_policy(policy)
|
236
|
-
url_encode
|
254
|
+
url_encode Base64.encode64(policy)
|
237
255
|
end
|
238
256
|
|
239
257
|
def self.create_signature(policy)
|
240
|
-
url_encode
|
258
|
+
url_encode Base64.encode64(
|
259
|
+
private_key.sign(OpenSSL::Digest::SHA1.new, (policy))
|
260
|
+
)
|
241
261
|
end
|
242
262
|
|
243
263
|
def self.extract_key_pair_id(key_path)
|
244
|
-
File.basename(key_path) =~ /^pk-(.*).pem$/ ?
|
264
|
+
File.basename(key_path) =~ /^pk-(.*).pem$/ ? Regexp.last_match[1] : nil
|
245
265
|
end
|
246
266
|
|
247
267
|
def self.url_encode(s)
|
248
|
-
s.gsub('+','-').gsub('=','_').gsub('/','~').gsub(/\n/,
|
268
|
+
s.gsub('+', '-').gsub('=', '_').gsub('/', '~').gsub(/\n/, '')
|
269
|
+
.gsub(' ', '')
|
249
270
|
end
|
250
271
|
|
251
272
|
def self.html_encode(s)
|
252
|
-
|
273
|
+
s.gsub('?', '%3F').gsub('=', '%3D').gsub('&', '%26')
|
253
274
|
end
|
254
275
|
end
|
255
276
|
end
|
@@ -3,12 +3,12 @@ require 'rails/generators'
|
|
3
3
|
|
4
4
|
module Cloudfront
|
5
5
|
class InstallGenerator < Rails::Generators::Base
|
6
|
-
source_root File.expand_path(
|
7
|
-
|
8
|
-
desc
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
7
|
+
|
8
|
+
desc 'This generator creates an initializer file at config/initializers'
|
9
9
|
def add_initializer
|
10
|
-
template
|
10
|
+
template 'cloudfront-signer.rb',
|
11
|
+
'config/initializers/cloudfront-signer.rb'
|
11
12
|
end
|
12
13
|
end
|
13
14
|
end
|
14
|
-
|
@@ -1,6 +1,6 @@
|
|
1
1
|
AWS::CF::Signer.configure do |config|
|
2
2
|
config.key_path = '/path/to/keyfile.pem'
|
3
|
-
# config.key = ENV.fetch('PRIVATE_KEY')
|
4
|
-
config.key_pair_id =
|
3
|
+
# or config.key = ENV.fetch('PRIVATE_KEY')
|
4
|
+
config.key_pair_id = 'XXYYZZ'
|
5
5
|
config.default_expires = 3600
|
6
6
|
end
|
data/spec/signer_spec.rb
CHANGED
@@ -2,10 +2,12 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe AWS::CF::Signer do
|
4
4
|
let(:key_pair_id) { 'APKAIKUROOUNR2BAFUUU' }
|
5
|
-
let(:key_path)
|
6
|
-
|
5
|
+
let(:key_path) do
|
6
|
+
File.expand_path File.dirname(__FILE__) + "/keys/pk-#{key_pair_id}.pem"
|
7
|
+
end
|
8
|
+
let(:key) { File.readlines(key_path).join '' }
|
7
9
|
|
8
|
-
context
|
10
|
+
context 'configured with key and key_pair_id' do
|
9
11
|
before do
|
10
12
|
AWS::CF::Signer.configure do |config|
|
11
13
|
config.key_pair_id = key_pair_id
|
@@ -13,98 +15,92 @@ describe AWS::CF::Signer do
|
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
16
|
-
it
|
17
|
-
AWS::CF::Signer.is_configured
|
18
|
+
it 'should be configured' do
|
19
|
+
expect(AWS::CF::Signer.is_configured?).to be true
|
18
20
|
end
|
19
21
|
|
20
|
-
it
|
21
|
-
AWS::CF::Signer.send(:private_key).
|
22
|
+
it 'sets the private_key' do
|
23
|
+
expect(AWS::CF::Signer.send(:private_key)).to(
|
24
|
+
be_an_instance_of OpenSSL::PKey::RSA
|
25
|
+
)
|
22
26
|
end
|
23
27
|
|
24
|
-
it
|
25
|
-
url =
|
28
|
+
it 'should expire in one hour by default' do
|
29
|
+
url = 'http://somedomain.com/sign me'
|
26
30
|
result = AWS::CF::Signer.sign_url(url)
|
27
|
-
get_query_value(result, 'Expires').to_i.
|
31
|
+
expect(get_query_value(result, 'Expires').to_i).to(
|
32
|
+
eq Time.now.to_i + 3600
|
33
|
+
)
|
28
34
|
end
|
29
35
|
end
|
30
36
|
|
31
|
-
context
|
32
|
-
|
37
|
+
context 'configured with key_path' do
|
33
38
|
before(:each) do
|
34
39
|
AWS::CF::Signer.configure do |config|
|
35
40
|
config.key_path = key_path
|
36
|
-
#config.key_pair_id = "XXYYZZ"
|
37
|
-
#config.default_expires = 3600
|
38
41
|
end
|
39
42
|
end
|
40
43
|
|
41
|
-
describe
|
42
|
-
|
43
|
-
|
44
|
-
AWS::CF::Signer.is_configured?.should eql(true)
|
44
|
+
describe 'before default use' do
|
45
|
+
it 'should be configured' do
|
46
|
+
expect(AWS::CF::Signer.is_configured?).to be true
|
45
47
|
end
|
46
48
|
|
47
|
-
it
|
48
|
-
AWS::CF::Signer.send(:private_key).
|
49
|
+
it 'sets the private_key' do
|
50
|
+
expect(AWS::CF::Signer.send(:private_key)).to(
|
51
|
+
be_an_instance_of OpenSSL::PKey::RSA
|
52
|
+
)
|
49
53
|
end
|
50
54
|
|
51
|
-
it
|
52
|
-
AWS::CF::Signer.default_expires.
|
55
|
+
it 'should expire urls and paths in one hour by default' do
|
56
|
+
expect(AWS::CF::Signer.default_expires).to eq 3600
|
53
57
|
end
|
54
58
|
|
55
|
-
it
|
59
|
+
it 'should optionally be configured to expire urls and paths' do
|
56
60
|
AWS::CF::Signer.default_expires = 600
|
57
|
-
AWS::CF::Signer.default_expires.
|
61
|
+
expect(AWS::CF::Signer.default_expires).to eq 600
|
58
62
|
AWS::CF::Signer.default_expires = nil
|
59
63
|
end
|
60
64
|
end
|
61
65
|
|
62
|
-
describe
|
63
|
-
|
64
|
-
|
65
|
-
url
|
66
|
-
result = AWS::CF::Signer.sign_url(url)
|
67
|
-
(result =~ /\s/).should be_nil
|
66
|
+
describe 'when signing a url' do
|
67
|
+
it 'should remove spaces from the url' do
|
68
|
+
url = 'http://somedomain.com/sign me'
|
69
|
+
expect(AWS::CF::Signer.sign_url(url)).not_to match(/\s/)
|
68
70
|
end
|
69
71
|
|
70
|
-
it
|
71
|
-
url =
|
72
|
-
|
73
|
-
(result =~ /\?/).should_not be_nil
|
74
|
-
(result =~ /=/).should_not be_nil
|
75
|
-
(result =~ /&/).should_not be_nil
|
72
|
+
it 'should not html encode the signed url by default' do
|
73
|
+
url = 'http://somedomain.com/someresource?opt1=one&opt2=two'
|
74
|
+
expect(AWS::CF::Signer.sign_url(url)).to match(/\?|=|&/)
|
76
75
|
end
|
77
76
|
|
78
|
-
it
|
79
|
-
url =
|
80
|
-
|
81
|
-
(result =~ /\?/).should be_nil
|
82
|
-
(result =~ /=/).should be_nil
|
83
|
-
(result =~ /&/).should be_nil
|
77
|
+
it 'should optionally html encode the signed url' do
|
78
|
+
url = 'http://somedomain.com/someresource?opt1=one&opt2=two'
|
79
|
+
expect(AWS::CF::Signer.sign_url_safe(url)).not_to match(/\?|=|&/)
|
84
80
|
end
|
85
81
|
|
86
|
-
it
|
87
|
-
url =
|
82
|
+
it 'should expire in one hour by default' do
|
83
|
+
url = 'http://somedomain.com/sign me'
|
88
84
|
result = AWS::CF::Signer.sign_url(url)
|
89
|
-
get_query_value(result, 'Expires').to_i.
|
85
|
+
expect(get_query_value(result, 'Expires').to_i).to(
|
86
|
+
eq Time.now.to_i + 3600
|
87
|
+
)
|
90
88
|
end
|
91
89
|
|
92
|
-
it
|
93
|
-
url =
|
94
|
-
result = AWS::CF::Signer.sign_url(url, :
|
95
|
-
get_query_value(result, 'Expires').to_i.
|
90
|
+
it 'should optionally expire in ten minutes' do
|
91
|
+
url = 'http://somedomain.com/sign me'
|
92
|
+
result = AWS::CF::Signer.sign_url(url, expires: Time.now + 600)
|
93
|
+
expect(get_query_value(result, 'Expires').to_i).to(
|
94
|
+
eq Time.now.to_i + 600
|
95
|
+
)
|
96
96
|
end
|
97
|
-
|
98
97
|
end
|
99
98
|
|
100
|
-
describe
|
101
|
-
|
102
|
-
|
103
|
-
path
|
104
|
-
result = AWS::CF::Signer.sign_path(path)
|
105
|
-
(result =~ /\s/).should_not be_nil
|
99
|
+
describe 'when signing a path' do
|
100
|
+
it 'should not remove spaces from the path' do
|
101
|
+
path = '/someprefix/sign me'
|
102
|
+
expect(AWS::CF::Signer.sign_path(path)).to match(/\s/)
|
106
103
|
end
|
107
|
-
|
108
104
|
end
|
109
105
|
end
|
110
106
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'codeclimate-test-reporter'
|
2
|
+
CodeClimate::TestReporter.start
|
3
|
+
|
1
4
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
5
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
6
|
|
@@ -5,16 +8,13 @@ require 'rspec'
|
|
5
8
|
require 'cloudfront-signer'
|
6
9
|
|
7
10
|
def get_query_value(url, key)
|
8
|
-
query_string = url.slice((url =~ /\?/) + 1..-1)
|
11
|
+
query_string = url.slice((url =~ /\?/) + 1..-1)
|
9
12
|
pairs = query_string.split('&')
|
10
13
|
pairs.each do |item|
|
11
|
-
if item.start_with?(key)
|
12
|
-
return item.split('=')[1]
|
13
|
-
end
|
14
|
+
return item.split('=')[1] if item.start_with?(key)
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
17
|
-
|
18
|
-
RSpec.configure do |config|
|
18
|
+
RSpec.configure do |_config|
|
19
19
|
|
20
20
|
end
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cloudfront-signer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
4
|
+
version: 2.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anthony Bouch
|
8
|
+
- Leonel Galan
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2015-
|
12
|
+
date: 2015-05-18 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: rspec
|
@@ -24,17 +25,33 @@ dependencies:
|
|
24
25
|
- - ">="
|
25
26
|
- !ruby/object:Gem::Version
|
26
27
|
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: codeclimate-test-reporter
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
27
42
|
description: A gem to sign url and stream paths for Amazon CloudFront private content.
|
28
43
|
Includes specific signing methods for both url and streaming paths, including html
|
29
|
-
'safe'
|
44
|
+
'safe' escaped versions of each.
|
30
45
|
email:
|
31
46
|
- tony@58bits.com
|
47
|
+
- leonelgalan@gmail.com
|
32
48
|
executables: []
|
33
49
|
extensions: []
|
34
50
|
extra_rdoc_files: []
|
35
51
|
files:
|
36
52
|
- ".gitignore"
|
37
53
|
- ".rspec"
|
54
|
+
- ".travis.yml"
|
38
55
|
- ChangeLog.markdown
|
39
56
|
- Gemfile
|
40
57
|
- LICENSE
|
@@ -49,8 +66,9 @@ files:
|
|
49
66
|
- spec/keys/rsa-APKAIKUROOUNR2BAFUUU.pem
|
50
67
|
- spec/signer_spec.rb
|
51
68
|
- spec/spec_helper.rb
|
52
|
-
homepage: http://github.com/
|
53
|
-
licenses:
|
69
|
+
homepage: http://github.com/leonelgalan/cloudfront-signer
|
70
|
+
licenses:
|
71
|
+
- MIT
|
54
72
|
metadata: {}
|
55
73
|
post_install_message:
|
56
74
|
rdoc_options: []
|
@@ -77,3 +95,4 @@ test_files:
|
|
77
95
|
- spec/keys/rsa-APKAIKUROOUNR2BAFUUU.pem
|
78
96
|
- spec/signer_spec.rb
|
79
97
|
- spec/spec_helper.rb
|
98
|
+
has_rdoc:
|