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