Statistics
| Branch: | Tag: | Revision:

root / snf-common / synnefo / lib / astakos.py @ b9818693

History | View | Annotate | Download (7 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 urlparse import urlparse
37
from urllib import unquote
38
from django.utils import simplejson as json
39

    
40
from synnefo.lib.pool.http import get_http_connection
41

    
42
logger = logging.getLogger(__name__)
43

    
44

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

    
66

    
67
def call(token, url, headers=None, body=None, method='GET'):
68
    p = urlparse(url)
69

    
70
    kwargs = {}
71
    if headers is None:
72
        headers = {}
73
    kwargs["headers"] = headers
74
    kwargs['headers']['X-Auth-Token'] = token
75
    if body:
76
        kwargs['body'] = body
77
        kwargs['headers'].setdefault('content-type',
78
                                     'application/octet-stream')
79
    kwargs['headers'].setdefault('content-length', len(body) if body else 0)
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:
124
        raise
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:
144
        raise
145
    else:
146
        return data.get('displayname_catalog')
147

    
148

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

    
155
    if not displayname:
156
        return
157

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

    
161

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

    
168
    if not uuid:
169
        return
170

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

    
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

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

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

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

    
229

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

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

    
243
    return None