Statistics
| Branch: | Tag: | Revision:

root / snf-common / synnefo / lib / astakos.py @ 74d988b0

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={}, body=None, method='GET'):
68
    p = urlparse(url)
69

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

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

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

    
94
    return json.loads(data)
95

    
96

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

    
101
    if usage:
102
        authentication_url += "?usage=1"
103

    
104
    return call(token, authentication_url)
105

    
106

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

    
114
    if override_users:
115
        return dict((u, u) for u in uuids)
116

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

    
126

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

    
134
    if override_users:
135
        return dict((u, u) for u in displaynames)
136

    
137
    try:
138
        data = call(
139
            token, url, headers={'content-type': 'application/json'},
140
            body=json.dumps({'displaynames': displaynames}), method='POST')
141
    except:
142
        raise
143
    else:
144
        return data.get('displayname_catalog')
145

    
146

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

    
153
    if not displayname:
154
        return
155

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

    
159

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

    
166
    if not uuid:
167
        return
168

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

    
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

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

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

    
220
    # use user uuid, instead of email, keep email/displayname reference
221
    # 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