Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

This adds support for using Amazon IAM role accounts on the instance. #83

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 40 additions & 34 deletions lib/aws/s3/authentication.rb
Original file line number Diff line number Diff line change
@@ -1,68 +1,69 @@
module AWS
module S3
module S3
# All authentication is taken care of for you by the AWS::S3 library. None the less, some details of the two types
# of authentication and when they are used may be of interest to some.
#
# === Header based authentication
#
# Header based authentication is achieved by setting a special <tt>Authorization</tt> header whose value
# Header based authentication is achieved by setting a special <tt>Authorization</tt> header whose value
# is formatted like so:
#
# "AWS #{access_key_id}:#{encoded_canonical}"
#
# The <tt>access_key_id</tt> is the public key that is assigned by Amazon for a given account which you use when
# establishing your initial connection. The <tt>encoded_canonical</tt> is computed according to rules layed out
# establishing your initial connection. The <tt>encoded_canonical</tt> is computed according to rules layed out
# by Amazon which we will describe presently.
#
# ==== Generating the encoded canonical string
#
# The "canonical string", generated by the CanonicalString class, is computed by collecting the current request method,
# a set of significant headers of the current request, and the current request path into a string.
# That canonical string is then encrypted with the <tt>secret_access_key</tt> assigned by Amazon. The resulting encrypted canonical
# The "canonical string", generated by the CanonicalString class, is computed by collecting the current request method,
# a set of significant headers of the current request, and the current request path into a string.
# That canonical string is then encrypted with the <tt>secret_access_key</tt> assigned by Amazon. The resulting encrypted canonical
# string is then base 64 encoded.
#
# === Query string based authentication
#
# When accessing a restricted object from the browser, you can authenticate via the query string, by setting the following parameters:
#
# "AWSAccessKeyId=#{access_key_id}&Expires=#{expires}&Signature=#{encoded_canonical}"
#
#
# The QueryString class is responsible for generating the appropriate parameters for authentication via the
# query string.
#
# The <tt>access_key_id</tt> and <tt>encoded_canonical</tt> are the same as described in the Header based authentication section.
# The <tt>access_key_id</tt> and <tt>encoded_canonical</tt> are the same as described in the Header based authentication section.
# The <tt>expires</tt> value dictates for how long the current url is valid (by default, it will expire in 5 minutes). Expiration can be specified
# either by an absolute time (expressed in seconds since the epoch), or in relative time (in number of seconds from now).
# Details of how to customize the expiration of the url are provided in the documentation for the QueryString class.
#
# All requests made by this library use header authentication. When a query string authenticated url is needed,
# All requests made by this library use header authentication. When a query string authenticated url is needed,
# the S3Object#url method will include the appropriate query string parameters.
#
# === Full authentication specification
#
# The full specification of the authentication protocol can be found at
# http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAuthentication.html
# http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAuthentication.html
class Authentication
constant :AMAZON_HEADER_PREFIX, 'x-amz-'

# Signature is the abstract super class for the Header and QueryString authentication methods. It does the job
# of computing the canonical_string using the CanonicalString class as well as encoding the canonical string. The subclasses
# parameterize these computations and arrange them in a string form appropriate to how they are used, in one case a http request
# header value, and in the other case key/value query string parameter pairs.
class Signature < String #:nodoc:
attr_reader :request, :access_key_id, :secret_access_key, :options

def initialize(request, access_key_id, secret_access_key, options = {})
super()
@request, @access_key_id, @secret_access_key = request, access_key_id, secret_access_key
@options = options
end

private

def canonical_string
options = {}
options[:expires] = expires if expires?
options[:security_token] = @options[:security_token] if @options.has_key?(:security_token)
CanonicalString.new(request, options)
end
memoized :canonical_string
Expand All @@ -72,20 +73,20 @@ def encoded_canonical
b64_hmac = [OpenSSL::HMAC.digest(digest, secret_access_key, canonical_string)].pack("m").strip
url_encode? ? CGI.escape(b64_hmac) : b64_hmac
end

def url_encode?
!@options[:url_encode].nil?
end

def expires?
is_a? QueryString
end

def date
request['date'].to_s.strip.empty? ? Time.now : Time.parse(request['date'])
end
end

# Provides header authentication by computing the value of the Authorization header. More details about the
# various authentication schemes can be found in the docs for its containing module, Authentication.
class Header < Signature #:nodoc:
Expand All @@ -94,7 +95,7 @@ def initialize(*args)
self << "AWS #{access_key_id}:#{encoded_canonical}"
end
end

# Provides query string authentication by computing the three authorization parameters: AWSAccessKeyId, Expires and Signature.
# More details about the various authentication schemes can be found in the docs for its containing module, Authentication.
class QueryString < Signature #:nodoc:
Expand All @@ -104,9 +105,9 @@ def initialize(*args)
options[:url_encode] = true
self << build
end

private

# Will return one of three values, in the following order of precedence:
#
# 1) Seconds since the epoch explicitly passed in the +:expires+ option
Expand All @@ -117,17 +118,17 @@ def expires
return options[:expires] if options[:expires]
date.to_i + expires_in
end

def expires_in
options.has_key?(:expires_in) ? Integer(options[:expires_in]) : DEFAULT_EXPIRY
end

def build
"AWSAccessKeyId=#{access_key_id}&Expires=#{expires}&Signature=#{encoded_canonical}"
end
end
# The CanonicalString is used to generate an encrypted signature, signed with your secrect access key. It is composed of

# The CanonicalString is used to generate an encrypted signature, signed with your secrect access key. It is composed of
# data related to the given request for which it provides authentication. This data includes the request method, request headers,
# and the request path. Both Header and QueryString use it to generate their signature.
class CanonicalString < String #:nodoc:
Expand All @@ -139,7 +140,7 @@ def default_headers
def interesting_headers
['content-md5', 'content-type', 'date', amazon_header_prefix]
end

def amazon_header_prefix
/^#{AMAZON_HEADER_PREFIX}/io
end
Expand All @@ -152,7 +153,7 @@ def query_parameters
response-expires response-cache-control
response-content-disposition response-content-encoding)
end

def query_parameters_for_signature(params)
params.select {|k, v| query_parameters.include?(k)}
end
Expand All @@ -168,13 +169,13 @@ def resource_parameters
end

attr_reader :request, :headers

def initialize(request, options = {})
super()
@request = request
@headers = {}
@options = options
# "For non-authenticated or anonymous requests. A NotImplemented error result code will be returned if
# "For non-authenticated or anonymous requests. A NotImplemented error result code will be returned if
# an authenticated (signed) request specifies a Host: header other than 's3.amazonaws.com'"
# (from http://docs.amazonwebservices.com/AmazonS3/2006-03-01/VirtualHosting.html)
request['Host'] = DEFAULT_HOST
Expand All @@ -185,27 +186,32 @@ def initialize(request, options = {})
def build
self << "#{request.method}\n"
ensure_date_is_valid

initialize_headers
set_expiry!

set_security_token!

headers.sort_by {|k, _| k}.each do |key, value|
value = value.to_s.strip
self << (key =~ self.class.amazon_header_prefix ? "#{key}:#{value}" : value)
self << "\n"
end
self << path
end

def initialize_headers
identify_interesting_headers
set_default_headers
end

def set_expiry!
self.headers['date'] = @options[:expires] if @options[:expires]
end


def set_security_token!
self.headers['x-amz-security-token'] = @options[:security_token] if @options[:security_token]
end

def ensure_date_is_valid
request['Date'] ||= Time.now.httpdate
end
Expand Down Expand Up @@ -249,7 +255,7 @@ def extract_significant_parameter
def resource_parameter?(key)
self.class.resource_parameters.include? key
end

def only_path
request.path[/^[^?]*/]
end
Expand Down
Loading