Revision b4b82ec4
b/Changelog | ||
---|---|---|
49 | 49 |
Re-registration with `snf-component-register' affects both the base and |
50 | 50 |
the ui URL. |
51 | 51 |
|
52 |
* Remove API call GET /account/v1.0/authenticate in favor of |
|
53 |
POST /identity/v2.0/tokens. |
|
54 |
|
|
52 | 55 |
* Management commands: |
53 | 56 |
* Introduced new commands: |
54 | 57 |
* component-show |
b/astakosclient/astakosclient/__init__.py | ||
---|---|---|
170 | 170 |
return self._get_value('_ui_prefix') |
171 | 171 |
|
172 | 172 |
@property |
173 |
def api_authenticate(self): |
|
174 |
return join_urls(self.account_prefix, "authenticate") |
|
175 |
|
|
176 |
@property |
|
177 | 173 |
def api_usercatalogs(self): |
178 | 174 |
return join_urls(self.account_prefix, "user_catalogs") |
179 | 175 |
|
... | ... | |
285 | 281 |
self.logger.error(msg % (data, str(err))) |
286 | 282 |
raise InvalidResponse(str(err), data) |
287 | 283 |
|
288 |
# ------------------------ |
|
289 |
# do a GET to ``API_AUTHENTICATE`` |
|
290 |
def get_user_info(self): |
|
291 |
"""Authenticate user and get user's info as a dictionary |
|
292 |
|
|
293 |
In case of success return user information (json parsed format). |
|
294 |
Otherwise raise an AstakosClientException. |
|
295 |
|
|
296 |
""" |
|
297 |
return self._call_astakos(self.api_authenticate) |
|
298 |
|
|
299 | 284 |
# ---------------------------------- |
300 | 285 |
# do a POST to ``API_USERCATALOGS`` (or ``API_SERVICE_USERCATALOGS``) |
301 | 286 |
# with {'uuids': uuids} |
b/astakosclient/docs/index.rst | ||
---|---|---|
40 | 40 |
|
41 | 41 |
client = AstakosClient("UQpYas7ElzWGD5yCcEXtjw", |
42 | 42 |
"https://accounts.example.com") |
43 |
user_info = client.get_user_info()
|
|
44 |
print user_info['username']
|
|
43 |
user_info = client.authenticate()
|
|
44 |
print user_info['access']['user']['id']
|
|
45 | 45 |
|
46 | 46 |
Another example where we ask for the username of a user with UUID: |
47 | 47 |
``b3de8eb0-3958-477e-als9-789af8dd352c`` |
... | ... | |
79 | 79 |
their token as well as the service endpoints one can access. In |
80 | 80 |
case of error, it raises an AstakosClientException exception. |
81 | 81 |
|
82 |
**get_user_info()** |
|
83 |
It returns a dict with the corresponding user's info. In case of |
|
84 |
error, it raises an AstakosClientException exception. |
|
85 |
|
|
86 | 82 |
**get_usernames(**\ uuids\ **)** |
87 | 83 |
Given a list of UUIDs it returns a uuid_catalog, that is a dictionary |
88 | 84 |
with the given UUIDs as keys and the corresponding user names as |
b/docs/astakos-api-guide.rst | ||
---|---|---|
20 | 20 |
========================= ================================ |
21 | 21 |
Revision Description |
22 | 22 |
========================= ================================ |
23 |
0.15 (October 29, 2013) Remove GET /authenticate in favor of POST /tokens |
|
23 | 24 |
0.14 (June 03, 2013) Remove endpoint listing |
24 | 25 |
0.14 (May 28, 2013) Extend token api with authenticate call |
25 | 26 |
0.14 (May 23, 2013) Extend api to list endpoints |
... | ... | |
79 | 80 |
User API Operations |
80 | 81 |
-------------------- |
81 | 82 |
|
82 |
The operations described in this chapter allow users to authenticate themselves, send feedback and get user uuid/displayname mappings. |
|
83 |
The operations described in this chapter allow users to send feedback and |
|
84 |
get user uuid/displayname mappings. |
|
83 | 85 |
|
84 | 86 |
All the operations require a valid user token. |
85 | 87 |
|
86 |
.. _authenticate-api-label: |
|
87 |
|
|
88 |
Authenticate |
|
89 |
^^^^^^^^^^^^ |
|
90 |
|
|
91 |
Authenticate API requests require a token. An application that wishes to connect to Astakos, but does not have a token, should redirect the user to ``/login``. (see :ref:`authentication-label`) |
|
92 |
|
|
93 |
============================== ========= ================== |
|
94 |
Uri Method Description |
|
95 |
============================== ========= ================== |
|
96 |
``/account/v1.0/authenticate`` GET Authenticate user using token |
|
97 |
============================== ========= ================== |
|
98 |
|
|
99 |
| |
|
100 |
|
|
101 |
==================== =========================== |
|
102 |
Request Header Name Value |
|
103 |
==================== =========================== |
|
104 |
X-Auth-Token User authentication token |
|
105 |
==================== =========================== |
|
106 |
|
|
107 |
Extended information on the user serialized in the json format will be returned: |
|
108 |
|
|
109 |
=========================== ============================ |
|
110 |
Name Description |
|
111 |
=========================== ============================ |
|
112 |
displayname User displayname |
|
113 |
uuid User unique identifier |
|
114 |
email List with user emails |
|
115 |
name User full name |
|
116 |
auth_token_created Token creation date |
|
117 |
auth_token_expires Token expiration date |
|
118 |
=========================== ============================ |
|
119 |
|
|
120 |
Example reply: |
|
121 |
|
|
122 |
:: |
|
123 |
|
|
124 |
{"id": "12", |
|
125 |
"displayname": "user@example.com", |
|
126 |
"uuid": "a9dc21d2-bcb2-4104-9a9e-402b7c70d6d8", |
|
127 |
"email": "[user@example.com]", |
|
128 |
"name": "Firstname Lastname", |
|
129 |
"auth_token_created": "Wed, 30 May 2012 10:03:37 GMT", |
|
130 |
"auth_token_expires": "Fri, 29 Jun 2012 10:03:37 GMT"} |
|
131 |
|
|
132 |
| |
|
133 |
|
|
134 |
=========================== ===================== |
|
135 |
Return Code Description |
|
136 |
=========================== ===================== |
|
137 |
204 (No Content) The request succeeded |
|
138 |
400 (Bad Request) Method not allowed or no user found |
|
139 |
401 (Unauthorized) Missing token or inactive user or penging approval terms |
|
140 |
500 (Internal Server Error) The request cannot be completed because of an internal error |
|
141 |
=========================== ===================== |
|
142 |
|
|
143 |
.. warning:: The service is also available under ``/ui/authenticate``. |
|
144 |
It will be removed in the next version. |
|
145 |
|
|
146 |
|
|
147 | 88 |
Send feedback |
148 | 89 |
^^^^^^^^^^^^^ |
149 | 90 |
|
... | ... | |
310 | 251 |
Authenticate |
311 | 252 |
^^^^^^^^^^^^ |
312 | 253 |
|
313 |
Fallback call which receives the user token or the user uuid/token pair and
|
|
314 |
returns back the token as well as information about the token holder and the
|
|
315 |
services he/she can access.
|
|
316 |
If not request body is provided (the request content length is missing or
|
|
317 |
equals to 0) the response contains only non authentication protected
|
|
318 |
information (the service catalog).
|
|
254 |
This call takes the user token or the user uuid/token pair, authenticates
|
|
255 |
the user and returns information about the token, its holder as well as
|
|
256 |
a list of services the user can access.
|
|
257 |
If no request body is provided (the request content length is missing or |
|
258 |
equals to 0), the call operates in public mode, attempts no authentication
|
|
259 |
and returns only the service catalog.
|
|
319 | 260 |
|
320 | 261 |
========================================= ========= ================== |
321 | 262 |
Uri Method Description |
b/snf-astakos-app/astakos/api/urls.py | ||
---|---|---|
50 | 50 |
'astakos.api.user', |
51 | 51 |
url(r'^feedback/?$', 'send_feedback'), |
52 | 52 |
url(r'^user_catalogs/?$', 'get_uuid_displayname_catalogs'), |
53 |
url(r'^authenticate/?$', 'authenticate'), |
|
54 | 53 |
) |
55 | 54 |
|
56 | 55 |
astakos_account_v1_0 += patterns( |
b/snf-astakos-app/astakos/api/user.py | ||
---|---|---|
31 | 31 |
# interpreted as representing official policies, either expressed |
32 | 32 |
# or implied, of GRNET S.A. |
33 | 33 |
|
34 |
from django.http import HttpResponse |
|
35 |
from django.utils import simplejson as json |
|
36 | 34 |
from django.views.decorators.csrf import csrf_exempt |
37 |
|
|
38 | 35 |
from snf_django.lib import api |
39 |
from snf_django.lib.api import faults |
|
40 |
|
|
41 |
from astakos.im.util import epoch |
|
42 | 36 |
|
43 | 37 |
from .util import ( |
44 | 38 |
get_uuid_displayname_catalogs as get_uuid_displayname_catalogs_util, |
... | ... | |
49 | 43 |
logger = logging.getLogger(__name__) |
50 | 44 |
|
51 | 45 |
|
52 |
@api.api_method(http_method="GET", token_required=True, user_required=False, |
|
53 |
logger=logger) |
|
54 |
@user_from_token # Authenticate user!! |
|
55 |
def authenticate(request): |
|
56 |
# Normal Response Codes: 200 |
|
57 |
# Error Response Codes: internalServerError (500) |
|
58 |
# badRequest (400) |
|
59 |
# unauthorised (401) |
|
60 |
user = request.user |
|
61 |
if not user: |
|
62 |
raise faults.BadRequest('No user') |
|
63 |
|
|
64 |
response = HttpResponse() |
|
65 |
user_info = { |
|
66 |
'id': user.id, |
|
67 |
'username': user.username, |
|
68 |
'uuid': user.uuid, |
|
69 |
'email': [user.email], |
|
70 |
'name': user.realname, |
|
71 |
'groups': list(user.groups.all().values_list('name', flat=True)), |
|
72 |
'auth_token': request.META.get('HTTP_X_AUTH_TOKEN'), |
|
73 |
'auth_token_created': epoch(user.auth_token_created), |
|
74 |
'auth_token_expires': epoch(user.auth_token_expires)} |
|
75 |
|
|
76 |
response.content = json.dumps(user_info) |
|
77 |
response['Content-Type'] = 'application/json; charset=UTF-8' |
|
78 |
response['Content-Length'] = len(response.content) |
|
79 |
return response |
|
80 |
|
|
81 |
|
|
82 | 46 |
@csrf_exempt |
83 | 47 |
@api.api_method(http_method="POST", token_required=True, user_required=False, |
84 | 48 |
logger=logger) |
b/snf-astakos-app/astakos/im/urls.py | ||
---|---|---|
1 |
# Copyright 2011-2012 GRNET S.A. All rights reserved.
|
|
1 |
# Copyright 2011, 2012, 2013 GRNET S.A. All rights reserved.
|
|
2 | 2 |
# |
3 | 3 |
# Redistribution and use in source and binary forms, with or |
4 | 4 |
# without modification, are permitted provided that the following |
... | ... | |
184 | 184 |
'astakos.im.views', |
185 | 185 |
url(r'^get_menu/?$', 'get_menu'), |
186 | 186 |
url(r'^get_services/?$', 'get_services')) |
187 |
|
|
188 |
urlpatterns += patterns( |
|
189 |
'astakos.api.user', |
|
190 |
url(r'^authenticate/?$', 'authenticate')) |
b/snf-cyclades-app/synnefo/helpdesk/tests.py | ||
---|---|---|
1 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
1 |
# Copyright 2011, 2012, 2013 GRNET S.A. All rights reserved.
|
|
2 | 2 |
# |
3 | 3 |
# Redistribution and use in source and binary forms, with or |
4 | 4 |
# without modification, are permitted provided that the following |
... | ... | |
83 | 83 |
request.user = None |
84 | 84 |
if request.META.get('HTTP_X_AUTH_TOKEN', None) == '0000': |
85 | 85 |
request.user_uniq = 'test' |
86 |
request.user = {'uniq': 'test', 'auth_token': '0000'} |
|
86 |
request.user = {"access": { |
|
87 |
"token": { |
|
88 |
"expires": "2013-06-19T15:23:59.975572+00:00", |
|
89 |
"id": "0000", |
|
90 |
"tenant": { |
|
91 |
"id": "test", |
|
92 |
"name": "Firstname Lastname" |
|
93 |
} |
|
94 |
}, |
|
95 |
"serviceCatalog": [], |
|
96 |
"user": { |
|
97 |
"roles_links": [], |
|
98 |
"id": "test", |
|
99 |
"roles": [{"id": 1, "name": "default"}], |
|
100 |
"name": "Firstname Lastname"}} |
|
101 |
} |
|
102 |
|
|
87 | 103 |
if request.META.get('HTTP_X_AUTH_TOKEN', None) == '0001': |
88 | 104 |
request.user_uniq = 'test' |
89 |
request.user = {'uniq': 'test', 'groups': ['default', |
|
90 |
'helpdesk'], |
|
91 |
'auth_token': '0001'} |
|
92 |
|
|
105 |
request.user = {"access": { |
|
106 |
"token": { |
|
107 |
"expires": "2013-06-19T15:23:59.975572+00:00", |
|
108 |
"id": "0001", |
|
109 |
"tenant": { |
|
110 |
"id": "test", |
|
111 |
"name": "Firstname Lastname" |
|
112 |
} |
|
113 |
}, |
|
114 |
"serviceCatalog": [], |
|
115 |
"user": { |
|
116 |
"roles_links": [], |
|
117 |
"id": "test", |
|
118 |
"roles": [{"id": 1, "name": "default"}, |
|
119 |
{"id": 2, "name": "helpdesk"}], |
|
120 |
"name": "Firstname Lastname"}} |
|
121 |
} |
|
93 | 122 |
|
94 | 123 |
@mock.patch("astakosclient.AstakosClient", new=AstakosClientMock) |
95 | 124 |
@mock.patch("snf_django.lib.astakos.get_user", new=get_user_mock) |
b/snf-cyclades-app/synnefo/helpdesk/views.py | ||
---|---|---|
1 |
# Copyright 2012 GRNET S.A. All rights reserved. |
|
1 |
# Copyright 2012, 2013 GRNET S.A. All rights reserved.
|
|
2 | 2 |
# |
3 | 3 |
# Redistribution and use in source and binary forms, with or |
4 | 4 |
# without modification, are permitted provided that the following |
... | ... | |
95 | 95 |
raise PermissionDenied |
96 | 96 |
|
97 | 97 |
token = request.POST.get('token', None) |
98 |
if token and token == request.user.get('auth_token', None): |
|
99 |
return func(request, *args, **kwargs) |
|
98 |
if token: |
|
99 |
try: |
|
100 |
req_token = request.user["access"]["token"]["id"] |
|
101 |
if token == req_token: |
|
102 |
return func(request, *args, **kwargs) |
|
103 |
except KeyError: |
|
104 |
pass |
|
100 | 105 |
|
101 | 106 |
raise PermissionDenied |
102 | 107 |
|
... | ... | |
117 | 122 |
astakos.get_user(request, settings.ASTAKOS_AUTH_URL, |
118 | 123 |
fallback_token=token, logger=logger) |
119 | 124 |
if hasattr(request, 'user') and request.user: |
120 |
groups = request.user.get('groups', []) |
|
125 |
groups = request.user['access']['user']['roles'] |
|
126 |
groups = [g["name"] for g in groups] |
|
121 | 127 |
|
122 | 128 |
if not groups: |
123 | 129 |
logger.error("Failed to access helpdesk view. User: %r", |
... | ... | |
181 | 187 |
is_uuid = UUID_SEARCH_REGEX.match(search_query) |
182 | 188 |
is_vm = VM_SEARCH_REGEX.match(search_query) |
183 | 189 |
account_name = search_query |
184 |
auth_token = request.user.get('auth_token')
|
|
190 |
auth_token = request.user['access']['token']['id']
|
|
185 | 191 |
|
186 | 192 |
if is_ip: |
187 | 193 |
try: |
... | ... | |
258 | 264 |
'vms': vms, |
259 | 265 |
'show_deleted': show_deleted, |
260 | 266 |
'account_name': account_name, |
261 |
'token': request.user['auth_token'],
|
|
267 |
'token': request.user['access']['token']['id'],
|
|
262 | 268 |
'networks': networks, |
263 | 269 |
'HELPDESK_MEDIA_URL': HELPDESK_MEDIA_URL, |
264 | 270 |
'UI_MEDIA_URL': UI_MEDIA_URL |
b/snf-cyclades-app/synnefo/ui/views.py | ||
---|---|---|
1 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
1 |
# Copyright 2011, 2012, 2013 GRNET S.A. All rights reserved.
|
|
2 | 2 |
# |
3 | 3 |
# Redistribution and use in source and binary forms, with or |
4 | 4 |
# without modification, are permitted provided that the following |
... | ... | |
50 | 50 |
from synnefo.util.version import get_component_version |
51 | 51 |
from synnefo.lib import join_urls |
52 | 52 |
|
53 |
from snf_django.lib.astakos import get_user |
|
54 | 53 |
from synnefo import cyclades_settings |
55 | 54 |
from synnefo.ui import settings as uisettings |
56 | 55 |
|
b/snf-cyclades-app/synnefo/userdata/tests.py | ||
---|---|---|
1 |
# Copyright 2011 GRNET S.A. All rights reserved. |
|
1 |
# Copyright 2011, 2012, 2013 GRNET S.A. All rights reserved.
|
|
2 | 2 |
# |
3 | 3 |
# Redistribution and use in source and binary forms, with or |
4 | 4 |
# without modification, are permitted provided that the following |
... | ... | |
46 | 46 |
def get_user_mock(request, *args, **kwargs): |
47 | 47 |
if request.META.get('HTTP_X_AUTH_TOKEN', None) == '0000': |
48 | 48 |
request.user_uniq = 'test' |
49 |
request.user = {'id': 'id', |
|
50 |
'username': 'username', |
|
51 |
'uuid': 'test'} |
|
49 |
request.user = {"access": { |
|
50 |
"token": { |
|
51 |
"expires": "2013-06-19T15:23:59.975572+00:00", |
|
52 |
"id": "token", |
|
53 |
"tenant": { |
|
54 |
"id": "test", |
|
55 |
"name": "Firstname Lastname" |
|
56 |
} |
|
57 |
}, |
|
58 |
"serviceCatalog": [], |
|
59 |
"user": { |
|
60 |
"roles_links": [], |
|
61 |
"id": "test", |
|
62 |
"roles": [{"id": 1, "name": "default"}], |
|
63 |
"name": "Firstname Lastname"}} |
|
64 |
} |
|
52 | 65 |
|
53 | 66 |
|
54 | 67 |
class AaiClient(Client): |
b/snf-django-lib/snf_django/lib/api/__init__.py | ||
---|---|---|
109 | 109 |
use_pool=True, |
110 | 110 |
retry=2, |
111 | 111 |
logger=logger) |
112 |
user_info = astakos.get_user_info()
|
|
113 |
request.user_uniq = user_info["uuid"]
|
|
112 |
user_info = astakos.authenticate()
|
|
113 |
request.user_uniq = user_info["access"]["user"]["id"]
|
|
114 | 114 |
request.user = user_info |
115 | 115 |
|
116 | 116 |
# Get the response object |
b/snf-django-lib/snf_django/lib/astakos.py | ||
---|---|---|
1 |
# Copyright 2011-2012 GRNET S.A. All rights reserved.
|
|
1 |
# Copyright 2011, 2012, 2013 GRNET S.A. All rights reserved.
|
|
2 | 2 |
# |
3 | 3 |
# Redistribution and use in source and binary forms, with or |
4 | 4 |
# without modification, are permitted provided that the following |
... | ... | |
44 | 44 |
client = AstakosClient(token, astakos_auth_url, |
45 | 45 |
retry=2, use_pool=True, logger=logger) |
46 | 46 |
try: |
47 |
return client.get_user_info()
|
|
47 |
return client.authenticate()
|
|
48 | 48 |
except Unauthorized: |
49 | 49 |
return None |
50 | 50 |
|
... | ... | |
65 | 65 |
if not user: |
66 | 66 |
return None |
67 | 67 |
|
68 |
# use user uuid, instead of email, keep email/displayname reference |
|
69 |
# to user_id |
|
70 |
request.user_uniq = user['uuid'] |
|
68 |
request.user_uniq = user['access']['user']['id'] |
|
71 | 69 |
request.user = user |
72 |
request.user_id = user.get('displayname') |
|
73 | 70 |
return user |
74 | 71 |
|
75 | 72 |
|
b/snf-django-lib/snf_django/utils/testing.py | ||
---|---|---|
132 | 132 |
""" |
133 | 133 |
with patch("snf_django.lib.api.get_token") as get_token: |
134 | 134 |
get_token.return_value = "DummyToken" |
135 |
with patch('astakosclient.AstakosClient.get_user_info') as m2: |
|
136 |
m2.return_value = {"uuid": text.udec(user, 'utf8')} |
|
135 |
with patch('astakosclient.AstakosClient.authenticate') as m2: |
|
136 |
m2.return_value = {"access": { |
|
137 |
"token": { |
|
138 |
"expires": "2013-06-19T15:23:59.975572+00:00", |
|
139 |
"id": "DummyToken", |
|
140 |
"tenant": { |
|
141 |
"id": text.udec(user, 'utf8'), |
|
142 |
"name": "Firstname Lastname" |
|
143 |
} |
|
144 |
}, |
|
145 |
"serviceCatalog": [], |
|
146 |
"user": { |
|
147 |
"roles_links": [], |
|
148 |
"id": text.udec(user, 'utf8'), |
|
149 |
"roles": [{"id": 1, "name": "default"}], |
|
150 |
"name": "Firstname Lastname"}} |
|
151 |
} |
|
152 |
|
|
137 | 153 |
with patch('astakosclient.AstakosClient.get_quotas') as m3: |
138 | 154 |
m3.return_value = { |
139 | 155 |
"system": { |
Also available in: Unified diff