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 23import os 24import datetime 25 26import boto.utils 27from boto.compat import json 28 29 30class Credentials(object): 31 """ 32 :ivar access_key: The AccessKeyID. 33 :ivar secret_key: The SecretAccessKey. 34 :ivar session_token: The session token that must be passed with 35 requests to use the temporary credentials 36 :ivar expiration: The timestamp for when the credentials will expire 37 """ 38 39 def __init__(self, parent=None): 40 self.parent = parent 41 self.access_key = None 42 self.secret_key = None 43 self.session_token = None 44 self.expiration = None 45 self.request_id = None 46 47 @classmethod 48 def from_json(cls, json_doc): 49 """ 50 Create and return a new Session Token based on the contents 51 of a JSON document. 52 53 :type json_doc: str 54 :param json_doc: A string containing a JSON document with a 55 previously saved Credentials object. 56 """ 57 d = json.loads(json_doc) 58 token = cls() 59 token.__dict__.update(d) 60 return token 61 62 @classmethod 63 def load(cls, file_path): 64 """ 65 Create and return a new Session Token based on the contents 66 of a previously saved JSON-format file. 67 68 :type file_path: str 69 :param file_path: The fully qualified path to the JSON-format 70 file containing the previously saved Session Token information. 71 """ 72 fp = open(file_path) 73 json_doc = fp.read() 74 fp.close() 75 return cls.from_json(json_doc) 76 77 def startElement(self, name, attrs, connection): 78 return None 79 80 def endElement(self, name, value, connection): 81 if name == 'AccessKeyId': 82 self.access_key = value 83 elif name == 'SecretAccessKey': 84 self.secret_key = value 85 elif name == 'SessionToken': 86 self.session_token = value 87 elif name == 'Expiration': 88 self.expiration = value 89 elif name == 'RequestId': 90 self.request_id = value 91 else: 92 pass 93 94 def to_dict(self): 95 """ 96 Return a Python dict containing the important information 97 about this Session Token. 98 """ 99 return {'access_key': self.access_key, 100 'secret_key': self.secret_key, 101 'session_token': self.session_token, 102 'expiration': self.expiration, 103 'request_id': self.request_id} 104 105 def save(self, file_path): 106 """ 107 Persist a Session Token to a file in JSON format. 108 109 :type path: str 110 :param path: The fully qualified path to the file where the 111 the Session Token data should be written. Any previous 112 data in the file will be overwritten. To help protect 113 the credentials contained in the file, the permissions 114 of the file will be set to readable/writable by owner only. 115 """ 116 fp = open(file_path, 'w') 117 json.dump(self.to_dict(), fp) 118 fp.close() 119 os.chmod(file_path, 0o600) 120 121 def is_expired(self, time_offset_seconds=0): 122 """ 123 Checks to see if the Session Token is expired or not. By default 124 it will check to see if the Session Token is expired as of the 125 moment the method is called. However, you can supply an 126 optional parameter which is the number of seconds of offset 127 into the future for the check. For example, if you supply 128 a value of 5, this method will return a True if the Session 129 Token will be expired 5 seconds from this moment. 130 131 :type time_offset_seconds: int 132 :param time_offset_seconds: The number of seconds into the future 133 to test the Session Token for expiration. 134 """ 135 now = datetime.datetime.utcnow() 136 if time_offset_seconds: 137 now = now + datetime.timedelta(seconds=time_offset_seconds) 138 ts = boto.utils.parse_ts(self.expiration) 139 delta = ts - now 140 return delta.total_seconds() <= 0 141 142 143class FederationToken(object): 144 """ 145 :ivar credentials: A Credentials object containing the credentials. 146 :ivar federated_user_arn: ARN specifying federated user using credentials. 147 :ivar federated_user_id: The ID of the federated user using credentials. 148 :ivar packed_policy_size: A percentage value indicating the size of 149 the policy in packed form 150 """ 151 152 def __init__(self, parent=None): 153 self.parent = parent 154 self.credentials = None 155 self.federated_user_arn = None 156 self.federated_user_id = None 157 self.packed_policy_size = None 158 self.request_id = None 159 160 def startElement(self, name, attrs, connection): 161 if name == 'Credentials': 162 self.credentials = Credentials() 163 return self.credentials 164 else: 165 return None 166 167 def endElement(self, name, value, connection): 168 if name == 'Arn': 169 self.federated_user_arn = value 170 elif name == 'FederatedUserId': 171 self.federated_user_id = value 172 elif name == 'PackedPolicySize': 173 self.packed_policy_size = int(value) 174 elif name == 'RequestId': 175 self.request_id = value 176 else: 177 pass 178 179 180class AssumedRole(object): 181 """ 182 :ivar user: The assumed role user. 183 :ivar credentials: A Credentials object containing the credentials. 184 """ 185 def __init__(self, connection=None, credentials=None, user=None): 186 self._connection = connection 187 self.credentials = credentials 188 self.user = user 189 190 def startElement(self, name, attrs, connection): 191 if name == 'Credentials': 192 self.credentials = Credentials() 193 return self.credentials 194 elif name == 'AssumedRoleUser': 195 self.user = User() 196 return self.user 197 198 def endElement(self, name, value, connection): 199 pass 200 201 202class User(object): 203 """ 204 :ivar arn: The arn of the user assuming the role. 205 :ivar assume_role_id: The identifier of the assumed role. 206 """ 207 def __init__(self, arn=None, assume_role_id=None): 208 self.arn = arn 209 self.assume_role_id = assume_role_id 210 211 def startElement(self, name, attrs, connection): 212 pass 213 214 def endElement(self, name, value, connection): 215 if name == 'Arn': 216 self.arn = value 217 elif name == 'AssumedRoleId': 218 self.assume_role_id = value 219 220 221class DecodeAuthorizationMessage(object): 222 """ 223 :ivar request_id: The request ID. 224 :ivar decoded_message: The decoded authorization message (may be JSON). 225 """ 226 def __init__(self, request_id=None, decoded_message=None): 227 self.request_id = request_id 228 self.decoded_message = decoded_message 229 230 def startElement(self, name, attrs, connection): 231 pass 232 233 def endElement(self, name, value, connection): 234 if name == 'requestId': 235 self.request_id = value 236 elif name == 'DecodedMessage': 237 self.decoded_message = value 238