Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Commit

Permalink
Adding support for STS. Includes adding access to service and using s…
Browse files Browse the repository at this point in the history
…ecurity tokens in S3. Other services will be added shortly. #363.
  • Loading branch information
garnaat committed Oct 11, 2011
1 parent 2d70ac2 commit 3437d7e
Show file tree
Hide file tree
Showing 13 changed files with 371 additions and 17 deletions.
21 changes: 18 additions & 3 deletions boto/__init__.py
Expand Up @@ -407,6 +407,20 @@ def connect_ses(aws_access_key_id=None, aws_secret_access_key=None, **kwargs):
from boto.ses import SESConnection
return SESConnection(aws_access_key_id, aws_secret_access_key, **kwargs)

def connect_sts(aws_access_key_id=None, aws_secret_access_key=None, **kwargs):
"""
:type aws_access_key_id: string
:param aws_access_key_id: Your AWS Access Key ID
:type aws_secret_access_key: string
:param aws_secret_access_key: Your AWS Secret Access Key
:rtype: :class:`boto.sts.STSConnection`
:return: A connection to Amazon's STS
"""
from boto.sts import STSConnection
return STSConnection(aws_access_key_id, aws_secret_access_key, **kwargs)

def connect_ia(ia_access_key_id=None, ia_secret_access_key=None,
is_secure=False, **kwargs):
"""
Expand All @@ -418,9 +432,10 @@ def connect_ia(ia_access_key_id=None, ia_secret_access_key=None,
section called "ia_access_key_id"
:type ia_secret_access_key: string
:param ia_secret_access_key: Your IA Secret Access Key. This will also look in your
boto config file for an entry in the Credentials
section called "ia_secret_access_key"
:param ia_secret_access_key: Your IA Secret Access Key. This will also
look in your boto config file for an entry
in the Credentials section called
"ia_secret_access_key"
:rtype: :class:`boto.s3.connection.S3Connection`
:return: A connection to the Internet Archive
Expand Down
9 changes: 7 additions & 2 deletions boto/auth.py
Expand Up @@ -77,7 +77,8 @@ def __init__(self, host, config, provider):
self._provider = provider
self._hmac = hmac.new(self._provider.secret_key, digestmod=sha)
if sha256:
self._hmac_256 = hmac.new(self._provider.secret_key, digestmod=sha256)
self._hmac_256 = hmac.new(self._provider.secret_key,
digestmod=sha256)
else:
self._hmac_256 = None

Expand Down Expand Up @@ -113,6 +114,9 @@ def add_auth(self, http_request, **kwargs):
if not headers.has_key('Date'):
headers['Date'] = formatdate(usegmt=True)

if self._provider.security_token:
key = self._provider.security_token_header
headers[key] = self._provider.security_token
c_string = boto.utils.canonical_string(method, auth_path, headers,
None, self._provider)
b64_hmac = self.sign_string(c_string)
Expand Down Expand Up @@ -187,7 +191,8 @@ def add_auth(self, http_request, **kwargs):
# if this is a retried request, the qs from the previous try will
# already be there, we need to get rid of that and rebuild it
http_request.path = http_request.path.split('?')[0]
http_request.path = (http_request.path + '?' + qs + '&Signature=' + urllib.quote(signature))
http_request.path = (http_request.path + '?' + qs +
'&Signature=' + urllib.quote(signature))

class QuerySignatureV0AuthHandler(QuerySignatureHelper, AuthHandler):
"""Provides Signature V0 Signing"""
Expand Down
6 changes: 4 additions & 2 deletions boto/connection.py
Expand Up @@ -364,7 +364,8 @@ class AWSAuthConnection(object):
def __init__(self, host, aws_access_key_id=None, aws_secret_access_key=None,
is_secure=True, port=None, proxy=None, proxy_port=None,
proxy_user=None, proxy_pass=None, debug=0,
https_connection_factory=None, path='/', provider='aws'):
https_connection_factory=None, path='/',
provider='aws', security_token=None):
"""
:type host: str
:param host: The host to make the connection to
Expand Down Expand Up @@ -463,7 +464,8 @@ def __init__(self, host, aws_access_key_id=None, aws_secret_access_key=None,

self.provider = Provider(provider,
aws_access_key_id,
aws_secret_access_key)
aws_secret_access_key,
security_token)

# allow config file to override default host
if self.provider.host:
Expand Down
4 changes: 3 additions & 1 deletion boto/provider.py
Expand Up @@ -151,10 +151,12 @@ class Provider(object):
}
}

def __init__(self, name, access_key=None, secret_key=None):
def __init__(self, name, access_key=None, secret_key=None,
security_token=None):
self.host = None
self.access_key = access_key
self.secret_key = secret_key
self.security_token = security_token
self.name = name
self.acl_class = self.AclClassMap[self.name]
self.canned_acls = self.CannedAclsMap[self.name]
Expand Down
4 changes: 2 additions & 2 deletions boto/s3/connection.py
Expand Up @@ -140,14 +140,14 @@ def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
proxy_user=None, proxy_pass=None,
host=DefaultHost, debug=0, https_connection_factory=None,
calling_format=SubdomainCallingFormat(), path='/',
provider='aws', bucket_class=Bucket):
provider='aws', bucket_class=Bucket, security_token=None):
self.calling_format = calling_format
self.bucket_class = bucket_class
AWSAuthConnection.__init__(self, host,
aws_access_key_id, aws_secret_access_key,
is_secure, port, proxy, proxy_port, proxy_user, proxy_pass,
debug=debug, https_connection_factory=https_connection_factory,
path=path, provider=provider)
path=path, provider=provider, security_token=security_token)

def _required_auth_capability(self):
return ['s3']
Expand Down
45 changes: 45 additions & 0 deletions boto/ses/__init__.py
Expand Up @@ -21,4 +21,49 @@
# IN THE SOFTWARE.

from connection import SESConnection
from boto.regioninfo import RegionInfo

def regions():
"""
Get all available regions for the SES service.
:rtype: list
:return: A list of :class:`boto.regioninfo.RegionInfo` instances
"""
return [RegionInfo(name='us-east-1',
endpoint='email.us-east-1.amazonaws.com',
connection_cls=SESConnection)]

def connect_to_region(region_name, **kw_params):
"""
Given a valid region name, return a
:class:`boto.sns.connection.SESConnection`.
:type: str
:param region_name: The name of the region to connect to.
:rtype: :class:`boto.sns.connection.SESConnection` or ``None``
:return: A connection to the given region, or None if an invalid region
name is given
"""
for region in regions():
if region.name == region_name:
return region.connect(**kw_params)
return None

def get_region(region_name, **kw_params):
"""
Find and return a :class:`boto.regioninfo.RegionInfo` object
given a region name.
:type: str
:param: The name of the region.
:rtype: :class:`boto.regioninfo.RegionInfo`
:return: The RegionInfo object for the given region or None if
an invalid region name is provided.
"""
for region in regions(**kw_params):
if region.name == region_name:
return region
return None
21 changes: 15 additions & 6 deletions boto/ses/connection.py
Expand Up @@ -22,6 +22,7 @@

from boto.connection import AWSAuthConnection
from boto.exception import BotoServerError
from boto.regioninfo import RegionInfo
import boto
import boto.jsonresponse

Expand All @@ -32,15 +33,23 @@
class SESConnection(AWSAuthConnection):

ResponseError = BotoServerError
DefaultHost = 'email.us-east-1.amazonaws.com'
DefaultRegionName = 'us-east-1'
DefaultRegionEndpoint = 'email.us-east-1.amazonaws.com'
APIVersion = '2010-12-01'

def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
port=None, proxy=None, proxy_port=None,
host=DefaultHost, debug=0):
AWSAuthConnection.__init__(self, host, aws_access_key_id,
aws_secret_access_key, True, port, proxy,
proxy_port, debug=debug)
is_secure=True, port=None, proxy=None, proxy_port=None,
proxy_user=None, proxy_pass=None, debug=0,
https_connection_factory=None, region=None, path='/'):
if not region:
region = RegionInfo(self, self.DefaultRegionName,
self.DefaultRegionEndpoint)
self.region = region
AWSAuthConnection.__init__(self, self.region.endpoint,
aws_access_key_id, aws_secret_access_key,
is_secure, port, proxy, proxy_port,
proxy_user, proxy_pass, debug,
https_connection_factory, path)

def _required_auth_capability(self):
return ['ses']
Expand Down
70 changes: 70 additions & 0 deletions boto/sts/__init__.py
@@ -0,0 +1,70 @@
# Copyright (c) 2010-2011 Mitch Garnaat http://garnaat.org/
# Copyright (c) 2010-2011, Eucalyptus Systems, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

from connection import STSConnection
from boto.regioninfo import RegionInfo

def regions():
"""
Get all available regions for the STS service.
:rtype: list
:return: A list of :class:`boto.regioninfo.RegionInfo` instances
"""
return [RegionInfo(name='us-east-1',
endpoint='sts.amazonaws.com',
connection_cls=STSConnection)
]

def connect_to_region(region_name, **kw_params):
"""
Given a valid region name, return a
:class:`boto.sts.connection.STSConnection`.
:type: str
:param region_name: The name of the region to connect to.
:rtype: :class:`boto.sts.connection.STSConnection` or ``None``
:return: A connection to the given region, or None if an invalid region
name is given
"""
for region in regions():
if region.name == region_name:
return region.connect(**kw_params)
return None

def get_region(region_name, **kw_params):
"""
Find and return a :class:`boto.regioninfo.RegionInfo` object
given a region name.
:type: str
:param: The name of the region.
:rtype: :class:`boto.regioninfo.RegionInfo`
:return: The RegionInfo object for the given region or None if
an invalid region name is provided.
"""
for region in regions(**kw_params):
if region.name == region_name:
return region
return None
90 changes: 90 additions & 0 deletions boto/sts/connection.py
@@ -0,0 +1,90 @@
# Copyright (c) 2011 Mitch Garnaat http://garnaat.org/
# Copyright (c) 2011, Eucalyptus Systems, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

from boto.connection import AWSQueryConnection
from boto.regioninfo import RegionInfo
from credentials import Credentials, FederationToken
import boto

class STSConnection(AWSQueryConnection):

DefaultRegionName = 'us-east-1'
DefaultRegionEndpoint = 'sts.amazonaws.com'
APIVersion = '2011-06-15'

def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
is_secure=True, port=None, proxy=None, proxy_port=None,
proxy_user=None, proxy_pass=None, debug=0,
https_connection_factory=None, region=None, path='/',
converter=None):
if not region:
region = RegionInfo(self, self.DefaultRegionName,
self.DefaultRegionEndpoint,
connection_cls=STSConnection)
self.region = region
AWSQueryConnection.__init__(self, aws_access_key_id,
aws_secret_access_key,
is_secure, port, proxy, proxy_port,
proxy_user, proxy_pass,
self.region.endpoint, debug,
https_connection_factory, path)

def _required_auth_capability(self):
return ['sign-v2']

def get_session_token(self, duration=None):
"""
:type duration: int
:param duration: The number of seconds the credentials should
remain valid.
"""
params = {}
if duration:
params['Duration'] = duration
return self.get_object('GetSessionToken', params,
Credentials, verb='POST')


def get_federation_token(self, name, duration=None, policy=None):
"""
:type name: str
:param name: The name of the Federated user associated with
the credentials.
:type duration: int
:param duration: The number of seconds the credentials should
remain valid.
:type policy: str
:param policy: A JSON policy to associate with these credentials.
"""
params = {'Name' : name}
if duration:
params['Duration'] = duration
if policy:
params['Policy'] = policy
return self.get_object('GetFederationToken', params,
FederationToken, verb='POST')


0 comments on commit 3437d7e

Please sign in to comment.