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