├── .gitignore ├── .rspec ├── ChangeLog.markdown ├── Gemfile ├── LICENSE ├── README.markdown ├── Rakefile ├── cloudfront-signer.gemspec ├── lib ├── cloudfront-signer.rb ├── cloudfront-signer │ └── version.rb └── generators │ └── cloudfront │ └── install │ ├── install_generator.rb │ └── templates │ └── cloudfront-signer.rb └── spec ├── keys ├── pk-APKAIKUROOUNR2BAFUUU.pem └── rsa-APKAIKUROOUNR2BAFUUU.pem ├── signer_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | .bundle 3 | Gemfile.lock 4 | pkg/* 5 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --colour 2 | --drb 3 | --format documentation 4 | -------------------------------------------------------------------------------- /ChangeLog.markdown: -------------------------------------------------------------------------------- 1 | ### 2.2.0 / 2015-04-29 2 | * Accepted merge request from https://github.com/leonelgalan - `sign_params` method returns raw params to be used in urls or cookies. 3 | 4 | ### 2.1.2 / 2015-04-16 5 | * Accepted merge request from https://github.com/tuvistavie - fixing custom policy bug. 6 | 7 | ### 2.1.1 / 2013-10-31 8 | * Added changelog file 9 | * Aceppted merge request from https://github.com/bullfight, Refactored configuration to allow for key to be passed in directly. 10 | 11 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | # Specify your gem's dependencies in aws-cf-signer.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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. 22 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # cloudfront-signer 2 | ## New Repository 3 | As discussed in [#5](https://github.com/58bits/cloudfront-signer/pull/5) the new home for this gem is [leonelgalan/cloudfront-signer](https://github.com/leonelgalan/cloudfront-signer). The new repository, won't be a fork, allowing users to post issues. 4 | 5 | --- 6 | 7 | See the [ChangeLog](https://github.com/58bits/cloudfront-signer/blob/master/ChangeLog.markdown) for details of this release. 8 | 9 | A fork and re-write of Dylan Vaughn's [signing gem](https://github.com/stlondemand/aws_cf_signer). 10 | 11 | See Amazon docs for [Using a Signed URL to Serve Private Content](http://docs.amazonwebservices.com/AmazonCloudFront/latest/DeveloperGuide/index.html?PrivateContent.html) 12 | 13 | This version uses all class methods and a configure method to set options. 14 | 15 | 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. 16 | 17 | ## Installation 18 | 19 | The original gem was published as `aws_cf_signer`. Use `gem install aws_cf_signer` to install that version. 20 | 21 | This gem has been publised as `cloudfront-signer`. Use `gem install cloudfront-signer` to install this gem. 22 | 23 | Alternatively, place a copy of cloudfront-signer.rb (and the cloundfront-signer sub directory) in your lib directory. 24 | 25 | 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: 26 | 27 | ``` 28 | bundle exec rails g cloudfront:install 29 | ``` 30 | 31 | and customizing the resulting `config/initializers/cloudfront-signer.rb` file. 32 | 33 | ### Generated `cloudfront-signer.rb` 34 | 35 | AWS::CF::Signer.configure do |config| 36 | config.key_path = '/path/to/keyfile.pem' 37 | # config.key = ENV.fetch('PRIVATE_KEY') # key_path not required if key supplied directly 38 | config.key_pair_id = "XXYYZZ" 39 | config.default_expires = 3600 # 1hr as seconds 40 | end 41 | 42 | ## Usage 43 | 44 | Call the class `sign_url` or `sign_path` method with optional policy settings. 45 | 46 | AWS::CF::Signer.sign_url 'http://mydomain/path/to/my/content' 47 | 48 | or 49 | 50 | AWS::CF::Signer.sign_url 'http://mydomain/path/to/my/content', :expires => Time.now + 600 51 | 52 | Streaming paths can be signed with the `sign_path` method. 53 | 54 | AWS::CF::Signer.sign_path 'path/to/my/content' 55 | 56 | or 57 | 58 | AWS::CF::Signer.sign_path 'path/to/my/content', :expires => Time.now + 600 59 | 60 | 61 | 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. 62 | 63 | 64 | ### Custom Policies 65 | 66 | See Example Custom Policy 1 at above AWS doc link 67 | 68 | url = AWS::CF::Signer.sign_url('http://d604721fxaaqy9.cloudfront.net/training/orientation.avi', 69 | :expires => 'Sat, 14 Nov 2009 22:20:00 GMT', 70 | :resource => 'http://d604721fxaaqy9.cloudfront.net/training/*', 71 | :ip_range => '145.168.143.0/24' 72 | ) 73 | 74 | See Example Custom Policy 2 at above AWS doc link 75 | 76 | url = AWS::CF::Signer.sign_url('http://d84l721fxaaqy9.cloudfront.net/downloads/pictures.tgz', 77 | :starting => 'Thu, 30 Apr 2009 06:43:10 GMT', 78 | :expires => 'Fri, 16 Oct 2009 06:31:56 GMT', 79 | :resource => 'http://*', 80 | :ip_range => '216.98.35.1/32' 81 | ) 82 | 83 | You can also pass in a path to a policy file. This will supersede any other policy options 84 | 85 | url = AWS::CF::Signer.sign_url('http://d84l721fxaaqy9.cloudfront.net/downloads/pictures.tgz', :policy_file => '/path/to/policy/file.txt') 86 | 87 | 88 | ## Patches/Pull Requests 89 | 90 | * Fork the project. 91 | * Make your feature addition or bug fix. 92 | * Add tests for it. 93 | * Commit 94 | * Send me a pull request. Bonus points for topic branches. 95 | 96 | ## Attributions 97 | 98 | Parts of signing code taken from a question on Stack Overflow asked by Ben Wiseley, and answered by Blaz Lipuscek and Manual M: 99 | 100 | * [http://stackoverflow.com/questions/2632457/create-signed-urls-for-cloudfront-with-ruby](http://stackoverflow.com/questions/2632457/create-signed-urls-for-cloudfront-with-ruby) 101 | * [http://stackoverflow.com/users/315829/ben-wiseley](http://stackoverflow.com/users/315829/ben-wiseley) 102 | * [http://stackoverflow.com/users/267804/blaz-lipuscek](http://stackoverflow.com/users/267804/blaz-lipuscek) 103 | * [http://stackoverflow.com/users/327914/manuel-m](http://stackoverflow.com/users/327914/manuel-m) 104 | 105 | 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. 106 | 107 | 108 | License 109 | ------- 110 | 111 | cloudfront-signer is distributed under the MIT License, portions copyright © 2011 Dylan Vaughn, STL, Anthony Bouch 112 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | 3 | require "rspec/core/rake_task" 4 | 5 | RSpec::Core::RakeTask.new(:spec) do |t| 6 | t.pattern = 'spec/**/*_spec.rb' 7 | t.rspec_opts = ["--colour", "--format", "nested"] 8 | end 9 | 10 | task :default => :spec 11 | 12 | -------------------------------------------------------------------------------- /cloudfront-signer.gemspec: -------------------------------------------------------------------------------- 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 gem to sign url and stream paths for Amazon CloudFront private content. 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 23 | -------------------------------------------------------------------------------- /lib/cloudfront-signer.rb: -------------------------------------------------------------------------------- 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 | require 'json' 8 | 9 | module AWS 10 | module CF 11 | class Signer 12 | # Public non-inheritable class accessors 13 | class << self 14 | 15 | # Public: Provides a configuration option to set the key_pair_id if it has not 16 | # been inferred from the key_path 17 | # 18 | # Examples 19 | # 20 | # AWS::CF::Signer.configure do |config| 21 | # config.key_pair_id = "XXYYZZ" 22 | # end 23 | # 24 | # Returns a String value indicating the current setting 25 | attr_accessor :key_pair_id 26 | 27 | # Public: Provides a configuration option that sets the key_path 28 | # 29 | # Examples 30 | # 31 | # AWS::CF::Signer.configure do |config| 32 | # config.key_path = "/path/to/your/keyfile.pem" 33 | # end 34 | # 35 | # Returns nothing. 36 | def key_path=(path) 37 | raise ArgumentError.new("The signing key could not be found at #{path}") unless File.exists?(path) 38 | @key_path = path 39 | self.key=(File.readlines(path).join("")) 40 | end 41 | 42 | # Public: Provides a configuration option to set the key directly as a string e.g. as an ENV var 43 | # 44 | # Examples 45 | # 46 | # AWS::CF::Signer.configure do |config| 47 | # config.key = ENV.fetch('KEY') 48 | # end 49 | # Returns nothing. 50 | def key=(key) 51 | @key = OpenSSL::PKey::RSA.new(key) 52 | end 53 | 54 | # Public: Provides an accessor to the key_path 55 | # 56 | # Returns a String value indicating the current setting 57 | def key_path 58 | @key_path 59 | end 60 | 61 | 62 | # Public: Provides a configuration option that sets the default_expires in seconds 63 | # 64 | # Examples 65 | # 66 | # AWS::CF::Signer.configure do |config| 67 | # config.default_expires = 3600 68 | # end 69 | # 70 | # Returns nothing. 71 | def default_expires=(value) 72 | @default_expires = value 73 | end 74 | 75 | # Public: Provides an accessor to the default_expires value 76 | # 77 | # Returns an Integer value indicating the current setting (seconds) 78 | def default_expires 79 | @default_expires ||= 3600 80 | end 81 | 82 | 83 | private 84 | 85 | # Private: Provides an accessor to the RSA key value 86 | # 87 | # Returns an RSA key pair. 88 | def private_key 89 | @key 90 | end 91 | end 92 | 93 | # Public: Provides a simple way to configure the signing class. 94 | # 95 | # Yields self. 96 | # 97 | # Examples 98 | # 99 | # AWS::CF::Signer.configure do |config| 100 | # config.key_path = "/path/to/yourkeyfile.pem" 101 | # config.key_pair_id = "XXYYZZ" 102 | # config.default_expires = 3600 # 1hr in seconds 103 | # end 104 | # 105 | # Returns nothing. 106 | def self.configure 107 | 108 | yield self if block_given? 109 | 110 | raise ArgumentError.new("You must supply the path to a PEM format RSA key pair.") unless self.key_path || private_key 111 | 112 | unless @key_pair_id 113 | @key_pair_id = extract_key_pair_id(self.key_path) 114 | raise ArgumentError.new("The Cloudfront signing key id could not be inferred from #{self.key_path}. Please supply the key pair id as a configuration argument.") unless @key_pair_id 115 | end 116 | 117 | end 118 | 119 | # Public: Provides a configuration check method which tests to see 120 | # that the key_path, key_pair_id and private key values have all been set. 121 | # 122 | # Returns a Boolean value indicating that settings are present. 123 | def self.is_configured? 124 | (self.key_pair_id.nil? || private_key.nil?) ? false : true 125 | end 126 | 127 | # Public: Sign a url - encoding any spaces in the url before signing. CloudFront 128 | # stipulates that signed URLs must not contain spaces (as opposed to stream 129 | # paths/filenames which CAN contain spaces). 130 | # 131 | # Returns a String 132 | def self.sign_url(subject, policy_options = {}) 133 | self.sign(subject, {:remove_spaces => true}, policy_options) 134 | end 135 | 136 | 137 | 138 | # Public: Sign a url (as above) and HTML encode the result. 139 | # 140 | # Returns a String 141 | def self.sign_url_safe(subject, policy_options = {}) 142 | self.sign(subject, {:remove_spaces => true, :html_escape => true}, policy_options) 143 | end 144 | 145 | # Public: Sign a stream path part or filename (spaces are allowed in stream paths 146 | # and so are not removed). 147 | # 148 | # Returns a String 149 | def self.sign_path(subject, policy_options ={}) 150 | self.sign(subject, {:remove_spaces => false}, policy_options) 151 | end 152 | 153 | # Public: Sign a stream path or filename and HTML encode the result. 154 | # 155 | # Returns a String 156 | def self.sign_path_safe(subject, policy_options ={}) 157 | self.sign(subject, {:remove_spaces => false, :html_escape => true}, policy_options) 158 | end 159 | 160 | # Public: Builds a signed url or stream resource name with optional configuration and 161 | # policy options 162 | # 163 | # Returns a String 164 | def self.sign(subject, configuration_options = {}, policy_options = {}) 165 | # If the url or stream path already has a query string parameter - append to that. 166 | separator = subject =~ /\?/ ? '&' : '?' 167 | 168 | if configuration_options[:remove_spaces] 169 | subject.gsub!(/\s/, "%20") 170 | end 171 | 172 | result = subject + separator + self.signed_params(subject, policy_options).collect{ |k,v| "#{k}=#{v}" }.join('&') 173 | 174 | if configuration_options[:html_escape] 175 | return html_encode(result) 176 | else 177 | return result 178 | end 179 | end 180 | 181 | # Public: Sign a subject url or stream resource name with optional policy options. 182 | # It returns raw params to be used in urls or cookies 183 | # 184 | # Returns a Hash 185 | def self.signed_params(subject, policy_options = {}) 186 | result = {} 187 | 188 | if policy_options[:policy_file] 189 | policy = IO.read(policy_options[:policy_file]) 190 | result['Policy'] = encode_policy(policy) 191 | else 192 | policy_options[:expires] = epoch_time(policy_options[:expires] || Time.now + default_expires) 193 | 194 | if policy_options.keys.size <= 1 195 | # Canned Policy - shorter URL 196 | expires_at = policy_options[:expires] 197 | policy = %({"Statement":[{"Resource":"#{subject}","Condition":{"DateLessThan":{"AWS:EpochTime":#{expires_at}}}}]}) 198 | result['Expires'] = expires_at 199 | else 200 | # Custom Policy 201 | resource = policy_options[:resource] || subject 202 | policy = generate_custom_policy(resource, policy_options) 203 | result['Policy'] = encode_policy(policy) 204 | end 205 | end 206 | 207 | result.merge 'Signature' => create_signature(policy), 208 | 'Key-Pair-Id' => @key_pair_id 209 | end 210 | 211 | # Private helper methods 212 | private 213 | 214 | def self.generate_custom_policy(resource, options) 215 | conditions = { 'DateLessThan' => { 'AWS:EpochTime' => epoch_time(options[:expires]) } } 216 | conditions['DateGreaterThan'] = { 'AWS:EpochTime' => epoch_time(options[:starting]) } if options[:starting] 217 | conditions['IpAddress'] = { 'AWS:SourceIp' => option[:ip_range] } if options[:ip_range] 218 | { 219 | 'Statement' => [{ 220 | 'Resource' => resource, 221 | 'Condition' => conditions 222 | }] 223 | }.to_json 224 | end 225 | 226 | def self.epoch_time(timelike) 227 | case timelike 228 | when String then Time.parse(timelike).to_i 229 | when Time then timelike.to_i 230 | when Fixnum then timelike 231 | else raise ArgumentError.new("Invalid argument - String, Fixnum or Time required - #{timelike.class} passed.") 232 | end 233 | end 234 | 235 | def self.encode_policy(policy) 236 | url_encode(Base64.encode64(policy)) 237 | end 238 | 239 | def self.create_signature(policy) 240 | url_encode(Base64.encode64(private_key.sign(OpenSSL::Digest::SHA1.new, (policy)))) 241 | end 242 | 243 | def self.extract_key_pair_id(key_path) 244 | File.basename(key_path) =~ /^pk-(.*).pem$/ ? $1 : nil 245 | end 246 | 247 | def self.url_encode(s) 248 | s.gsub('+','-').gsub('=','_').gsub('/','~').gsub(/\n/,'').gsub(' ','') 249 | end 250 | 251 | def self.html_encode(s) 252 | return s.gsub('?', '%3F').gsub('=', '%3D').gsub('&', '%26') 253 | end 254 | end 255 | end 256 | end 257 | -------------------------------------------------------------------------------- /lib/cloudfront-signer/version.rb: -------------------------------------------------------------------------------- 1 | module AWS 2 | module CF 3 | VERSION = "2.2.0" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/generators/cloudfront/install/install_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails' 2 | require 'rails/generators' 3 | 4 | module Cloudfront 5 | class InstallGenerator < Rails::Generators::Base 6 | source_root File.expand_path("../templates", __FILE__) 7 | 8 | desc "This generator creates an initializer file at config/initializers" 9 | def add_initializer 10 | template "cloudfront-signer.rb", "config/initializers/cloudfront-signer.rb" 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/generators/cloudfront/install/templates/cloudfront-signer.rb: -------------------------------------------------------------------------------- 1 | AWS::CF::Signer.configure do |config| 2 | config.key_path = '/path/to/keyfile.pem' 3 | # config.key = ENV.fetch('PRIVATE_KEY') # key_path not required if key supplied directly 4 | config.key_pair_id = "XXYYZZ" 5 | config.default_expires = 3600 6 | end 7 | -------------------------------------------------------------------------------- /spec/keys/pk-APKAIKUROOUNR2BAFUUU.pem: -------------------------------------------------------------------------------- 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----- 16 | -------------------------------------------------------------------------------- /spec/keys/rsa-APKAIKUROOUNR2BAFUUU.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCp280I7v8JBVJBN7Kdfl4eD+no 3 | yqzbLAsz9mIr07hZQ3PjVa5g3j5Q8oXioU2ycxzXephfPr83l/FTAtPSZQ94Jh6u 4 | /CdoEYXfEtFbJYQ2lHXrra36yVcyyxQ6tAKgUHdWnZ/vbItUhLnhCSqwelTNpgRz 5 | f6AKdVOtQPaZ+bnkQQIDAQAB 6 | -----END PUBLIC KEY----- 7 | -------------------------------------------------------------------------------- /spec/signer_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe AWS::CF::Signer do 4 | let(:key_pair_id) { 'APKAIKUROOUNR2BAFUUU' } 5 | let(:key_path) { File.expand_path(File.dirname(__FILE__) + "/keys/pk-#{key_pair_id}.pem") } 6 | let(:key) { File.readlines(key_path).join("") } 7 | 8 | context "configured with key and key_pair_id" do 9 | before do 10 | AWS::CF::Signer.configure do |config| 11 | config.key_pair_id = key_pair_id 12 | config.key = key 13 | end 14 | end 15 | 16 | it "should be configured" do 17 | AWS::CF::Signer.is_configured?.should eql(true) 18 | end 19 | 20 | it "sets the private_key" do 21 | AWS::CF::Signer.send(:private_key).should be_instance_of OpenSSL::PKey::RSA 22 | end 23 | 24 | it "should expire in one hour by default" do 25 | url = "http://somedomain.com/sign me" 26 | result = AWS::CF::Signer.sign_url(url) 27 | get_query_value(result, 'Expires').to_i.should eql((Time.now + 3600).to_i) 28 | end 29 | end 30 | 31 | context "configured with key_path" do 32 | 33 | before(:each) do 34 | AWS::CF::Signer.configure do |config| 35 | config.key_path = key_path 36 | #config.key_pair_id = "XXYYZZ" 37 | #config.default_expires = 3600 38 | end 39 | end 40 | 41 | describe "before default use" do 42 | 43 | it "should be configured" do 44 | AWS::CF::Signer.is_configured?.should eql(true) 45 | end 46 | 47 | it "sets the private_key" do 48 | AWS::CF::Signer.send(:private_key).should be_instance_of OpenSSL::PKey::RSA 49 | end 50 | 51 | it "should expire urls and paths in one hour by default" do 52 | AWS::CF::Signer.default_expires.should eql(3600) 53 | end 54 | 55 | it "should optionally be configured to expire urls and paths in ten minutes" do 56 | AWS::CF::Signer.default_expires = 600 57 | AWS::CF::Signer.default_expires.should eql(600) 58 | AWS::CF::Signer.default_expires = nil 59 | end 60 | end 61 | 62 | describe "when signing a url" do 63 | 64 | it "should remove spaces from the url" do 65 | url = "http://somedomain.com/sign me" 66 | result = AWS::CF::Signer.sign_url(url) 67 | (result =~ /\s/).should be_nil 68 | end 69 | 70 | it "should not html encode the signed url by default" do 71 | url = "http://somedomain.com/someresource?opt1=one&opt2=two" 72 | result = AWS::CF::Signer.sign_url(url) 73 | (result =~ /\?/).should_not be_nil 74 | (result =~ /=/).should_not be_nil 75 | (result =~ /&/).should_not be_nil 76 | end 77 | 78 | it "should optionally html encode the signed url" do 79 | url = "http://somedomain.com/someresource?opt1=one&opt2=two" 80 | result = AWS::CF::Signer.sign_url_safe(url) 81 | (result =~ /\?/).should be_nil 82 | (result =~ /=/).should be_nil 83 | (result =~ /&/).should be_nil 84 | end 85 | 86 | it "should expire in one hour by default" do 87 | url = "http://somedomain.com/sign me" 88 | result = AWS::CF::Signer.sign_url(url) 89 | get_query_value(result, 'Expires').to_i.should eql((Time.now + 3600).to_i) 90 | end 91 | 92 | it "should optionally expire in ten minutes" do 93 | url = "http://somedomain.com/sign me" 94 | result = AWS::CF::Signer.sign_url(url, :expires => Time.now + 600) 95 | get_query_value(result, 'Expires').to_i.should eql((Time.now + 600 ).to_i) 96 | end 97 | 98 | end 99 | 100 | describe "when signing a path" do 101 | 102 | it "should not remove spaces from the path" do 103 | path = "/someprefix/sign me" 104 | result = AWS::CF::Signer.sign_path(path) 105 | (result =~ /\s/).should_not be_nil 106 | end 107 | 108 | end 109 | end 110 | end 111 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 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 21 | --------------------------------------------------------------------------------