Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (7.1 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:
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
def get_user_uuid(
149
        token,
150
        displayname,
151
        url='http://127.0.0.1:8000/user_catalogs',
152
        override_users={}):
153

    
154
    if not displayname:
155
        return
156

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

    
160

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

    
167
    if not uuid:
168
        return
169

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

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

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

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

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

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

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

    
225

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

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

    
239
    return None