Skip to content

Instantly share code, notes, and snippets.

@itskingori
Last active January 1, 2016 14:29
Show Gist options
  • Save itskingori/8157675 to your computer and use it in GitHub Desktop.
Save itskingori/8157675 to your computer and use it in GitHub Desktop.
Use a canned policy to create a CloudFront signed URL in Rails (slight modification required for plain Ruby) ... includes method to create a download attachment URL as well (this one requires bucket and distribution configuration) i.e. using content disposition header with attachment
module Helper
def self.get_url_using_canned_policy(url, expires_in = 300)
# The date and time, in Unix time format (in seconds) and Coordinated
# Universal Time (UTC), that you want the URL to stop allowing access to
# the object. For example, January 1, 2013 10:00 am UTC converts to
# 1357034400 in Unix time format. For information about UTC, see RFC 3339,
# Date and Time on the Internet: Timestamps,
# http://tools.ietf.org/html/rfc3339.
expires = (Time.now.utc + expires_in).to_i
key_pair_id = 'YOUR_KEY_PAIR_ID'
# This policy is well known by CloudFront, but we still need to sign it, since it contains our parameters
policy = %Q[{"Statement":[{"Resource":"#{url}","Condition":{"DateLessThan":{"AWS:EpochTime":#{expires}}}}]}]
# The signature is a hashed and signed version of the policy statement.
# The policy contains characters that cannot be part of a URL, so we
# Base64 encode it to create a URL-safe version of the signature.
private_key = OpenSSL::PKey::RSA.new(File.read(Rails.root.join('config', 'certs', 'pk-XXXXXXXXXXXXXXXXXXX.pem')))
signature = Base64.strict_encode64(private_key.sign(OpenSSL::Digest::SHA1.new, policy))
signature.tr!('+=/', '-_~')
"#{url}?Expires=#{expires}&Signature=#{signature}&Key-Pair-Id=#{key_pair_id}"
end
# Use a canned policy to create a CloudFront signed download URL (e.g. attachments)
def self.get_download_url_using_canned_policy(url, desired_download_filename,expires_in = 300)
# all parameter names and values are escaped using the [rfc3986] percent-
# encoding (%xx) mechanism. characters not in the unreserved character set
# ([rfc3986] section 2.3) must be encoded. characters in the unreserved
# character set must not be encoded. hexadecimal characters in encodings
# must be upper case. text names and values must be encoded as utf-8 octets
# before percent-encoding them per [rfc3629].
# reserved character regexp, per section 5.1
reserved_characters = /[^a-zA-Z0-9\-\.\_\~]/
# You must do the following;
# 1) Configure your download streaming distribution behaviour to forward query strings
# 2) Restrict bucket access and grant CF read permissions on the bucket
# For more information read;
# 1) http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/QueryStringParameters.html
# 2) https://forums.aws.amazon.com/thread.jspa?messageID=421768#
# Example construct url (removed some characters & double spaces just to be safe);
# https://abcdefgxyz.cloundfront.net/filename.ext?response-content-disposition=<attachment|inline>;filename=<utf8 and urlencoded desired filename>&response-content-type=<Mimetype>
header = URI.escape %Q[attachment;filename="#{desired_download_filename.gsub(/[^\w'. ]/, '').gsub(/\s+/, ' ')}"], reserved_characters
url << "?response-content-disposition=#{header}"
# This part is the same as above so just refer to comments in the
# get_url_using_canned_policy method to know what's going on
expires = (Time.now.utc + expires_in).to_i
key_pair_id = 'YOUR_KEY_PAIR_ID'
policy = %Q[{"Statement":[{"Resource":"#{url}","Condition":{"DateLessThan":{"AWS:EpochTime":#{expires}}}}]}]
private_key = OpenSSL::PKey::RSA.new(File.read(Rails.root.join('config', 'certs', 'pk-XXXXXXXXXXXXXXXXXXX.pem')))
signature = Base64.strict_encode64(private_key.sign(OpenSSL::Digest::SHA1.new, policy))
signature.tr!('+=/', '-_~')
"#{url}&Expires=#{expires}&Signature=#{signature}&Key-Pair-Id=#{key_pair_id}"
end
end
# Call something like ... Helper.get_url_using_canned_policy(url, seconds)
# Assumes keyfile is in RAILS_ROOT/config/certs ... change if you wanna
# Thanks to https://gist.github.com/iHiD/955355
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment