From d6c350f83db9ce6b0d8e650a2c2ef8b312d250c6 Mon Sep 17 00:00:00 2001 From: Leonel Galan Date: Tue, 21 Apr 2015 15:38:36 -0400 Subject: [PATCH 1/2] Separates url building and signing To be used in signing cookies. See PR for details. --- lib/cloudfront-signer.rb | 46 ++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/lib/cloudfront-signer.rb b/lib/cloudfront-signer.rb index 58ebb6d..a332681 100644 --- a/lib/cloudfront-signer.rb +++ b/lib/cloudfront-signer.rb @@ -130,7 +130,7 @@ def self.is_configured? # # Returns a String def self.sign_url(subject, policy_options = {}) - self.sign(subject, {:remove_spaces => true}, policy_options) + self.build_url(subject, {:remove_spaces => true}, policy_options) end @@ -139,7 +139,7 @@ def self.sign_url(subject, policy_options = {}) # # Returns a String def self.sign_url_safe(subject, policy_options = {}) - self.sign(subject, {:remove_spaces => true, :html_escape => true}, policy_options) + self.build_url(subject, {:remove_spaces => true, :html_escape => true}, policy_options) end # Public: Sign a stream path part or filename (spaces are allowed in stream paths @@ -147,25 +147,21 @@ def self.sign_url_safe(subject, policy_options = {}) # # Returns a String def self.sign_path(subject, policy_options ={}) - self.sign(subject, {:remove_spaces => false}, policy_options) + self.build_url(subject, {:remove_spaces => false}, policy_options) end # Public: Sign a stream path or filename and HTML encode the result. # # Returns a String def self.sign_path_safe(subject, policy_options ={}) - self.sign(subject, {:remove_spaces => false, :html_escape => true}, policy_options) + self.build_url(subject, {:remove_spaces => false, :html_escape => true}, policy_options) end - - # Public: Sign a subject url or stream resource name with optional configuration and + # Public: Builds a signed url or stream resource name with optional configuration and # policy options # # Returns a String - def self.sign(subject, configuration_options = {}, policy_options = {}) - - raise "Configure using AWS::CF::Signer.configure before signing." unless self.is_configured? - + def self.build_url(subject, configuration_options = {}, policy_options = {}) # If the url or stream path already has a query string parameter - append to that. separator = subject =~ /\?/ ? '&' : '?' @@ -173,29 +169,43 @@ def self.sign(subject, configuration_options = {}, policy_options = {}) subject.gsub!(/\s/, "%20") end + result = subject + separator + self.sign(subject, policy_options).collect{ |k,v| "#{k}=#{v}" }.join('&') + + if configuration_options[:html_escape] + return html_encode(result) + else + return result + end + end + + # Public: Sign a subject url or stream resource name with optional policy options. + # It returns raw params to be used in urls or cookies + # + # Returns a Hash + def self.sign(subject, policy_options = {}) + result = {} + if policy_options[:policy_file] policy = IO.read(policy_options[:policy_file]) - result = "#{subject}#{separator}Policy=#{encode_policy(policy)}&Signature=#{create_signature(policy)}&Key-Pair-Id=#{@key_pair_id}" + result['Policy'] = encode_policy(policy) else policy_options[:expires] = epoch_time(policy_options[:expires] || Time.now + default_expires) + if policy_options.keys.size <= 1 # Canned Policy - shorter URL expires_at = policy_options[:expires] policy = %({"Statement":[{"Resource":"#{subject}","Condition":{"DateLessThan":{"AWS:EpochTime":#{expires_at}}}}]}) - result = "#{subject}#{separator}Expires=#{expires_at}&Signature=#{create_signature(policy)}&Key-Pair-Id=#{@key_pair_id}" + result['Expires'] = expires_at else # Custom Policy resource = policy_options[:resource] || subject policy = generate_custom_policy(resource, policy_options) - result = "#{subject}#{separator}Policy=#{encode_policy(policy)}&Signature=#{create_signature(policy)}&Key-Pair-Id=#{@key_pair_id}" + result['Policy'] = encode_policy(policy) end end - if configuration_options[:html_escape] - return html_encode(result) - else - return result - end + result.merge 'Signature' => create_signature(policy), + 'Key-Pair-Id' => @key_pair_id end # Private helper methods From fedcc3182e32133e4bd0ad0b79c0106168896c91 Mon Sep 17 00:00:00 2001 From: Leonel Galan Date: Tue, 28 Apr 2015 11:55:02 -0400 Subject: [PATCH 2/2] Renames build_url back to sign This allows it to be backwards compatible with the existing gem, as discussed in https://github.com/58bits/cloudfront-signer/pull/5 ## Usage: ```ruby # In a controller, before serving the images before_action :set_cookie def set_cookie signed_params = AWS::CF::Signer.signed_params( nil, resource: 'http://images.example.com/*', expires: 1.day.from_now ) signed_params.each do |name, value| cookies["CloudFront-#{name}"] = { value: value, domain: :all, httponly: true } end end ``` --- lib/cloudfront-signer.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/cloudfront-signer.rb b/lib/cloudfront-signer.rb index a332681..e31b895 100644 --- a/lib/cloudfront-signer.rb +++ b/lib/cloudfront-signer.rb @@ -130,7 +130,7 @@ def self.is_configured? # # Returns a String def self.sign_url(subject, policy_options = {}) - self.build_url(subject, {:remove_spaces => true}, policy_options) + self.sign(subject, {:remove_spaces => true}, policy_options) end @@ -139,7 +139,7 @@ def self.sign_url(subject, policy_options = {}) # # Returns a String def self.sign_url_safe(subject, policy_options = {}) - self.build_url(subject, {:remove_spaces => true, :html_escape => true}, policy_options) + self.sign(subject, {:remove_spaces => true, :html_escape => true}, policy_options) end # Public: Sign a stream path part or filename (spaces are allowed in stream paths @@ -147,21 +147,21 @@ def self.sign_url_safe(subject, policy_options = {}) # # Returns a String def self.sign_path(subject, policy_options ={}) - self.build_url(subject, {:remove_spaces => false}, policy_options) + self.sign(subject, {:remove_spaces => false}, policy_options) end # Public: Sign a stream path or filename and HTML encode the result. # # Returns a String def self.sign_path_safe(subject, policy_options ={}) - self.build_url(subject, {:remove_spaces => false, :html_escape => true}, policy_options) + self.sign(subject, {:remove_spaces => false, :html_escape => true}, policy_options) end # Public: Builds a signed url or stream resource name with optional configuration and # policy options # # Returns a String - def self.build_url(subject, configuration_options = {}, policy_options = {}) + def self.sign(subject, configuration_options = {}, policy_options = {}) # If the url or stream path already has a query string parameter - append to that. separator = subject =~ /\?/ ? '&' : '?' @@ -169,7 +169,7 @@ def self.build_url(subject, configuration_options = {}, policy_options = {}) subject.gsub!(/\s/, "%20") end - result = subject + separator + self.sign(subject, policy_options).collect{ |k,v| "#{k}=#{v}" }.join('&') + result = subject + separator + self.signed_params(subject, policy_options).collect{ |k,v| "#{k}=#{v}" }.join('&') if configuration_options[:html_escape] return html_encode(result) @@ -182,7 +182,7 @@ def self.build_url(subject, configuration_options = {}, policy_options = {}) # It returns raw params to be used in urls or cookies # # Returns a Hash - def self.sign(subject, policy_options = {}) + def self.signed_params(subject, policy_options = {}) result = {} if policy_options[:policy_file]