Statistics
| Branch: | Tag: | Revision:

root / snf-django-lib / snf_django / lib / astakos.py @ 6c1c0738

History | View | Annotate | Download (8.7 kB)

1 8acb1f97 Kostas Papadimitriou
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 d65af928 Kostas Papadimitriou
#
3 8acb1f97 Kostas Papadimitriou
# Redistribution and use in source and binary forms, with or
4 8acb1f97 Kostas Papadimitriou
# without modification, are permitted provided that the following
5 8acb1f97 Kostas Papadimitriou
# conditions are met:
6 d65af928 Kostas Papadimitriou
#
7 8acb1f97 Kostas Papadimitriou
#   1. Redistributions of source code must retain the above
8 8acb1f97 Kostas Papadimitriou
#      copyright notice, this list of conditions and the following
9 8acb1f97 Kostas Papadimitriou
#      disclaimer.
10 d65af928 Kostas Papadimitriou
#
11 8acb1f97 Kostas Papadimitriou
#   2. Redistributions in binary form must reproduce the above
12 8acb1f97 Kostas Papadimitriou
#      copyright notice, this list of conditions and the following
13 8acb1f97 Kostas Papadimitriou
#      disclaimer in the documentation and/or other materials
14 8acb1f97 Kostas Papadimitriou
#      provided with the distribution.
15 d65af928 Kostas Papadimitriou
#
16 8acb1f97 Kostas Papadimitriou
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 8acb1f97 Kostas Papadimitriou
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 8acb1f97 Kostas Papadimitriou
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 8acb1f97 Kostas Papadimitriou
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 8acb1f97 Kostas Papadimitriou
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 8acb1f97 Kostas Papadimitriou
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 8acb1f97 Kostas Papadimitriou
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 8acb1f97 Kostas Papadimitriou
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 8acb1f97 Kostas Papadimitriou
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 8acb1f97 Kostas Papadimitriou
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 8acb1f97 Kostas Papadimitriou
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 8acb1f97 Kostas Papadimitriou
# POSSIBILITY OF SUCH DAMAGE.
28 d65af928 Kostas Papadimitriou
#
29 8acb1f97 Kostas Papadimitriou
# The views and conclusions contained in the software and
30 8acb1f97 Kostas Papadimitriou
# documentation are those of the authors and should not be
31 8acb1f97 Kostas Papadimitriou
# interpreted as representing official policies, either expressed
32 8acb1f97 Kostas Papadimitriou
# or implied, of GRNET S.A.
33 8acb1f97 Kostas Papadimitriou
34 83204389 Kostas Papadimitriou
import logging
35 83204389 Kostas Papadimitriou
36 74d988b0 Christos Stavrakakis
from urlparse import urlparse
37 74d988b0 Christos Stavrakakis
from urllib import unquote
38 8acb1f97 Kostas Papadimitriou
from django.utils import simplejson as json
39 8acb1f97 Kostas Papadimitriou
40 1a736ca8 Christos Stavrakakis
from objpool.http import PooledHTTPConnection
41 27cfa807 Vangelis Koukis
42 83204389 Kostas Papadimitriou
logger = logging.getLogger(__name__)
43 8acb1f97 Kostas Papadimitriou
44 74d988b0 Christos Stavrakakis
45 c700f742 Sofia Papagiannaki
def retry(howmany):
46 c700f742 Sofia Papagiannaki
    def execute(func):
47 c700f742 Sofia Papagiannaki
        def f(*args, **kwargs):
48 c700f742 Sofia Papagiannaki
            attempts = 0
49 cd5033bd Sofia Papagiannaki
            while True:
50 c700f742 Sofia Papagiannaki
                try:
51 c700f742 Sofia Papagiannaki
                    return func(*args, **kwargs)
52 c700f742 Sofia Papagiannaki
                except Exception, e:
53 cd5033bd Sofia Papagiannaki
                    is_last_attempt = attempts == howmany - 1
54 cd5033bd Sofia Papagiannaki
                    if is_last_attempt:
55 cd5033bd Sofia Papagiannaki
                        raise e
56 c700f742 Sofia Papagiannaki
                    if e.args:
57 c700f742 Sofia Papagiannaki
                        status = e.args[-1]
58 050485bd Sofia Papagiannaki
                        # In case of Unauthorized response
59 050485bd Sofia Papagiannaki
                        # or Not Found return directly
60 c700f742 Sofia Papagiannaki
                        if status == 401 or status == 404:
61 c700f742 Sofia Papagiannaki
                            raise e
62 c700f742 Sofia Papagiannaki
                    attempts += 1
63 c700f742 Sofia Papagiannaki
        return f
64 c700f742 Sofia Papagiannaki
    return execute
65 c700f742 Sofia Papagiannaki
66 74d988b0 Christos Stavrakakis
67 b9818693 Christos Stavrakakis
def call(token, url, headers=None, body=None, method='GET'):
68 c700f742 Sofia Papagiannaki
    p = urlparse(url)
69 d65af928 Kostas Papadimitriou
70 8acb1f97 Kostas Papadimitriou
    kwargs = {}
71 b9818693 Christos Stavrakakis
    if headers is None:
72 b9818693 Christos Stavrakakis
        headers = {}
73 b9818693 Christos Stavrakakis
    kwargs["headers"] = headers
74 8acb1f97 Kostas Papadimitriou
    kwargs['headers']['X-Auth-Token'] = token
75 890c2065 Sofia Papagiannaki
    if body:
76 890c2065 Sofia Papagiannaki
        kwargs['body'] = body
77 74d988b0 Christos Stavrakakis
        kwargs['headers'].setdefault('content-type',
78 74d988b0 Christos Stavrakakis
                                     'application/octet-stream')
79 890c2065 Sofia Papagiannaki
    kwargs['headers'].setdefault('content-length', len(body) if body else 0)
80 890c2065 Sofia Papagiannaki
81 4ab1af1a Georgios D. Tsoukalas
    with PooledHTTPConnection(p.netloc, p.scheme) as conn:
82 890c2065 Sofia Papagiannaki
        conn.request(method, p.path + '?' + p.query, **kwargs)
83 27cfa807 Vangelis Koukis
        response = conn.getresponse()
84 27cfa807 Vangelis Koukis
        headers = response.getheaders()
85 74d988b0 Christos Stavrakakis
        headers = dict((unquote(h), unquote(v)) for h, v in headers)
86 27cfa807 Vangelis Koukis
        length = response.getheader('content-length', None)
87 27cfa807 Vangelis Koukis
        data = response.read(length)
88 27cfa807 Vangelis Koukis
        status = int(response.status)
89 d65af928 Kostas Papadimitriou
90 8acb1f97 Kostas Papadimitriou
    if status < 200 or status >= 300:
91 27cfa807 Vangelis Koukis
        raise Exception(data, status)
92 83204389 Kostas Papadimitriou
93 8acb1f97 Kostas Papadimitriou
    return json.loads(data)
94 8acb1f97 Kostas Papadimitriou
95 c700f742 Sofia Papagiannaki
96 050485bd Sofia Papagiannaki
def authenticate(
97 7bffb0bd Kostas Papadimitriou
        token, authentication_url='http://127.0.0.1:8000/im/authenticate',
98 7bffb0bd Kostas Papadimitriou
        usage=False):
99 7bffb0bd Kostas Papadimitriou
100 7bffb0bd Kostas Papadimitriou
    if usage:
101 7bffb0bd Kostas Papadimitriou
        authentication_url += "?usage=1"
102 7bffb0bd Kostas Papadimitriou
103 c700f742 Sofia Papagiannaki
    return call(token, authentication_url)
104 c700f742 Sofia Papagiannaki
105 7bffb0bd Kostas Papadimitriou
106 c700f742 Sofia Papagiannaki
@retry(3)
107 890c2065 Sofia Papagiannaki
def get_displaynames(
108 050485bd Sofia Papagiannaki
        token,
109 890c2065 Sofia Papagiannaki
        uuids,
110 890c2065 Sofia Papagiannaki
        url='http://127.0.0.1:8000/user_catalogs',
111 050485bd Sofia Papagiannaki
        override_users={}):
112 0308f3a7 Sofia Papagiannaki
113 890c2065 Sofia Papagiannaki
    if override_users:
114 74d988b0 Christos Stavrakakis
        return dict((u, u) for u in uuids)
115 0308f3a7 Sofia Papagiannaki
116 c700f742 Sofia Papagiannaki
    try:
117 890c2065 Sofia Papagiannaki
        data = call(
118 74d988b0 Christos Stavrakakis
            token, url,  headers={'content-type': 'application/json'},
119 74d988b0 Christos Stavrakakis
            body=json.dumps({'uuids': uuids}), method='POST')
120 e31e4274 Sofia Papagiannaki
    except:
121 e31e4274 Sofia Papagiannaki
        raise
122 c700f742 Sofia Papagiannaki
    else:
123 890c2065 Sofia Papagiannaki
        return data.get('uuid_catalog')
124 c700f742 Sofia Papagiannaki
125 c700f742 Sofia Papagiannaki
126 c700f742 Sofia Papagiannaki
@retry(3)
127 890c2065 Sofia Papagiannaki
def get_uuids(
128 050485bd Sofia Papagiannaki
        token,
129 890c2065 Sofia Papagiannaki
        displaynames,
130 890c2065 Sofia Papagiannaki
        url='http://127.0.0.1:8000/user_catalogs',
131 050485bd Sofia Papagiannaki
        override_users={}):
132 890c2065 Sofia Papagiannaki
133 050485bd Sofia Papagiannaki
    if override_users:
134 74d988b0 Christos Stavrakakis
        return dict((u, u) for u in displaynames)
135 0308f3a7 Sofia Papagiannaki
136 c700f742 Sofia Papagiannaki
    try:
137 890c2065 Sofia Papagiannaki
        data = call(
138 74d988b0 Christos Stavrakakis
            token, url, headers={'content-type': 'application/json'},
139 74d988b0 Christos Stavrakakis
            body=json.dumps({'displaynames': displaynames}), method='POST')
140 e31e4274 Sofia Papagiannaki
    except:
141 e31e4274 Sofia Papagiannaki
        raise
142 c700f742 Sofia Papagiannaki
    else:
143 890c2065 Sofia Papagiannaki
        return data.get('displayname_catalog')
144 890c2065 Sofia Papagiannaki
145 74d988b0 Christos Stavrakakis
146 890c2065 Sofia Papagiannaki
def get_user_uuid(
147 890c2065 Sofia Papagiannaki
        token,
148 890c2065 Sofia Papagiannaki
        displayname,
149 890c2065 Sofia Papagiannaki
        url='http://127.0.0.1:8000/user_catalogs',
150 890c2065 Sofia Papagiannaki
        override_users={}):
151 890c2065 Sofia Papagiannaki
152 890c2065 Sofia Papagiannaki
    if not displayname:
153 890c2065 Sofia Papagiannaki
        return
154 890c2065 Sofia Papagiannaki
155 890c2065 Sofia Papagiannaki
    displayname_dict = get_uuids(token, [displayname], url, override_users)
156 890c2065 Sofia Papagiannaki
    return displayname_dict.get(displayname)
157 890c2065 Sofia Papagiannaki
158 890c2065 Sofia Papagiannaki
159 890c2065 Sofia Papagiannaki
def get_displayname(
160 890c2065 Sofia Papagiannaki
        token,
161 890c2065 Sofia Papagiannaki
        uuid,
162 890c2065 Sofia Papagiannaki
        url='http://127.0.0.1:8000/user_catalogs',
163 890c2065 Sofia Papagiannaki
        override_users={}):
164 890c2065 Sofia Papagiannaki
165 890c2065 Sofia Papagiannaki
    if not uuid:
166 890c2065 Sofia Papagiannaki
        return
167 c700f742 Sofia Papagiannaki
168 890c2065 Sofia Papagiannaki
    uuid_dict = get_displaynames(token, [uuid], url, override_users)
169 890c2065 Sofia Papagiannaki
    return uuid_dict.get(uuid)
170 c700f742 Sofia Papagiannaki
171 74d988b0 Christos Stavrakakis
172 09fbe62f Christos Stavrakakis
def user_for_token(token, authentication_url, usage=False):
173 8acb1f97 Kostas Papadimitriou
    if not token:
174 8acb1f97 Kostas Papadimitriou
        return None
175 d65af928 Kostas Papadimitriou
176 8acb1f97 Kostas Papadimitriou
    try:
177 7bffb0bd Kostas Papadimitriou
        return authenticate(token, authentication_url, usage=usage)
178 1055f3c6 Sofia Papagiannaki
    except Exception, e:
179 1055f3c6 Sofia Papagiannaki
        # In case of Unauthorized response return None
180 1055f3c6 Sofia Papagiannaki
        if e.args and e.args[-1] == 401:
181 1055f3c6 Sofia Papagiannaki
            return None
182 1055f3c6 Sofia Papagiannaki
        raise e
183 8acb1f97 Kostas Papadimitriou
184 74d988b0 Christos Stavrakakis
185 050485bd Sofia Papagiannaki
def get_user(
186 050485bd Sofia Papagiannaki
        request,
187 050485bd Sofia Papagiannaki
        authentication_url='http://127.0.0.1:8000/im/authenticate',
188 7bffb0bd Kostas Papadimitriou
        fallback_token=None,
189 7bffb0bd Kostas Papadimitriou
        usage=False):
190 8acb1f97 Kostas Papadimitriou
    request.user = None
191 8acb1f97 Kostas Papadimitriou
    request.user_uniq = None
192 d65af928 Kostas Papadimitriou
193 8acb1f97 Kostas Papadimitriou
    # Try to find token in a parameter or in a request header.
194 050485bd Sofia Papagiannaki
    user = user_for_token(
195 09fbe62f Christos Stavrakakis
        request.GET.get('X-Auth-Token'), authentication_url,
196 7bffb0bd Kostas Papadimitriou
        usage=usage)
197 8acb1f97 Kostas Papadimitriou
    if not user:
198 050485bd Sofia Papagiannaki
        user = user_for_token(
199 050485bd Sofia Papagiannaki
            request.META.get('HTTP_X_AUTH_TOKEN'),
200 050485bd Sofia Papagiannaki
            authentication_url,
201 7bffb0bd Kostas Papadimitriou
            usage=usage)
202 8acb1f97 Kostas Papadimitriou
    if not user:
203 09fbe62f Christos Stavrakakis
        user = user_for_token(fallback_token, authentication_url, usage=usage)
204 6b5b443b Antony Chazapis
    if not user:
205 83204389 Kostas Papadimitriou
        logger.warning("Cannot retrieve user details from %s",
206 83204389 Kostas Papadimitriou
                       authentication_url)
207 7bffb0bd Kostas Papadimitriou
        return None
208 d65af928 Kostas Papadimitriou
209 74d988b0 Christos Stavrakakis
    # use user uuid, instead of email, keep email/displayname reference
210 74d988b0 Christos Stavrakakis
    # to user_id
211 83204389 Kostas Papadimitriou
    request.user_uniq = user['uuid']
212 8acb1f97 Kostas Papadimitriou
    request.user = user
213 890c2065 Sofia Papagiannaki
    request.user_id = user.get('displayname')
214 83204389 Kostas Papadimitriou
    return user
215 d65af928 Kostas Papadimitriou
216 d65af928 Kostas Papadimitriou
217 d65af928 Kostas Papadimitriou
def get_token_from_cookie(request, cookiename):
218 d65af928 Kostas Papadimitriou
    """
219 d65af928 Kostas Papadimitriou
    Extract token from the cookie name provided. Cookie should be in the same
220 d65af928 Kostas Papadimitriou
    form as astakos service sets its cookie contents::
221 d65af928 Kostas Papadimitriou

222 d65af928 Kostas Papadimitriou
        <user_uniq>|<user_token>
223 d65af928 Kostas Papadimitriou
    """
224 d65af928 Kostas Papadimitriou
    try:
225 d65af928 Kostas Papadimitriou
        cookie_content = unquote(request.COOKIES.get(cookiename, None))
226 d65af928 Kostas Papadimitriou
        return cookie_content.split("|")[1]
227 d65af928 Kostas Papadimitriou
    except AttributeError:
228 d65af928 Kostas Papadimitriou
        pass
229 d65af928 Kostas Papadimitriou
230 d65af928 Kostas Papadimitriou
    return None
231 76a13815 Christos Stavrakakis
232 76a13815 Christos Stavrakakis
233 76a13815 Christos Stavrakakis
class UserCache(object):
234 76a13815 Christos Stavrakakis
    """uuid<->displayname user 'cache'"""
235 76a13815 Christos Stavrakakis
236 76a13815 Christos Stavrakakis
    def __init__(self, astakos_url, astakos_token, split=100):
237 76a13815 Christos Stavrakakis
        self.astakos_token = astakos_token
238 76a13815 Christos Stavrakakis
        self.astakos_url = astakos_url
239 76a13815 Christos Stavrakakis
        self.user_catalog_url = astakos_url.replace("im/authenticate",
240 76a13815 Christos Stavrakakis
                                               "service/api/user_catalogs")
241 76a13815 Christos Stavrakakis
        self.users = {}
242 76a13815 Christos Stavrakakis
243 76a13815 Christos Stavrakakis
        self.split = split
244 76a13815 Christos Stavrakakis
        assert(self.split > 0), "split must be positive"
245 76a13815 Christos Stavrakakis
246 76a13815 Christos Stavrakakis
    def fetch_names(self, uuid_list):
247 76a13815 Christos Stavrakakis
        total = len(uuid_list)
248 76a13815 Christos Stavrakakis
        split = self.split
249 76a13815 Christos Stavrakakis
250 76a13815 Christos Stavrakakis
        for start in range(0, total, split):
251 76a13815 Christos Stavrakakis
            end = start + split
252 76a13815 Christos Stavrakakis
            try:
253 76a13815 Christos Stavrakakis
                names = get_displaynames(token=self.astakos_token,
254 76a13815 Christos Stavrakakis
                                         url=self.user_catalog_url,
255 76a13815 Christos Stavrakakis
                                         uuids=uuid_list[start:end])
256 76a13815 Christos Stavrakakis
                self.users.update(names)
257 76a13815 Christos Stavrakakis
            except Exception as e:
258 76a13815 Christos Stavrakakis
                logger.error("Failed to fetch names: %s",  e)
259 76a13815 Christos Stavrakakis
260 76a13815 Christos Stavrakakis
    def get_uuid(self, name):
261 76a13815 Christos Stavrakakis
        if not name in self.users:
262 76a13815 Christos Stavrakakis
            try:
263 76a13815 Christos Stavrakakis
                self.users[name] = get_user_uuid(token=self.astakos_token,
264 76a13815 Christos Stavrakakis
                                                 url=self.user_catalog_url,
265 76a13815 Christos Stavrakakis
                                                 displayname=name)
266 76a13815 Christos Stavrakakis
            except Exception as e:
267 76a13815 Christos Stavrakakis
                logger.error("Can not get uuid for name %s: %s", name, e)
268 76a13815 Christos Stavrakakis
                self.users[name] = name
269 76a13815 Christos Stavrakakis
270 76a13815 Christos Stavrakakis
        return self.users[name]
271 76a13815 Christos Stavrakakis
272 76a13815 Christos Stavrakakis
    def get_name(self, uuid):
273 76a13815 Christos Stavrakakis
        """Do the uuid-to-email resolving"""
274 76a13815 Christos Stavrakakis
275 76a13815 Christos Stavrakakis
        if not uuid in self.users:
276 76a13815 Christos Stavrakakis
            try:
277 76a13815 Christos Stavrakakis
                self.users[uuid] = get_displayname(token=self.astakos_token,
278 76a13815 Christos Stavrakakis
                                                   url=self.user_catalog_url,
279 76a13815 Christos Stavrakakis
                                                   uuid=uuid)
280 76a13815 Christos Stavrakakis
            except Exception as e:
281 76a13815 Christos Stavrakakis
                logging.error("Can not get display name for uuid %s: %s",
282 76a13815 Christos Stavrakakis
                              uuid, e)
283 76a13815 Christos Stavrakakis
                self.users[uuid] = "-"
284 76a13815 Christos Stavrakakis
285 76a13815 Christos Stavrakakis
        return self.users[uuid]