cloudfront-signer 1.0.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.
- 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
|