Statistics
| Branch: | Tag: | Revision:

root / snf-common / synnefo / lib / astakos.py @ 1c65202f

History | View | Annotate | Download (7.2 kB)

1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
import logging
35

    
36
from time import time, mktime
37
from urlparse import urlparse, urlsplit, urlunsplit
38
from urllib import quote, unquote, urlencode
39

    
40
from django.conf import settings
41
from django.utils import simplejson as json
42
from django.utils.http import urlencode
43

    
44
from synnefo.lib.pool.http import get_http_connection
45

    
46
logger = logging.getLogger(__name__)
47

    
48
def retry(howmany):
49
    def execute(func):
50
        def f(*args, **kwargs):
51
            attempts = 0
52
            while True:
53
                try:
54
                    return func(*args, **kwargs)
55
                except Exception, e:
56
                    is_last_attempt = attempts == howmany - 1
57
                    if is_last_attempt:
58
                        raise e
59
                    if e.args:
60
                        status = e.args[-1]
61
                        # In case of Unauthorized response
62
                        # or Not Found return directly
63
                        if status == 401 or status == 404:
64
                            raise e
65
                    attempts += 1
66
        return f
67
    return execute
68

    
69
def call(token, url, headers={}, body=None, method='GET'):
70
    p = urlparse(url)
71

    
72
    kwargs = {}
73
    kwargs['headers'] = headers
74
    kwargs['headers']['X-Auth-Token'] = token
75
    if body:
76
        kwargs['body'] = body
77
        kwargs['headers'].setdefault('content-type', 'application/octet-stream')
78
    kwargs['headers'].setdefault('content-length', len(body) if body else 0)
79

    
80

    
81
    conn = get_http_connection(p.netloc, p.scheme)
82
    try:
83
        conn.request(method, p.path + '?' + p.query, **kwargs)
84
        response = conn.getresponse()
85
        headers = response.getheaders()
86
        headers = dict((unquote(h), unquote(v)) for h,v in headers)
87
        length = response.getheader('content-length', None)
88
        data = response.read(length)
89
        status = int(response.status)
90
    finally:
91
        conn.close()
92

    
93
    if status < 200 or status >= 300:
94
        raise Exception(data, status)
95

    
96
    return json.loads(data)
97

    
98

    
99
def authenticate(
100
        token, authentication_url='http://127.0.0.1:8000/im/authenticate',
101
        usage=False):
102

    
103
    if usage:
104
        authentication_url += "?usage=1"
105

    
106
    return call(token, authentication_url)
107

    
108

    
109
@retry(3)
110
def get_displaynames(
111
        token,
112
        uuids,
113
        url='http://127.0.0.1:8000/user_catalogs',
114
        override_users={}):
115

    
116
    if override_users:
117
        return dict((u,u) for u in uuids)
118

    
119
    try:
120
        data = call(
121
            token, url,  headers={'content-type':'application/json'},
122
            body=json.dumps({'uuids':uuids}), method='POST')
123
    except Exception, e:
124
        raise e
125
    else:
126
        return data.get('uuid_catalog')
127

    
128

    
129
@retry(3)
130
def get_uuids(
131
        token,
132
        displaynames,
133
        url='http://127.0.0.1:8000/user_catalogs',
134
        override_users={}):
135

    
136
    if override_users:
137
        return dict((u,u) for u in displaynames)
138

    
139
    try:
140
        data = call(
141
            token, url, headers={'content-type':'application/json'},
142
            body=json.dumps({'displaynames':displaynames}), method='POST')
143
    except Exception, e:
144
        import traceback
145
        traceback.print_exc()
146
        raise e
147
    else:
148
        return data.get('displayname_catalog')
149

    
150
def get_user_uuid(
151
        token,
152
        displayname,
153
        url='http://127.0.0.1:8000/user_catalogs',
154
        override_users={}):
155

    
156
    if not displayname:
157
        return
158

    
159
    displayname_dict = get_uuids(token, [displayname], url, override_users)
160
    return displayname_dict.get(displayname)
161

    
162

    
163
def get_displayname(
164
        token,
165
        uuid,
166
        url='http://127.0.0.1:8000/user_catalogs',
167
        override_users={}):
168

    
169
    if not uuid:
170
        return
171

    
172
    uuid_dict = get_displaynames(token, [uuid], url, override_users)
173
    return uuid_dict.get(uuid)
174

    
175
def user_for_token(token, authentication_url, override_users, usage=False):
176
    if not token:
177
        return None
178

    
179
    if override_users:
180
        try:
181
            return {'uuid': override_users[token].decode('utf8')}
182
        except:
183
            return None
184

    
185
    try:
186
        return authenticate(token, authentication_url, usage=usage)
187
    except Exception, e:
188
        # In case of Unauthorized response return None
189
        if e.args and e.args[-1] == 401:
190
            return None
191
        raise e
192

    
193
def get_user(
194
        request,
195
        authentication_url='http://127.0.0.1:8000/im/authenticate',
196
        override_users={},
197
        fallback_token=None,
198
        usage=False):
199
    request.user = None
200
    request.user_uniq = None
201

    
202
    # Try to find token in a parameter or in a request header.
203
    user = user_for_token(
204
        request.GET.get('X-Auth-Token'), authentication_url, override_users,
205
        usage=usage)
206
    if not user:
207
        user = user_for_token(
208
            request.META.get('HTTP_X_AUTH_TOKEN'),
209
            authentication_url,
210
            override_users,
211
            usage=usage)
212
    if not user:
213
        user = user_for_token(
214
            fallback_token, authentication_url, override_users,
215
            usage=usage)
216
    if not user:
217
        logger.warning("Cannot retrieve user details from %s",
218
                       authentication_url)
219
        return None
220

    
221
    # use user uuid, instead of email, keep email/displayname reference to user_id
222
    request.user_uniq = user['uuid']
223
    request.user = user
224
    request.user_id = user.get('displayname')
225
    return user
226

    
227

    
228
def get_token_from_cookie(request, cookiename):
229
    """
230
    Extract token from the cookie name provided. Cookie should be in the same
231
    form as astakos service sets its cookie contents::
232

233
        <user_uniq>|<user_token>
234
    """
235
    try:
236
        cookie_content = unquote(request.COOKIES.get(cookiename, None))
237
        return cookie_content.split("|")[1]
238
    except AttributeError:
239
        pass
240

    
241
    return None