cloudfront-signer 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.markdown +103 -0
- data/Rakefile +11 -0
- data/cloudfront-signer.gemspec +22 -0
- data/lib/cloudfront-signer.rb +188 -0
- data/lib/cloudfront-signer/version.rb +5 -0
- data/spec/keys/pk-APKAIKUROOUNR2BAFUUU.pem +15 -0
- data/spec/keys/rsa-APKAIKUROOUNR2BAFUUU.pem +6 -0
- data/spec/signer_spec.rb +74 -0
- data/spec/spec_helper.rb +20 -0
- metadata +76 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2011 Anthony Bouch
|
2
|
+
Portions Copyright (c) 2011 Dylan Vaughn
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
cloudfront-signer
|
2
|
+
=================
|
3
|
+
|
4
|
+
A fork of Dylan Vaughn's [excellent signing gem](https://github.com/stlondemand/aws_cf_signer).
|
5
|
+
|
6
|
+
See Amazon docs for [Using a Signed URL to Serve Private Content](http://docs.amazonwebservices.com/AmazonCloudFront/latest/DeveloperGuide/index.html?PrivateContent.html)
|
7
|
+
|
8
|
+
This version uses all class methods and a configure method to initialize options.
|
9
|
+
|
10
|
+
Seperate helper methods exist for safe signing of urls and stream paths, each of which has slightly different requirements. For example, urls must not contain any spaces, whereas a stream path can. Also we might not want to html escape a url or path if it is being supplied to a JavaScript block or Flash object.
|
11
|
+
|
12
|
+
Installation
|
13
|
+
------------
|
14
|
+
|
15
|
+
The original gem was published as `aws_cf_signer`. Use `gem install aws_cf_signer` to install that version.
|
16
|
+
|
17
|
+
This gem has been publised as `cloudfront-signer`. Use `gem install cloudfront-signer` to install this gem.
|
18
|
+
|
19
|
+
Alternatively, place a copy of cloudfront-signer.rb (and the cloundfront-signer sub directory) in your lib directory.
|
20
|
+
|
21
|
+
In either case the signing class must be configured - supplying the path to a signing key.
|
22
|
+
|
23
|
+
In a Rails app this can be done by creating an initializer script in the /config/initializers directory.
|
24
|
+
|
25
|
+
e.g.
|
26
|
+
|
27
|
+
require 'cloudfront-signer'
|
28
|
+
|
29
|
+
AWS::CF::Signer.configure! "#{Rails.root}/keys/pk-APKAIKURNAUNR2BDSFDMA.pem"
|
30
|
+
|
31
|
+
|
32
|
+
Usage
|
33
|
+
-----
|
34
|
+
|
35
|
+
Call the class `sign_url` or `sign_path` method with optional policy settings.
|
36
|
+
|
37
|
+
AWS::CF::Signer.sign_url 'http://mydomain/path/to/my/content'
|
38
|
+
|
39
|
+
or
|
40
|
+
|
41
|
+
AWS::CF::Signer.sign_url 'http://mydomain/path/to/my/content', :expires => Time.now + 600
|
42
|
+
|
43
|
+
Streaming paths can be signed with the `sign_path` method.
|
44
|
+
|
45
|
+
AWS::CF::Signer.sign_path 'path/to/my/content'
|
46
|
+
|
47
|
+
or
|
48
|
+
|
49
|
+
AWS::CF::Signer.sign_path 'path/to/my/content', :expires => Time.now + 600
|
50
|
+
|
51
|
+
|
52
|
+
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.
|
53
|
+
|
54
|
+
|
55
|
+
### Custom Policies
|
56
|
+
|
57
|
+
See Example Custom Policy 1 at above AWS doc link
|
58
|
+
|
59
|
+
url = AWS::CF::Signer.sign_url('http://d604721fxaaqy9.cloudfront.net/training/orientation.avi',
|
60
|
+
:expires => 'Sat, 14 Nov 2009 22:20:00 GMT',
|
61
|
+
:resource => 'http://d604721fxaaqy9.cloudfront.net/training/*',
|
62
|
+
:ip_range => '145.168.143.0/24'
|
63
|
+
)
|
64
|
+
|
65
|
+
See Example Custom Policy 2 at above AWS doc link
|
66
|
+
|
67
|
+
url = AWS::CF::Signer.sign_url('http://d84l721fxaaqy9.cloudfront.net/downloads/pictures.tgz',
|
68
|
+
:starting => 'Thu, 30 Apr 2009 06:43:10 GMT',
|
69
|
+
:expires => 'Fri, 16 Oct 2009 06:31:56 GMT',
|
70
|
+
:resource => 'http://*',
|
71
|
+
:ip_range => '216.98.35.1/32'
|
72
|
+
)
|
73
|
+
|
74
|
+
You can also pass in a path to a policy file. This will supersede any other policy options
|
75
|
+
|
76
|
+
url = AWS::CF::Signer.sign_url('http://d84l721fxaaqy9.cloudfront.net/downloads/pictures.tgz', :policy_file => '/path/to/policy/file.txt')
|
77
|
+
|
78
|
+
|
79
|
+
Note on Patches/Pull Requests (verbatum from Dylan's original repository)
|
80
|
+
-------------------------------------------------------------------------
|
81
|
+
|
82
|
+
* Fork the project.
|
83
|
+
* Make your feature addition or bug fix.
|
84
|
+
* Add tests for it. This is important so I don't break it in a
|
85
|
+
future version unintentionally.
|
86
|
+
* Commit, do not mess with rakefile, version, or history.
|
87
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
88
|
+
* Send me a pull request. Bonus points for topic branches.
|
89
|
+
|
90
|
+
Attributions
|
91
|
+
------------
|
92
|
+
|
93
|
+
Parts of signing code taken from a question on Stack Overflow asked by Ben Wiseley, and answered by Blaz Lipuscek and Manual M:
|
94
|
+
|
95
|
+
* [http://stackoverflow.com/questions/2632457/create-signed-urls-for-cloudfront-with-ruby](http://stackoverflow.com/questions/2632457/create-signed-urls-for-cloudfront-with-ruby)
|
96
|
+
* [http://stackoverflow.com/users/315829/ben-wiseley](http://stackoverflow.com/users/315829/ben-wiseley)
|
97
|
+
* [http://stackoverflow.com/users/267804/blaz-lipuscek](http://stackoverflow.com/users/267804/blaz-lipuscek)
|
98
|
+
* [http://stackoverflow.com/users/327914/manuel-m](http://stackoverflow.com/users/327914/manuel-m)
|
99
|
+
|
100
|
+
License
|
101
|
+
-------
|
102
|
+
|
103
|
+
cloudfront-signer is distributed under the MIT License, portions copyright © 2011 Dylan Vaughn, STL, Anthony Bouch
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "cloudfront-signer/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "cloudfront-signer"
|
7
|
+
s.version = AWS::CF::VERSION
|
8
|
+
s.authors = ["Anthony Bouch"]
|
9
|
+
s.email = ["tony@58bits.com"]
|
10
|
+
s.homepage = "http://github.com/58bits/cloudfront-signer"
|
11
|
+
s.summary = %q{A gem to sign url and stream paths for Amazon CloudFront private content.}
|
12
|
+
s.description = %q{A fork of Dylan Vaughn's excellent signing gem - https://github.com/stlondemand/aws_cf_signer. The gem has been rewritten to use class methods and includes specific signing methods for both url and streaming paths, including html 'safe' escpaed versions of each.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "cloudfront-signer"
|
15
|
+
|
16
|
+
s.add_development_dependency "rspec"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# A re-write of https://github.com/stlondemand/aws_cf_signer
|
2
|
+
#
|
3
|
+
require 'openssl'
|
4
|
+
require 'time'
|
5
|
+
require 'base64'
|
6
|
+
require "cloudfront-signer/version"
|
7
|
+
|
8
|
+
module AWS
|
9
|
+
module CF
|
10
|
+
class Signer
|
11
|
+
# Public non-inheritable class accessors
|
12
|
+
#----------------------------------------------------------------------------
|
13
|
+
class << self
|
14
|
+
attr_accessor :key_pair_id
|
15
|
+
|
16
|
+
def key_path
|
17
|
+
@key_path
|
18
|
+
end
|
19
|
+
|
20
|
+
def key_path=(path)
|
21
|
+
raise ArgumentError.new("The signing key could not be found at #{path}") unless File.exists?(path)
|
22
|
+
@key_path = path
|
23
|
+
@key = OpenSSL::PKey::RSA.new(File.readlines(path).join(""))
|
24
|
+
end
|
25
|
+
|
26
|
+
def default_expires
|
27
|
+
@default_expires ||= 3600
|
28
|
+
end
|
29
|
+
|
30
|
+
def default_expires=(value)
|
31
|
+
raise ArgumentError.new("The default expires value must be an integer") unless value.is_a?(Integer)
|
32
|
+
@default_expires = value
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Private key
|
38
|
+
#----------------------------------------------------------------------------
|
39
|
+
def private_key
|
40
|
+
@key
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
#----------------------------------------------------------------------------
|
45
|
+
def self.is_configured?
|
46
|
+
if self.key_path && self.key_pair_id && private_key
|
47
|
+
return true
|
48
|
+
else
|
49
|
+
return false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
# Configure the signing class.
|
55
|
+
#----------------------------------------------------------------------------
|
56
|
+
def self.configure!(key_path, options = {})
|
57
|
+
|
58
|
+
raise ArgumentError.new("You must supply the path to a PEM format private RSA key.") if key_path.nil?
|
59
|
+
self.key_path = key_path
|
60
|
+
|
61
|
+
@key_pair_id = options[:key_pair_id] || extract_key_pair_id(key_path)
|
62
|
+
unless @key_pair_id
|
63
|
+
raise ArgumentError.new("The Cloudfront signing key id could not be inferred from #{key_path}. Please supply the key pair id as a configuration arguemet.")
|
64
|
+
end
|
65
|
+
|
66
|
+
@default_expires = options[:default_expires] ? options[:default_expires] : 3600
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
# Signing methods
|
71
|
+
#----------------------------------------------------------------------------
|
72
|
+
|
73
|
+
# Sign a url - encoding any spaces in the url before signing. CloudFront
|
74
|
+
# stipulates that signed URLs must not contain spaces (as opposed to stream
|
75
|
+
# paths/filenames which CAN contain spaces).
|
76
|
+
#----------------------------------------------------------------------------
|
77
|
+
def self.sign_url(subject, policy_options = {})
|
78
|
+
self.sign(subject, {:remove_spaces => true}, policy_options)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Sign a url (as above) and HTML encode the result.
|
82
|
+
#----------------------------------------------------------------------------
|
83
|
+
def self.sign_url_safe(subject, policy_options = {})
|
84
|
+
self.sign(subject, {:remove_spaces => true, :html_escape => true}, policy_options)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Sign a stream path part or filename (spaces are allowed in stream paths
|
88
|
+
# and so are not removed).
|
89
|
+
#----------------------------------------------------------------------------
|
90
|
+
def self.sign_path(subject, policy_options ={})
|
91
|
+
self.sign(subject, {:remove_spaces => false}, policy_options)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Sign a stream path or filename and HTML encode the result.
|
95
|
+
#----------------------------------------------------------------------------
|
96
|
+
def self.sign_path_safe(subject, policy_options ={})
|
97
|
+
self.sign(subject, {:remove_spaces => false, :html_escape => true}, policy_options)
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
# Sign a subject url or stream resource name with optional configuration and
|
102
|
+
# policy options
|
103
|
+
#----------------------------------------------------------------------------
|
104
|
+
def self.sign(subject, configuration_options = {}, policy_options = {})
|
105
|
+
|
106
|
+
raise "Configure using AWS::CF.Signer.configure! before signing." unless self.is_configured?
|
107
|
+
|
108
|
+
# If the url or stream path already has a query string parameter - append to that.
|
109
|
+
separator = subject =~ /\?/ ? '&' : '?'
|
110
|
+
|
111
|
+
if configuration_options[:remove_spaces]
|
112
|
+
subject.gsub!(/\s/, "%20")
|
113
|
+
end
|
114
|
+
|
115
|
+
if policy_options[:policy_file]
|
116
|
+
policy = IO.read(policy_options[:policy_file])
|
117
|
+
result = "#{subject}#{separator}Policy=#{encode_policy(policy)}&Signature=#{create_signature(policy)}&Key-Pair-Id=#{@key_pair_id}"
|
118
|
+
else
|
119
|
+
if policy_options.keys.size <= 1
|
120
|
+
# Canned Policy - shorter URL
|
121
|
+
expires_at = epoch_time(policy_options[:expires] || Time.now + self.default_expires)
|
122
|
+
policy = %({"Statement":[{"Resource":"#{subject}","Condition":{"DateLessThan":{"AWS:EpochTime":#{expires_at}}}}]})
|
123
|
+
result = "#{subject}#{separator}Expires=#{expires_at}&Signature=#{create_signature(policy)}&Key-Pair-Id=#{@key_pair_id}"
|
124
|
+
else
|
125
|
+
# Custom Policy
|
126
|
+
resource = policy_options[:resource] || subject
|
127
|
+
policy = generate_custom_policy(resource, policy_options)
|
128
|
+
result = "#{subject}#{separator}Policy=#{encode_policy(policy)}&Signature=#{create_signature(policy)}&Key-Pair-Id=#{@key_pair_id}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
if configuration_options[:html_escape]
|
133
|
+
return html_encode(result)
|
134
|
+
else
|
135
|
+
return result
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
# Private helper methods
|
141
|
+
#----------------------------------------------------------------------------
|
142
|
+
private
|
143
|
+
|
144
|
+
|
145
|
+
#----------------------------------------------------------------------------
|
146
|
+
def self.generate_custom_policy(resource, options)
|
147
|
+
conditions = ["\"DateLessThan\":{\"AWS:EpochTime\":#{epoch_time(options[:expires])}}"]
|
148
|
+
conditions << "\"DateGreaterThan\":{\"AWS:EpochTime\":#{epoch_time(options[:starting])}}" if options[:starting]
|
149
|
+
conditions << "\"IpAddress\":{\"AWS:SourceIp\":\"#{options[:ip_range]}\"" if options[:ip_range]
|
150
|
+
%({"Statement":[{"Resource":"#{resource}","Condition":{#{conditions.join(',')}}}}]})
|
151
|
+
end
|
152
|
+
|
153
|
+
#----------------------------------------------------------------------------
|
154
|
+
def self.epoch_time(timelike)
|
155
|
+
case timelike
|
156
|
+
when String then Time.parse(timelike).to_i
|
157
|
+
when Time then timelike.to_i
|
158
|
+
else raise ArgumentError.new("Invalid argument - String or Time required - #{timelike.class} passed.")
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
#----------------------------------------------------------------------------
|
163
|
+
def self.encode_policy(policy)
|
164
|
+
url_encode(Base64.encode64(policy))
|
165
|
+
end
|
166
|
+
|
167
|
+
#----------------------------------------------------------------------------
|
168
|
+
def self.create_signature(policy)
|
169
|
+
url_encode(Base64.encode64(private_key.sign(OpenSSL::Digest::SHA1.new, (policy))))
|
170
|
+
end
|
171
|
+
|
172
|
+
#----------------------------------------------------------------------------
|
173
|
+
def self.extract_key_pair_id(key_path)
|
174
|
+
File.basename(key_path) =~ /^pk-(.*).pem$/ ? $1 : nil
|
175
|
+
end
|
176
|
+
|
177
|
+
#----------------------------------------------------------------------------
|
178
|
+
def self.url_encode(s)
|
179
|
+
s.gsub('+','-').gsub('=','_').gsub('/','~').gsub(/\n/,'').gsub(' ','')
|
180
|
+
end
|
181
|
+
|
182
|
+
#----------------------------------------------------------------------------
|
183
|
+
def self.html_encode(s)
|
184
|
+
return s.gsub('?', '%3F').gsub('=', '%3D').gsub('&', '%26')
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIICXgIBAAKBgQCp280I7v8JBVJBN7Kdfl4eD+noyqzbLAsz9mIr07hZQ3PjVa5g
|
3
|
+
3j5Q8oXioU2ycxzXephfPr83l/FTAtPSZQ94Jh6u/CdoEYXfEtFbJYQ2lHXrra36
|
4
|
+
yVcyyxQ6tAKgUHdWnZ/vbItUhLnhCSqwelTNpgRzf6AKdVOtQPaZ+bnkQQIDAQAB
|
5
|
+
AoGAXWSPTbQq4gjc+yLmwJW0pg7V67tUY4XJ+x4jSDm3CM1/sKVxpa1M0jEm0D8k
|
6
|
+
e1Ozrf6oPOZBOQ4AEEZjtTD/2Yi8U0bwG97fg9NlZddGNN2jj8pEOWY53/iVWcfb
|
7
|
+
VGXVDlhUA0uIZhKK3Sl2SW9t/8p7affjJmGKn2nGLieRKIkCQQDQmExXqRnVNtCz
|
8
|
+
qjTPt81MU4cIrzXr/tUC9s6An8OcgiTDjiIOnY3XB/F19lpMQIMEzrB7f04GrpkQ
|
9
|
+
0w6p/3NXAkEA0HXjiSyZaEoXoR2e/dTZrKw8npnjjW0CpKeSf8PK8qpFPK0UJOk7
|
10
|
+
aU0rStQmoAmygcHiw3hJ7slyVS8f9zn+JwJBAMMVbHCfadWKSm19RZ7um0ZC6Asr
|
11
|
+
MhbgYX9AK6kHwf3hiViK2TcqCrmMaDqWh6TAwMgCNfOKAAMnz2d4vEIo8kkCQQCl
|
12
|
+
qnq4gkQsWG2s8jBvg1+2VW8bkCsCMvbdyfqoJP69mUnK7bXLm7tGdTiJkE5d8zb0
|
13
|
+
3hQLyiXfaiK9xeS+gk0TAkEAtuFcd+taoBnjhVL6q0OhNuA1T1+qYr5fyzQWKKKC
|
14
|
+
+WMRi2/JCJCL/SX12q5hMq759VnzfnbgqwAq6MlPUZKEBQ==
|
15
|
+
-----END RSA PRIVATE KEY-----
|
@@ -0,0 +1,6 @@
|
|
1
|
+
-----BEGIN PUBLIC KEY-----
|
2
|
+
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCp280I7v8JBVJBN7Kdfl4eD+no
|
3
|
+
yqzbLAsz9mIr07hZQ3PjVa5g3j5Q8oXioU2ycxzXephfPr83l/FTAtPSZQ94Jh6u
|
4
|
+
/CdoEYXfEtFbJYQ2lHXrra36yVcyyxQ6tAKgUHdWnZ/vbItUhLnhCSqwelTNpgRz
|
5
|
+
f6AKdVOtQPaZ+bnkQQIDAQAB
|
6
|
+
-----END PUBLIC KEY-----
|
data/spec/signer_spec.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AWS::CF::Signer do
|
4
|
+
|
5
|
+
let(:key_path) { File.expand_path(File.dirname(__FILE__) + '/keys/pk-APKAIKUROOUNR2BAFUUU.pem') }
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
AWS::CF::Signer.configure!(key_path)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "before default use" do
|
12
|
+
|
13
|
+
it "should be configured" do
|
14
|
+
AWS::CF::Signer.is_configured?.should eql(true)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should expire urls and paths in one hour by default" do
|
18
|
+
AWS::CF::Signer.default_expires.should eql(3600)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should optionally be configured to expire urls and paths in ten minutes" do
|
22
|
+
AWS::CF::Signer.configure!(key_path, :default_expires => 600)
|
23
|
+
AWS::CF::Signer.default_expires.should eql(600)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "when signing a url" do
|
28
|
+
|
29
|
+
it "should remove spaces from the url" do
|
30
|
+
url = "http://somedomain.com/sign me"
|
31
|
+
result = AWS::CF::Signer.sign_url(url)
|
32
|
+
(result =~ /\s/).should be_nil
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should not html encode the signed url by default" do
|
36
|
+
url = "http://somedomain.com/someresource?opt1=one&opt2=two"
|
37
|
+
result = AWS::CF::Signer.sign_url(url)
|
38
|
+
(result =~ /\?/).should_not be_nil
|
39
|
+
(result =~ /=/).should_not be_nil
|
40
|
+
(result =~ /&/).should_not be_nil
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should optionally html encode the signed url" do
|
44
|
+
url = "http://somedomain.com/someresource?opt1=one&opt2=two"
|
45
|
+
result = AWS::CF::Signer.sign_url_safe(url)
|
46
|
+
(result =~ /\?/).should be_nil
|
47
|
+
(result =~ /=/).should be_nil
|
48
|
+
(result =~ /&/).should be_nil
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should expire in one hour by default" do
|
52
|
+
url = "http://somedomain.com/sign me"
|
53
|
+
result = AWS::CF::Signer.sign_url(url)
|
54
|
+
get_query_value(result, 'Expires').to_i.should eql((Time.now + 3600).to_i)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should optionally expire in ten minutes" do
|
58
|
+
url = "http://somedomain.com/sign me"
|
59
|
+
result = AWS::CF::Signer.sign_url(url, :expires => Time.now + 600)
|
60
|
+
get_query_value(result, 'Expires').to_i.should eql((Time.now + 600 ).to_i)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
describe "when signing a path" do
|
67
|
+
|
68
|
+
it "should not remove spaces from the path" do
|
69
|
+
path = "/someprefix/sign me"
|
70
|
+
result = AWS::CF::Signer.sign_path(path)
|
71
|
+
(result =~ /\s/).should_not be_nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'rspec'
|
5
|
+
require 'cloudfront-signer'
|
6
|
+
|
7
|
+
def get_query_value(url, key)
|
8
|
+
query_string = url.slice((url =~ /\?/) + 1..-1)
|
9
|
+
pairs = query_string.split('&')
|
10
|
+
pairs.each do |item|
|
11
|
+
if item.start_with?(key)
|
12
|
+
return item.split('=')[1]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cloudfront-signer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Anthony Bouch
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-08-08 00:00:00.000000000 +07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
requirement: &2151692620 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *2151692620
|
26
|
+
description: A fork of Dylan Vaughn's excellent signing gem - https://github.com/stlondemand/aws_cf_signer.
|
27
|
+
The gem has been rewritten to use class methods and includes specific signing methods
|
28
|
+
for both url and streaming paths, including html 'safe' escpaed versions of each.
|
29
|
+
email:
|
30
|
+
- tony@58bits.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- .gitignore
|
36
|
+
- Gemfile
|
37
|
+
- LICENSE
|
38
|
+
- README.markdown
|
39
|
+
- Rakefile
|
40
|
+
- cloudfront-signer.gemspec
|
41
|
+
- lib/cloudfront-signer.rb
|
42
|
+
- lib/cloudfront-signer/version.rb
|
43
|
+
- spec/keys/pk-APKAIKUROOUNR2BAFUUU.pem
|
44
|
+
- spec/keys/rsa-APKAIKUROOUNR2BAFUUU.pem
|
45
|
+
- spec/signer_spec.rb
|
46
|
+
- spec/spec_helper.rb
|
47
|
+
has_rdoc: true
|
48
|
+
homepage: http://github.com/58bits/cloudfront-signer
|
49
|
+
licenses: []
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubyforge_project: cloudfront-signer
|
68
|
+
rubygems_version: 1.6.2
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: A gem to sign url and stream paths for Amazon CloudFront private content.
|
72
|
+
test_files:
|
73
|
+
- spec/keys/pk-APKAIKUROOUNR2BAFUUU.pem
|
74
|
+
- spec/keys/rsa-APKAIKUROOUNR2BAFUUU.pem
|
75
|
+
- spec/signer_spec.rb
|
76
|
+
- spec/spec_helper.rb
|