Revision 81a906f8

b/snf-django-lib/snf_django/lib/astakos.py
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34
import logging
34
from astakosclient import AstakosClient
35
from astakosclient.errors import Unauthorized
35 36

  
36
from urlparse import urlparse
37
from urllib import unquote
38
from django.utils import simplejson as json
39 37

  
40
from objpool.http import PooledHTTPConnection
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
    with PooledHTTPConnection(p.netloc, p.scheme) as conn:
82
        conn.request(method, p.path + '?' + p.query, **kwargs)
83
        response = conn.getresponse()
84
        headers = response.getheaders()
85
        headers = dict((unquote(h), unquote(v)) for h, v in headers)
86
        length = response.getheader('content-length', None)
87
        data = response.read(length)
88
        status = int(response.status)
89

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

  
93
    return json.loads(data)
94

  
95

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

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

  
103
    return call(token, authentication_url)
104

  
105

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

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

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

  
125

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

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

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

  
145

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

  
152
    if not displayname:
153
        return
154

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

  
158

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

  
165
    if not uuid:
166
        return
167

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

  
171

  
172
def user_for_token(token, authentication_url, usage=False):
38
def user_for_token(client, token, usage=False):
173 39
    if not token:
174 40
        return None
175 41

  
176 42
    try:
177
        return authenticate(token, authentication_url, usage=usage)
178
    except Exception, e:
179
        # In case of Unauthorized response return None
180
        if e.args and e.args[-1] == 401:
181
            return None
182
        raise e
43
        return client.get_user_info(token, usage=True)
44
    except Unauthorized:
45
        return None
183 46

  
184 47

  
185
def get_user(
186
        request,
187
        astakos_url='http://127.0.0.1:8000/im/authenticate',
188
        fallback_token=None,
189
        usage=False):
48
def get_user(request, astakos_url, fallback_token=None,
49
             usage=False, logger=None):
190 50
    request.user = None
191 51
    request.user_uniq = None
192 52

  
193
    authentication_url = astakos_url + "im/authenticate"
53
    client = AstakosClient(astakos_url, retry=2, use_pool=True, logger=logger)
194 54
    # Try to find token in a parameter or in a request header.
195
    user = user_for_token(
196
        request.GET.get('X-Auth-Token'), authentication_url,
197
        usage=usage)
55
    user = user_for_token(client, request.GET.get('X-Auth-Token'), usage=usage)
198 56
    if not user:
199
        user = user_for_token(
200
            request.META.get('HTTP_X_AUTH_TOKEN'),
201
            authentication_url,
202
            usage=usage)
57
        user = user_for_token(client,
58
                              request.META.get('HTTP_X_AUTH_TOKEN'),
59
                              usage=usage)
203 60
    if not user:
204
        user = user_for_token(fallback_token, authentication_url, usage=usage)
61
        user = user_for_token(client, fallback_token, usage=usage)
205 62
    if not user:
206
        logger.warning("Cannot retrieve user details from %s",
207
                       authentication_url)
208 63
        return None
209 64

  
210 65
    # use user uuid, instead of email, keep email/displayname reference
......
215 70
    return user
216 71

  
217 72

  
218
def get_token_from_cookie(request, cookiename):
219
    """
220
    Extract token from the cookie name provided. Cookie should be in the same
221
    form as astakos service sets its cookie contents::
222

  
223
        <user_uniq>|<user_token>
224
    """
225
    try:
226
        cookie_content = unquote(request.COOKIES.get(cookiename, None))
227
        return cookie_content.split("|")[1]
228
    except AttributeError:
229
        pass
230

  
231
    return None
232

  
233

  
234 73
class UserCache(object):
235 74
    """uuid<->displayname user 'cache'"""
236 75

  
237
    def __init__(self, astakos_url, astakos_token, split=100):
76
    def __init__(self, astakos_url, astakos_token, split=100, logger=None):
77
        self.astakos = AstakosClient(astakos_url, retry=2,
78
                                     use_pool=True, logger=logger)
238 79
        self.astakos_token = astakos_token
239
        self.astakos_url = astakos_url
240
        self.user_catalog_url = astakos_url + "service/api/user_catalogs"
241 80
        self.users = {}
242 81

  
243 82
        self.split = split
......
250 89
        for start in range(0, total, split):
251 90
            end = start + split
252 91
            try:
253
                names = get_displaynames(token=self.astakos_token,
254
                                         url=self.user_catalog_url,
255
                                         uuids=uuid_list[start:end])
92
                names = self.astakos.service_get_usernames(
93
                    self.astakos_token, uuid_list[start:end])
256 94
                self.users.update(names)
257
            except Exception as e:
258
                logger.error("Failed to fetch names: %s",  e)
95
            except:
96
                pass
259 97

  
260 98
    def get_uuid(self, name):
261 99
        if not name in self.users:
262 100
            try:
263
                self.users[name] = get_user_uuid(token=self.astakos_token,
264
                                                 url=self.user_catalog_url,
265
                                                 displayname=name)
266
            except Exception as e:
267
                logger.error("Can not get uuid for name %s: %s", name, e)
101
                self.users[name] = \
102
                    self.astakos.service.get_uuid(
103
                        self.astakos_token, name)
104
            except:
268 105
                self.users[name] = name
269 106

  
270 107
        return self.users[name]
......
274 111

  
275 112
        if not uuid in self.users:
276 113
            try:
277
                self.users[uuid] = get_displayname(token=self.astakos_token,
278
                                                   url=self.user_catalog_url,
279
                                                   uuid=uuid)
280
            except Exception as e:
281
                logging.error("Can not get display name for uuid %s: %s",
282
                              uuid, e)
114
                self.users[uuid] = \
115
                    self.astakos.get_username(
116
                        self.astakos_token, uuid)
117
            except:
283 118
                self.users[uuid] = "-"
284 119

  
285 120
        return self.users[uuid]

Also available in: Unified diff