OLD | NEW |
(Empty) | |
| 1 # Copyright (c) 2011 Mitch Garnaat http://garnaat.org/ |
| 2 # Copyright (c) 2011, Eucalyptus Systems, Inc. |
| 3 # |
| 4 # Permission is hereby granted, free of charge, to any person obtaining a |
| 5 # 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, dis- |
| 8 # tribute, sublicense, and/or sell copies of the Software, and to permit |
| 9 # persons to whom the Software is furnished to do so, subject to the fol- |
| 10 # lowing conditions: |
| 11 # |
| 12 # The above copyright notice and this permission notice shall be included |
| 13 # in all copies or substantial portions of the Software. |
| 14 # |
| 15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| 16 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- |
| 17 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT |
| 18 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| 19 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| 21 # IN THE SOFTWARE. |
| 22 |
| 23 from boto.connection import AWSQueryConnection |
| 24 from boto.regioninfo import RegionInfo |
| 25 from credentials import Credentials, FederationToken, AssumedRole |
| 26 import boto |
| 27 import boto.utils |
| 28 import datetime |
| 29 import threading |
| 30 |
| 31 _session_token_cache = {} |
| 32 |
| 33 |
| 34 class STSConnection(AWSQueryConnection): |
| 35 |
| 36 DefaultRegionName = 'us-east-1' |
| 37 DefaultRegionEndpoint = 'sts.amazonaws.com' |
| 38 APIVersion = '2011-06-15' |
| 39 |
| 40 def __init__(self, aws_access_key_id=None, aws_secret_access_key=None, |
| 41 is_secure=True, port=None, proxy=None, proxy_port=None, |
| 42 proxy_user=None, proxy_pass=None, debug=0, |
| 43 https_connection_factory=None, region=None, path='/', |
| 44 converter=None, validate_certs=True): |
| 45 if not region: |
| 46 region = RegionInfo(self, self.DefaultRegionName, |
| 47 self.DefaultRegionEndpoint, |
| 48 connection_cls=STSConnection) |
| 49 self.region = region |
| 50 self._mutex = threading.Semaphore() |
| 51 AWSQueryConnection.__init__(self, aws_access_key_id, |
| 52 aws_secret_access_key, |
| 53 is_secure, port, proxy, proxy_port, |
| 54 proxy_user, proxy_pass, |
| 55 self.region.endpoint, debug, |
| 56 https_connection_factory, path, |
| 57 validate_certs=validate_certs) |
| 58 |
| 59 def _required_auth_capability(self): |
| 60 return ['sign-v2'] |
| 61 |
| 62 def _check_token_cache(self, token_key, duration=None, window_seconds=60): |
| 63 token = _session_token_cache.get(token_key, None) |
| 64 if token: |
| 65 now = datetime.datetime.utcnow() |
| 66 expires = boto.utils.parse_ts(token.expiration) |
| 67 delta = expires - now |
| 68 if delta < datetime.timedelta(seconds=window_seconds): |
| 69 msg = 'Cached session token %s is expired' % token_key |
| 70 boto.log.debug(msg) |
| 71 token = None |
| 72 return token |
| 73 |
| 74 def _get_session_token(self, duration=None, |
| 75 mfa_serial_number=None, mfa_token=None): |
| 76 params = {} |
| 77 if duration: |
| 78 params['DurationSeconds'] = duration |
| 79 if mfa_serial_number: |
| 80 params['SerialNumber'] = mfa_serial_number |
| 81 if mfa_token: |
| 82 params['TokenCode'] = mfa_token |
| 83 return self.get_object('GetSessionToken', params, |
| 84 Credentials, verb='POST') |
| 85 |
| 86 def get_session_token(self, duration=None, force_new=False, |
| 87 mfa_serial_number=None, mfa_token=None): |
| 88 """ |
| 89 Return a valid session token. Because retrieving new tokens |
| 90 from the Secure Token Service is a fairly heavyweight operation |
| 91 this module caches previously retrieved tokens and returns |
| 92 them when appropriate. Each token is cached with a key |
| 93 consisting of the region name of the STS endpoint |
| 94 concatenated with the requesting user's access id. If there |
| 95 is a token in the cache meeting with this key, the session |
| 96 expiration is checked to make sure it is still valid and if |
| 97 so, the cached token is returned. Otherwise, a new session |
| 98 token is requested from STS and it is placed into the cache |
| 99 and returned. |
| 100 |
| 101 :type duration: int |
| 102 :param duration: The number of seconds the credentials should |
| 103 remain valid. |
| 104 |
| 105 :type force_new: bool |
| 106 :param force_new: If this parameter is True, a new session token |
| 107 will be retrieved from the Secure Token Service regardless |
| 108 of whether there is a valid cached token or not. |
| 109 |
| 110 :type mfa_serial_number: str |
| 111 :param mfa_serial_number: The serial number of an MFA device. |
| 112 If this is provided and if the mfa_passcode provided is |
| 113 valid, the temporary session token will be authorized with |
| 114 to perform operations requiring the MFA device authentication. |
| 115 |
| 116 :type mfa_token: str |
| 117 :param mfa_token: The 6 digit token associated with the |
| 118 MFA device. |
| 119 """ |
| 120 token_key = '%s:%s' % (self.region.name, self.provider.access_key) |
| 121 token = self._check_token_cache(token_key, duration) |
| 122 if force_new or not token: |
| 123 boto.log.debug('fetching a new token for %s' % token_key) |
| 124 try: |
| 125 self._mutex.acquire() |
| 126 token = self._get_session_token(duration, |
| 127 mfa_serial_number, |
| 128 mfa_token) |
| 129 _session_token_cache[token_key] = token |
| 130 finally: |
| 131 self._mutex.release() |
| 132 return token |
| 133 |
| 134 def get_federation_token(self, name, duration=None, policy=None): |
| 135 """ |
| 136 :type name: str |
| 137 :param name: The name of the Federated user associated with |
| 138 the credentials. |
| 139 |
| 140 :type duration: int |
| 141 :param duration: The number of seconds the credentials should |
| 142 remain valid. |
| 143 |
| 144 :type policy: str |
| 145 :param policy: A JSON policy to associate with these credentials. |
| 146 |
| 147 """ |
| 148 params = {'Name': name} |
| 149 if duration: |
| 150 params['DurationSeconds'] = duration |
| 151 if policy: |
| 152 params['Policy'] = policy |
| 153 return self.get_object('GetFederationToken', params, |
| 154 FederationToken, verb='POST') |
| 155 |
| 156 def assume_role(self, role_arn, role_session_name, policy=None, |
| 157 duration_seconds=None, external_id=None): |
| 158 """ |
| 159 Returns a set of temporary credentials that the caller can use to |
| 160 access resources that are allowed by the temporary credentials. The |
| 161 credentials are valid for the duration that the caller specified, which |
| 162 can be from 15 minutes (900 seconds) to 1 hour (3600 seconds) |
| 163 |
| 164 :type role_arn: str |
| 165 :param role_arn: The Amazon Resource Name (ARN) of the role that the |
| 166 caller is assuming. |
| 167 |
| 168 :type role_session_name: str |
| 169 :param role_session_name: The session name of the temporary security |
| 170 credentials. The session name is part of the AssumedRoleUser. |
| 171 |
| 172 :type policy: str |
| 173 :param policy: A supplemental policy that can be associated with the |
| 174 temporary security credentials. The caller can limit the |
| 175 permissions that are available on the role's temporary security |
| 176 credentials to maintain the least amount of privileges. When a |
| 177 service call is made with the temporary security credentials, both |
| 178 policies (the role policy and supplemental policy) are checked. |
| 179 |
| 180 |
| 181 :type duration_seconds: int |
| 182 :param duration_seconds: he duration, in seconds, of the role session. |
| 183 The value can range from 900 seconds (15 minutes) to 3600 seconds |
| 184 (1 hour). By default, the value is set to 3600 seconds. |
| 185 |
| 186 :type external_id: str |
| 187 :param external_id: A unique identifier that is used by |
| 188 third-party services to ensure that they are assuming a role that |
| 189 corresponds to the correct users. For third-party services that |
| 190 have access to resources across multiple AWS accounts, the unique |
| 191 client ID helps third-party services simplify access control |
| 192 verification. |
| 193 |
| 194 :return: An instance of :class:`boto.sts.credentials.AssumedRole` |
| 195 |
| 196 """ |
| 197 params = { |
| 198 'RoleArn': role_arn, |
| 199 'RoleSessionName': role_session_name |
| 200 } |
| 201 if policy is not None: |
| 202 params['Policy'] = policy |
| 203 if duration_seconds is not None: |
| 204 params['DurationSeconds'] = duration_seconds |
| 205 if external_id is not None: |
| 206 params['ExternalId'] = external_id |
| 207 return self.get_object('AssumeRole', params, AssumedRole, verb='POST') |
OLD | NEW |