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