Revision 890c2065

b/docs/astakos-api-guide.rst
20 20
=========================  ================================
21 21
Revision                   Description
22 22
=========================  ================================
23
0.13 (January 21, 2013)    Extend api to export user presentation & quota information.
23 24
0.6 (June 06, 2012)        Split service and admin API.
24 25
0.1 (Feb 10, 2012)         Initial release.
25 26
=========================  ================================
26 27

  
27
Admin API Operations
28
--------------------
28
Get Services
29
^^^^^^^^^^^^
29 30

  
30
The operations described in this chapter allow users to authenticate themselves and priviledged users (ex. helpdesk) to access other user information.
31
Returns a json formatted list containing information about the supported cloud services.
31 32

  
32
Most of the operations require a valid token assigned to users having the necessary permissions.
33
==================== =========  ==================
34
Uri                  Method     Description
35
==================== =========  ==================
36
``/im/get_services`` GET        Get cloud services
37
==================== =========  ==================
33 38

  
34
.. _authenticate-api-label:
39
Example reply:
35 40

  
36
Authenticate
37
^^^^^^^^^^^^
41
::
38 42

  
39
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`)
43
    [{"url": "/", "icon": "home-icon.png", "name": "grnet cloud", "id": "1"},
44
    {"url": "/okeanos.html", "name": "~okeanos", "id": "2"},
45
    {"url": "/ui/", "name": "pithos+", "id": "3"}]
46

  
47

  
48
Get Menu
49
^^^^^^^^
50

  
51
Returns a json formatted list containing the cloud bar links.
40 52

  
41 53
==================== =========  ==================
42 54
Uri                  Method     Description
43 55
==================== =========  ==================
44
``/im/authenticate`` GET        Authenticate user using token
56
``/im/get_menu``     GET        Get cloud bar menu
45 57
==================== =========  ==================
46 58

  
47
|
48

  
49
====================  ===========================
50
Request Header Name   Value
51
====================  ===========================
52
X-Auth-Token          Authentication token
53
====================  ===========================
59
Example reply if request user is not authenticated:
54 60

  
55
Extended information on the user serialized in the json format will be returned:
61
::
56 62

  
57
===========================  ============================
58
Name                         Description
59
===========================  ============================
60
username                     User uniq identifier
61
uniq                         User email (uniq identifier used by Astakos)
62
auth_token                   Authentication token
63
auth_token_expires           Token expiration date
64
auth_token_created           Token creation date
65
has_credits                  Whether user has credits
66
has_signed_terms             Whether user has aggred on terms
67
groups                       User groups
68
===========================  ============================
63
    [{"url": "/im/", "name": "Sign in"}]
69 64

  
70
Example reply:
65
Example reply if request user is authenticated:
71 66

  
72 67
::
73 68

  
74
  {"username": "4ad9f34d6e7a4992b34502d40f40cb",
75
  "uniq": "user@example.com"
76
  "auth_token": "0000",
77
  "auth_token_expires": "Fri, 29 Jun 2012 10:03:37 GMT",
78
  "auth_token_created": "Wed, 30 May 2012 10:03:37 GMT",
79
  "has_credits": false,
80
  "has_signed_terms": true}
69
    [{"url": "/im/login", "name": "user@example.com"},
70
    {"url": "/im/profile", "name": "My account"},
71
    {"url": "/im/logout", "name": "Sign out"}]
81 72

  
82
|
73
Admin API Operations
74
--------------------
83 75

  
84
=========================== =====================
85
Return Code                 Description
86
=========================== =====================
87
204 (No Content)            The request succeeded
88
400 (Bad Request)           Method not allowed or no user found
89
401 (Unauthorized)          Missing token or inactive user or penging approval terms
90
500 (Internal Server Error) The request cannot be completed because of an internal error
91
=========================== =====================
76
The operations described in this chapter allow users to authenticate themselves and priviledged users (ex. helpdesk) to access other user information.
92 77

  
93
Get User by email
94
^^^^^^^^^^^^^^^^^
78
Most of the operations require a valid token assigned to users having the necessary permissions.
95 79

  
96
Returns a json formatted dictionary containing information about a specific user
80
.. _authenticate-api-label:
81

  
82
Authenticate
83
^^^^^^^^^^^^
97 84

  
98
============================== =========  ==================
99
Uri                            Method     Description
100
============================== =========  ==================
101
``/im/admin/api/v2.0/users/``  GET        Get user information by email
102
============================== =========  ==================
85
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`)
86

  
87
==================== =========  ==================
88
Uri                  Method     Description
89
==================== =========  ==================
90
``/im/authenticate`` GET        Authenticate user using token
91
==================== =========  ==================
103 92

  
104 93
|
105 94

  
106 95
====================  ===========================
107 96
Request Header Name   Value
108 97
====================  ===========================
109
X-Auth-Token          Authentication token owned by
110
                      a user having or inheriting ``im.can_access_userinfo`` permission
98
X-Auth-Token          User authentication token
111 99
====================  ===========================
112 100

  
113 101
|
......
115 103
======================  =========================
116 104
Request Parameter Name  Value
117 105
======================  =========================
118
name                    Email
106
usage                    (optional)
119 107
======================  =========================
120 108

  
109
Extended information on the user serialized in the json format will be returned:
121 110

  
122
|
123

  
124
=========================== =====================
125
Return Code                 Description
126
=========================== =====================
127
200 (OK)                    The request succeeded
128
400 (Bad Request)           Method not allowed
129
401 (Unauthorized)          Missing or invalid token or unauthorized user
130
404 (Not Found)             Missing email or inactive user
131
500 (Internal Server Error) The request cannot be completed because of an internal error
132
=========================== =====================
111
===========================  ============================
112
Name                         Description
113
===========================  ============================
114
displayname                     User displayname
115
uuid                         User unique identifier
116
email                        List with user emails
117
name                         User full name
118
auth_token_created           Token creation date
119
auth_token_expires           Token expiration date
120
usage                        List of user resource usage (if usage request parameter is present)
121
===========================  ============================
133 122

  
134
Example reply:
123
Example reply without `usage` request parameter:
135 124

  
136 125
::
137 126

  
138
    {"username": "7e530044f90a4e7ba2cb94f3a26c40",
139
    "auth_token_created": "Wed, 30 May 2012 10:03:37 GMT",
140
    "name": "Firstname Surname",
141
    "groups": ["default"],
142
    "user_permissions": [],
143
    "has_credits": false,
144
    "auth_token_expires":"Fri, 29 Jun 2012 10:03:37 GMT",
145
    "enabled": true,
146
    "email": ["user@example.com"],
147
    "id": 4}
127
  {"id": "12",
128
  "displayname": "user@example.com",
129
  "uuid": "a9dc21d2-bcb2-4104-9a9e-402b7c70d6d8",
130
  "email": "[user@example.com]",
131
  "name": "Firstname Lastname",
132
  "auth_token_created": "Wed, 30 May 2012 10:03:37 GMT",
133
  "auth_token_expires": "Fri, 29 Jun 2012 10:03:37 GMT"}
148 134

  
149
Get User by username
150
^^^^^^^^^^^^^^^^^^^^
135
Example reply with `usage` request parameter:
151 136

  
152
Returns a json formatted dictionary containing information about a specific user
153

  
154
======================================== =========  ==================
155
Uri                                      Method     Description
156
======================================== =========  ==================
157
``/im/admin/api/v2.0/users/{username}``  GET        Get user information by username
158
======================================== =========  ==================
137
::
159 138

  
160
|
139
  {"id": "12",
140
  "displayname": "user@example.com",
141
  "uuid": "a9dc21d2-bcb2-4104-9a9e-402b7c70d6d8",
142
  "email": "[user@example.com]",
143
  "name": "Firstname Lastname",
144
  "auth_token_created": "Wed, 30 May 2012 10:03:37 GMT",
145
  "auth_token_expires": "Fri, 29 Jun 2012 10:03:37 GMT",
146
  "usage": [{"currValue": 4536392,
147
             "display_name": "Storage Space",
148
             "description": "Pithos account diskspace",
149
             "verbose_name": "Storage Space",
150
             "help_text_input_each": "This is the total amount of space on Pithos that will be granted to each user of this Project ", "maxValue": 5368710653,
151
             "pluralized_display_name": "Storage Space",
152
             "report_desc": "Storage Space",
153
             "help_text": "This is the space on Pithos for storing files and VM Images. ",
154
             "is_abbreviation": false,
155
             "placeholder": "eg. 10GB",
156
             "unit": "bytes",
157
             "name": "pithos+.diskspace"},
158
            {"currValue": 0,
159
             "display_name": "System Disk",
160
             "description": "Virtual machine disk size",
161
             "verbose_name": "System Disk",
162
             "help_text_input_each": "This is the total amount of System Disk that will be granted to each user of this Project (this refers to the total System Disk of all VMs, not each VM's System Disk)  ",
163
             "maxValue": 53687091200,
164
             "pluralized_display_name": "System Disk",
165
             "report_desc": "System Disk",
166
             "help_text": "This is the System Disk that the VMs have that run the OS ",
167
             "is_abbreviation": false,
168
             "placeholder": "eg. 5GB, 2GB etc",
169
             "unit": "bytes",
170
             "name": "cyclades.disk"},
171
            {"currValue": 0,
172
             "display_name": "CPU",
173
             "description": "Number of virtual machine processors",
174
             "verbose_name": "cpu",
175
             "help_text_input_each": "This is the total number of CPUs that will be granted to each user of this Project (on all VMs)  ", "maxValue": 6, "pluralized_display_name": "CPUs",
176
             "report_desc": "CPUs",
177
             "help_text": "CPUs used by VMs ",
178
             "is_abbreviation": true,
179
             "placeholder": "eg. 1",
180
             "unit": "",
181
             "name": "cyclades.cpu"},
182
            {"currValue": 0,
183
             "display_name": "RAM",
184
             "description": "Virtual machines",
185
             "verbose_name": "ram",
186
             "help_text_input_each": "This is the total amount of RAM that will be granted to each user of this Project (on all VMs)  ", "maxValue": 6442450944,
187
             "pluralized_display_name": "RAM",
188
             "report_desc": "RAM",
189
             "help_text": "RAM used by VMs ",
190
             "is_abbreviation": true,
191
             "placeholder": "eg. 4GB",
192
             "unit": "bytes", "name": "cyclades.ram"},
193
            {"currValue": 0, "display_name": "VM",
194
             "description": "Number of virtual machines",
195
             "verbose_name": "vm", "help_text_input_each": "This is the total number of VMs that will be granted to each user of this Project ", "maxValue": 2,
196
             "pluralized_display_name": "VMs",
197
             "report_desc": "Virtual Machines",
198
             "help_text": "These are the VMs one can create on the Cyclades UI ",
199
             "is_abbreviation": true, "placeholder": "eg. 2",
200
             "unit": "",
201
             "name": "cyclades.vm"},
202
            {"currValue": 0,
203
             "display_name": "private network",
204
             "description": "Private networks",
205
             "verbose_name": "private network",
206
             "help_text_input_each": "This is the total number of Private Networks that will be granted to each user of this Project ",
207
             "maxValue": 1,
208
             "pluralized_display_name": "private networks",
209
             "report_desc": "Private Networks",
210
             "help_text": "These are the Private Networks one can create on the Cyclades UI. ",
211
             "is_abbreviation": false,
212
             "placeholder": "eg. 1",
213
             "unit": "",
214
             "name": "cyclades.network.private"}]}
161 215

  
162
====================  ===========================
163
Request Header Name   Value
164
====================  ===========================
165
X-Auth-Token          Authentication token owned
166
                      by a user having or inheriting ``im.can_access_userinfo`` permission
167
====================  ===========================
168 216

  
169 217
|
170 218

  
171 219
=========================== =====================
172 220
Return Code                 Description
173 221
=========================== =====================
174
200 (OK)                    The request succeeded
175
400 (Bad Request)           Method not allowed
176
401 (Unauthorized)          Missing or invalid token or unauthorized user
177
404 (Not Found)             Invalid username
222
204 (No Content)            The request succeeded
223
400 (Bad Request)           Method not allowed or no user found
224
401 (Unauthorized)          Missing token or inactive user or penging approval terms
178 225
500 (Internal Server Error) The request cannot be completed because of an internal error
179 226
=========================== =====================
180 227

  
181
Example reply:
182

  
183
::
184

  
185
    {"username": "7e530044f90a4e7ba2cb94f3a26c40",
186
    "auth_token_created": "Wed, 30 May 2012 10:03:37 GMT",
187
    "name": "Firstname Surname",
188
    "groups": ["default"],
189
    "user_permissions": [],
190
    "has_credits": false,
191
    "auth_token_expires":
192
    "Fri, 29 Jun 2012 10:03:37 GMT",
193
    "enabled": true,
194
    "email": ["user@example.com"],
195
    "id": 4}
196

  
197
Get Services
198
^^^^^^^^^^^^
199

  
200
Returns a json formatted list containing information about the supported cloud services.
201

  
202
==================== =========  ==================
203
Uri                  Method     Description
204
==================== =========  ==================
205
``/im/get_services`` GET        Get cloud services
206
==================== =========  ==================
207

  
208
Example reply:
209

  
210
::
211

  
212
    [{"url": "/", "icon": "home-icon.png", "name": "grnet cloud", "id": "1"},
213
    {"url": "/okeanos.html", "name": "~okeanos", "id": "2"},
214
    {"url": "/ui/", "name": "pithos+", "id": "3"}]
215

  
216

  
217
Get Menu
218
^^^^^^^^
219

  
220
Returns a json formatted list containing the cloud bar links. 
221

  
222
==================== =========  ==================
223
Uri                  Method     Description
224
==================== =========  ==================
225
``/im/get_menu``     GET        Get cloud bar menu
226
==================== =========  ==================
227

  
228
Example reply if request user is not authenticated:
229

  
230
::
231

  
232
    [{"url": "/im/", "name": "Sign in"}]
233

  
234
Example reply if request user is authenticated:
235

  
236
::
237

  
238
    [{"url": "/im/login", "name": "user@example.com"},
239
    {"url": "/im/profile", "name": "My account"},
240
    {"url": "/im/logout", "name": "Sign out"}]
241

  
242
Service API Operations
243
----------------------
244

  
245
The operations described in this chapter allow services to access user information and perform specific tasks.
246

  
247
The operations require a valid service token.
248 228

  
249 229
Send feedback
250 230
^^^^^^^^^^^^^
......
254 234
========================= =========  ==================
255 235
Uri                       Method     Description
256 236
========================= =========  ==================
257
``/im/service/feedback``  POST       Send feedback
237
``/feedback``             POST       Send feedback
258 238
========================= =========  ==================
259 239

  
260 240
|
......
262 242
====================  ============================
263 243
Request Header Name   Value
264 244
====================  ============================
265
X-Auth-Token          Service Authentication token
245
X-Auth-Token          User authentication token
266 246
====================  ============================
267 247

  
268 248
|
......
270 250
======================  =========================
271 251
Request Parameter Name  Value
272 252
======================  =========================
273
auth_token              User token
274 253
feedback_msg            Feedback message
275 254
feedback_data           Additional information about service client status
276 255
======================  =========================
......
281 260
Return Code                 Description
282 261
=========================== =====================
283 262
200 (OK)                    The request succeeded
263
502 (Bad Gateway)           Send feedback failure
284 264
400 (Bad Request)           Method not allowed or missing or invalid user token parameter or invalid message data
285 265
401 (Unauthorized)          Missing or expired service token
286 266
500 (Internal Server Error) The request cannot be completed because of an internal error
287 267
=========================== =====================
288 268

  
289
Get User by email
290
^^^^^^^^^^^^^^^^^
269
Get User catalog
270
^^^^^^^^^^^^^^^^
291 271

  
292 272
Returns a json formatted dictionary containing information about a specific user
293 273

  
294 274
================================ =========  ==================
295 275
Uri                              Method     Description
296 276
================================ =========  ==================
297
``/im/service/api/v2.0/users/``  GET        Get user information by email
277
``/user_catalogs``               POST       Get 2 catalogs containing uuid to displayname mapping and the opposite
298 278
================================ =========  ==================
299 279

  
300 280
|
......
302 282
====================  ============================
303 283
Request Header Name   Value
304 284
====================  ============================
305
X-Auth-Token          Service Authentication token
285
X-Auth-Token          User authentication token
306 286
====================  ============================
307 287

  
308 288
|
309 289

  
310
======================  =========================
311
Request Parameter Name  Value
312
======================  =========================
313
name                    Email
314
======================  =========================
315

  
316
|
317

  
318
=========================== =====================
319
Return Code                 Description
320
=========================== =====================
321
200 (OK)                    The request succeeded
322
400 (Bad Request)           Method not allowed
323
401 (Unauthorized)          Missing or expired or invalid service token
324
404 (Not Found)             Missing email or inactive user
325
500 (Internal Server Error) The request cannot be completed because of an internal error
326
=========================== =====================
290
The request body is a json formatted dictionary containing a list with uuids and another list of displaynames to translate.
327 291

  
328
Example reply:
292
Example request content:
329 293

  
330 294
::
331 295

  
332
    {"username": "7e530044f90a4e7ba2cb94f3a26c40",
333
    "auth_token_created": "Wed, 30 May 2012 10:03:37 GMT",
334
    "name": "Firstname Surname",
335
    "groups": ["default"],
336
    "user_permissions": [],
337
    "has_credits": false,
338
    "auth_token_expires":"Fri, 29 Jun 2012 10:03:37 GMT",
339
    "enabled": true,
340
    "email": ["user@example.com"],
341
    "id": 4}
296
  {"displaynames": ["user1@example.com", "user2@example.com"],
297
   "uuids":["ff53baa9-c025-4d56-a6e3-963db0438830", "a9dc21d2-bcb2-4104-9a9e-402b7c70d6d8"]}
342 298

  
343
Get User by username
344
^^^^^^^^^^^^^^^^^^^^
345

  
346
Returns a json formatted dictionary containing information about a specific user
299
Example reply:
347 300

  
348
========================================== =========  ==================
349
Uri                                        Method     Description
350
========================================== =========  ==================
351
``/im/service/api/v2.0/users/{username}``  GET        Get user information by username
352
========================================== =========  ==================
301
::
353 302

  
354
|
303
  {"displayname_catalog": {"user1@example.com": "a9dc21d2-bcb2-4104-9a9e-402b7c70d6d8",
304
                           "user2@example.com": "816351c7-7405-4f26-a968-6380cf47ba1f"},
305
  'uuid_catalog': {"a9dc21d2-bcb2-4104-9a9e-402b7c70d6d8": "user1@example.com",
306
                   "ff53baa9-c025-4d56-a6e3-963db0438830": "user2@example.com"}}
355 307

  
356
====================  ============================
357
Request Header Name   Value
358
====================  ============================
359
X-Auth-Token          Service Authentication token
360
====================  ============================
361 308

  
362 309
|
363 310

  
......
365 312
Return Code                 Description
366 313
=========================== =====================
367 314
200 (OK)                    The request succeeded
368
400 (Bad Request)           Method not allowed
315
400 (Bad Request)           Method not allowed or request body is not json formatted
369 316
401 (Unauthorized)          Missing or expired or invalid service token
370
404 (Not Found)             Invalid username
371 317
500 (Internal Server Error) The request cannot be completed because of an internal error
372 318
=========================== =====================
373

  
374
Example reply:
375

  
376
::
377

  
378
    {"username": "7e530044f90a4e7ba2cb94f3a26c40",
379
    "auth_token_created": "Wed, 30 May 2012 10:03:37 GMT",
380
    "name": "Firstname Surname",
381
    "groups": ["default"],
382
    "user_permissions": [],
383
    "has_credits": false,
384
    "auth_token_expires":
385
    "Fri, 29 Jun 2012 10:03:37 GMT",
386
    "enabled": true,
387
    "email": ["user@example.com"],
388
    "id": 4}
b/docs/pithos-api-guide.rst
27 27
=========================  ================================
28 28
Revision                   Description
29 29
=========================  ================================
30
0.13 (Jun 21, 2013)        Proxy identity management services
31
\                          Uuid to displayname translation
32
0.9 (Feb 17, 2012)         Change permissions model.
30 33
0.10 (Jul 18, 2012)        Support for bulk COPY/MOVE/DELETE
31 34
\                          Optionally include public objects in listings.
32 35
0.9 (Feb 17, 2012)         Change permissions model.
......
72 75
\                          Create object using hashmap.
73 76
0.3 (June 14, 2011)        Large object support with ``X-Object-Manifest``.
74 77
\                          Allow for publicly available objects via ``https://hostname/public``.
75
\                          Support time-variant account/container listings. 
78
\                          Support time-variant account/container listings.
76 79
\                          Add source version when duplicating with ``PUT``/``COPY``.
77 80
\                          Request version in object ``HEAD``/``GET`` requests (list versions with ``GET``).
78 81
0.2 (May 31, 2011)         Add object meta listing and filtering in containers.
......
106 109

  
107 110
A user management service that implements a login URI according to these conventions is Astakos (https://code.grnet.gr/projects/astakos), by GRNET.
108 111

  
112
User feedback
113
-------------
114

  
115
Client software using Pithos, should forward to the ``/feedback`` URI. The Pithos service, depending on its configuration will delegate the request to the appropriate identity management URI.
116

  
117
======================  =========================
118
Request Parameter Name  Value
119
======================  =========================
120
auth_token              User token
121
feedback_msg            Feedback message
122
feedback_data           Additional information about service client status
123
======================  =========================
124

  
125
|
126

  
127
=========================== =====================
128
Return Code                 Description
129
=========================== =====================
130
200 (OK)                    The request succeeded
131
502 (Bad Gateway)           Send feedback failure
132
400 (Bad Request)           Method not allowed or missing or invalid user token parameter or invalid message data
133
401 (Unauthorized)          Missing or expired service token
134
500 (Internal Server Error) The request cannot be completed because of an internal error
135
=========================== =====================
136

  
137
User translation catalogs
138
-------------------------
139

  
140
Client software using Pithos, should forward to the ``/user_catalog`` URI to get uuid to displayname translations and vice versa. The Pithos service, depending on its configuration will delegate the request to the appropriate identity management URI.
141

  
142
The request body is a json formatted dictionary containing a list with uuids and another list of displaynames to translate.
143

  
144
Example request content:
145

  
146
::
147

  
148
  {"displaynames": ["user1@example.com", "user2@example.com"],
149
   "uuids":["ff53baa9-c025-4d56-a6e3-963db0438830", "a9dc21d2-bcb2-4104-9a9e-402b7c70d6d8"]}
150

  
151
Example reply:
152

  
153
::
154

  
155
  {"displayname_catalog": {"user1@example.com": "a9dc21d2-bcb2-4104-9a9e-402b7c70d6d8",
156
                        "user2@example.com": "816351c7-7405-4f26-a968-6380cf47ba1f"},
157
  'uuid_catalog': {"a9dc21d2-bcb2-4104-9a9e-402b7c70d6d8": "user1@example.com",
158
                   "ff53baa9-c025-4d56-a6e3-963db0438830": "user2@example.com"}}
159

  
160

  
161
|
162

  
163
=========================== =====================
164
Return Code                 Description
165
=========================== =====================
166
200 (OK)                    The request succeeded
167
400 (Bad Request)           Method not allowed or request body is not json formatted
168
401 (Unauthorized)          Missing or expired or invalid service token
169
500 (Internal Server Error) The request cannot be completed because of an internal error
170
=========================== =====================
171

  
109 172
The Pithos API
110 173
--------------
111 174

  
b/snf-astakos-app/astakos/im/api/__init__.py
45 45
from astakos.im.api.faults import Fault, ItemNotFound, InternalServerError, BadRequest
46 46
from astakos.im.settings import (
47 47
    INVITATIONS_ENABLED, COOKIE_NAME, EMAILCHANGE_ENABLED, QUOTAHOLDER_URL)
48
from astakos.im.forms import FeedbackForm
49
from astakos.im.functions import send_feedback as send_feedback_func
48 50

  
49 51
import logging
50 52
logger = logging.getLogger(__name__)
......
203 205
        super(MenuItem, self).__setattribute__(name, value)
204 206
        if name == 'current_path':
205 207
            self.__set_is_active__()
208

  
209
def __get_uuid_displayname_catalogs(request):
210
    # Normal Response Codes: 200
211
    # Error Response Codes: badRequest (400)
212

  
213
    try:
214
        input_data = json.loads(request.raw_post_data)
215
    except:
216
        raise BadRequest('Request body should be json formatted.')
217
    else:
218
        uuids = input_data.get('uuids', [])
219
        displaynames = input_data.get('displaynames', [])
220
        d  = {'uuid_catalog':AstakosUser.objects.uuid_catalog(uuids),
221
              'displayname_catalog':AstakosUser.objects.displayname_catalog(displaynames)}
222

  
223
        response = HttpResponse()
224
        response.status = 200
225
        response.content = json.dumps(d)
226
        response['Content-Type'] = 'application/json; charset=UTF-8'
227
        response['Content-Length'] = len(response.content)
228
        return response
229

  
230
def __send_feedback(request, email_template_name='im/feedback_mail.txt', user=None):
231
    if not user:
232
        auth_token = request.POST.get('auth', '')
233
        if not auth_token:
234
            raise BadRequest('Missing user authentication')
235

  
236
        try:
237
            user = AstakosUser.objects.get(auth_token=auth_token)
238
        except AstakosUser.DoesNotExist:
239
            raise BadRequest('Invalid user authentication')
240

  
241
    form = FeedbackForm(request.POST)
242
    if not form.is_valid():
243
        raise BadRequest('Invalid data')
244

  
245
    msg = form.cleaned_data['feedback_msg']
246
    data = form.cleaned_data['feedback_data']
247
    try:
248
        send_feedback_func(msg, data, user, email_template_name)
249
    except:
250
        return HttpResponse(status=502)
251
    return HttpResponse(status=200)
b/snf-astakos-app/astakos/im/api/service.py
40 40
from django.views.decorators.csrf import csrf_exempt
41 41
from django.utils import simplejson as json
42 42

  
43
from . import render_fault
43
from . import render_fault, __get_uuid_displayname_catalog, __send_feedback
44 44
from .faults import (
45 45
    Fault, Unauthorized, InternalServerError, BadRequest, ItemNotFound)
46
from astakos.im.models import AstakosUser, Service
47
from astakos.im.forms import FeedbackForm
48
from astakos.im.functions import send_feedback as send_feedback_func
46
from astakos.im.models import Service
49 47

  
50 48
logger = logging.getLogger(__name__)
51 49

  
......
82 80
        return wrapper
83 81
    return decorator
84 82

  
85

  
86
@api_method(http_method='GET', token_required=True)
87
def get_user_info(request):
83
@csrf_exempt
84
@api_method(http_method='POST', token_required=True)
85
def get_uuid_displayname_catalogs(request):
88 86
    # Normal Response Codes: 200
89 87
    # Error Response Codes: internalServerError (500)
90 88
    #                       badRequest (400)
91 89
    #                       unauthorised (401)
92
    #                       itemNotFound (404)
93
    username = request.META.get('HTTP_X_USER_USERNAME')
94
    uuid = request.META.get('HTTP_X_USER_UUID')
95
    if not username and not uuid:
96
        raise BadRequest('Either username or uuid is required.')
97

  
98
    query = AstakosUser.objects.all()
99
    user_info = None
100
    if username:
101
        try:
102
            user = query.get(username__iexact=username)
103
        except AstakosUser.DoesNotExist:
104
            raise ItemNotFound('Invalid username: %s' % username)
105
        else:
106
            user_info = {'uuid': user.uuid}
107
    else:
108
        try:
109
            user = query.get(uuid=uuid)
110
        except AstakosUser.DoesNotExist:
111
            raise ItemNotFound('Invalid uuid: %s' % uuid)
112
        else:
113
            user_info = {'username': user.username}
114

  
115
    response = HttpResponse()
116
    response.status = 200
117
    response.content = json.dumps(user_info)
118
    response['Content-Type'] = 'application/json; charset=UTF-8'
119
    response['Content-Length'] = len(response.content)
120
    return response
121 90

  
91
    return __get_uuid_displayname_catalog(request)
122 92

  
123 93
@csrf_exempt
124 94
@api_method(http_method='POST', token_required=True)
......
127 97
    # Error Response Codes: internalServerError (500)
128 98
    #                       badRequest (400)
129 99
    #                       unauthorised (401)
130
    auth_token = request.POST.get('auth', '')
131
    if not auth_token:
132
        raise BadRequest('Missing user authentication')
133

  
134
    user = None
135
    try:
136
        user = AstakosUser.objects.get(auth_token=auth_token)
137
    except:
138
        pass
139

  
140
    if not user:
141
        raise BadRequest('Invalid user authentication')
142

  
143
    form = FeedbackForm(request.POST)
144
    if not form.is_valid():
145
        raise BadRequest('Invalid data')
146 100

  
147
    msg = form.cleaned_data['feedback_msg']
148
    data = form.cleaned_data['feedback_data']
149
    send_feedback_func(msg, data, user, email_template_name)
150
    response = HttpResponse(status=200)
151
    response['Content-Length'] = len(response.content)
152
    return response
101
    return __send_feedback(request, email_template_name)
b/snf-astakos-app/astakos/im/api/user.py
38 38

  
39 39
from django.http import HttpResponse
40 40
from django.utils import simplejson as json
41
from django.views.decorators.csrf import csrf_exempt
41 42

  
42 43
from .faults import (
43 44
    Fault, Unauthorized, InternalServerError, BadRequest, Forbidden)
44
from . import render_fault
45
from . import render_fault, __get_uuid_displayname_catalogs, __send_feedback
46

  
45 47
from astakos.im.models import AstakosUser
46 48
from astakos.im.util import epoch
47 49

  
......
115 117
        'email': [user.email],
116 118
        'name': user.realname,
117 119
        'auth_token_created': epoch(user.auth_token_created),
118
        'auth_token_expires': epoch(user.auth_token_expires),
119
        'has_credits': user.has_credits}
120
        'auth_token_expires': epoch(user.auth_token_expires)}
120 121

  
121 122
    # append usage data if requested
122 123
    if request.REQUEST.get('usage', None):
......
132 133
    response['Content-Type'] = 'application/json; charset=UTF-8'
133 134
    response['Content-Length'] = len(response.content)
134 135
    return response
136

  
137
@csrf_exempt
138
@api_method(http_method='POST', token_required=True)
139
def get_uuid_displayname_catalogs(request, user=None):
140
    # Normal Response Codes: 200
141
    # Error Response Codes: internalServerError (500)
142
    #                       badRequest (400)
143
    #                       unauthorised (401)
144

  
145
    return __get_uuid_displayname_catalogs(request)
146

  
147
@csrf_exempt
148
@api_method(http_method='POST', token_required=True)
149
def send_feedback(request, email_template_name='im/feedback_mail.txt', user=None):
150
    # Normal Response Codes: 200
151
    # Error Response Codes: internalServerError (500)
152
    #                       badRequest (400)
153
    #                       unauthorised (401)
154

  
155
    return __send_feedback(request, email_template_name, user)
b/snf-astakos-app/astakos/im/models.py
301 301
    def verified(self):
302 302
        return self.filter(email_verified=True)
303 303

  
304
    def uuid_catalog(self, l=None):
305
        """
306
        Returns a uuid to username mapping for the uuids appearing in l.
307
        If l is None returns the mapping for all existing users.
308
        """
309
        q = self.filter(uuid__in=l) if l != None else self
310
        return dict(q.values_list('uuid', 'username'))
311

  
312
    def displayname_catalog(self, l=None):
313
        """
314
        Returns a username to uuid mapping for the usernames appearing in l.
315
        If l is None returns the mapping for all existing users.
316
        """
317
        q = self.filter(uuid__in=l) if l != None else self
318
        q = self.filter(username__in=l) if l != None else self
319
        return dict(q.values_list('username', 'uuid'))
320

  
321

  
304 322

  
305 323
class AstakosUser(User):
306 324
    """
b/snf-astakos-app/astakos/im/urls.py
158 158
urlpatterns += patterns(
159 159
    'astakos.im.api.user',
160 160
    url(r'^authenticate/?$', 'authenticate'))
161

  
162
urlpatterns += patterns(
163
    'astakos.im.api.service',
164
    url(r'^service/api/v2.0/feedback/?$', 'send_feedback'),
165
    url(r'^service/api/v2.0/users/?$', 'get_user_info'))
b/snf-astakos-app/astakos/urls.py
33 33

  
34 34
from django.conf.urls.defaults import include, patterns
35 35

  
36

  
37 36
urlpatterns = patterns('',
38
                       (r'^im/', include('astakos.im.urls'))
39
                       )
37
                       (r'^im/', include('astakos.im.urls')),
38
                       (r'^feedback/?$', 'astakos.im.api.user.send_feedback'),
39
                       (r'^user_catalogs/?$', 'astakos.im.api.user.get_uuid_displayname_catalogs'))
b/snf-common/synnefo/lib/astakos.py
66 66
        return f
67 67
    return execute
68 68

  
69
def call(token, url, headers={}):
69
def call(token, url, headers={}, body=None, method='GET'):
70 70
    p = urlparse(url)
71 71

  
72 72
    kwargs = {}
73 73
    kwargs['headers'] = headers
74 74
    kwargs['headers']['X-Auth-Token'] = token
75
    kwargs['headers']['Content-Length'] = 0
75
    if body:
76
        kwargs['body'] = body
77
        kwargs['headers'].setdefault('content-type', 'application/octet-stream')
78
    kwargs['headers'].setdefault('content-length', len(body) if body else 0)
79

  
76 80

  
77 81
    conn = get_http_connection(p.netloc, p.scheme)
78 82
    try:
79
        conn.request('GET', p.path + '?' + p.query, **kwargs)
83
        conn.request(method, p.path + '?' + p.query, **kwargs)
80 84
        response = conn.getresponse()
81 85
        headers = response.getheaders()
82 86
        headers = dict((unquote(h), unquote(v)) for h,v in headers)
......
103 107

  
104 108

  
105 109
@retry(3)
106
def get_username(
110
def get_displaynames(
107 111
        token,
108
        uuid,
109
        url='http://127.0.0.1:8000/im/service/api/v2.0/users',
112
        uuids,
113
        url='http://127.0.0.1:8000/user_catalogs',
110 114
        override_users={}):
111
    if override_users:
112
        return uuid
113 115

  
114
    headers = {}
115
    if uuid:
116
        headers['X-User-Uuid'] = uuid
116
    if override_users:
117
        return dict((u,u) for u in uuids)
117 118

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

  
125 128

  
126 129
@retry(3)
127
def get_user_uuid(
130
def get_uuids(
128 131
        token,
129
        username,
130
        url='http://127.0.0.1:8000/im/service/api/v2.0/users',
132
        displaynames,
133
        url='http://127.0.0.1:8000/user_catalogs',
131 134
        override_users={}):
135

  
132 136
    if override_users:
133
        return username
137
        return dict((u,u) for u in displaynames)
134 138

  
135
    headers = {}
136
    if username:
137
        headers['X-User-Username'] = username
138 139
    try:
139
        data = call(token, url, headers)
140
        data = call(
141
            token, url, headers={'content-type':'application/json'},
142
            body=json.dumps({'displaynames':displaynames}), method='POST')
140 143
    except Exception, e:
144
        import traceback
145
        traceback.print_exc()
141 146
        raise e
142 147
    else:
143
        return data.get('uuid')
148
        return data.get('displayname_catalog')
149

  
150
def get_user_uuid(
151
        token,
152
        displayname,
153
        url='http://127.0.0.1:8000/user_catalogs',
154
        override_users={}):
155

  
156
    if not displayname:
157
        return
158

  
159
    displayname_dict = get_uuids(token, [displayname], url, override_users)
160
    return displayname_dict.get(displayname)
161

  
162

  
163
def get_displayname(
164
        token,
165
        uuid,
166
        url='http://127.0.0.1:8000/user_catalogs',
167
        override_users={}):
168

  
169
    if not uuid:
170
        return
144 171

  
172
    uuid_dict = get_displaynames(token, [uuid], url, override_users)
173
    return uuid_dict.get(uuid)
145 174

  
146 175
def user_for_token(token, authentication_url, override_users, usage=False):
147 176
    if not token:
......
189 218
                       authentication_url)
190 219
        return None
191 220

  
192
    # use user uuid, instead of email, keep email/username reference to user_id
221
    # use user uuid, instead of email, keep email/displayname reference to user_id
193 222
    request.user_uniq = user['uuid']
194 223
    request.user = user
195
    request.user_id = user.get('username')
224
    request.user_id = user.get('displayname')
196 225
    return user
197 226

  
198 227

  
b/snf-cyclades-app/synnefo/api/management/commands/cyclades-astakos-migrate.py
52 52
def warn(*msgs):
53 53
    print "WARNING: %s" % ' '.join(msgs)
54 54

  
55
get_username = functools.partial(astakos.get_username,
55
get_displayname = functools.partial(astakos.get_displayname,
56 56
                                 settings.CYCLADES_ASTAKOS_SERVICE_TOKEN,
57 57
                                 url=settings.ASTAKOS_URL.replace('authenticate',
58 58
                                                                 'service/api/v2.0/users'))
b/snf-pithos-app/README
27 27

  
28 28
Configure in ``settings.py`` or a ``.conf`` file in ``/etc/synnefo`` if using snf-webproject.
29 29

  
30
===============================  ==================================================     ============================================================
30
===============================  ====================================================   ============================================================
31 31
Name                             Default value                                          Description
32
===============================  ==================================================     ============================================================
33
PITHOS_ASTAKOS_URL               \http://127.0.0.1:8000/im/                             Astakos API URL
32
===============================  ====================================================   ============================================================
33
PROXY_USER_SERVICES              True                                                   Whether to proxy user feedback and catalog services
34
PITHOS_USER_CATALOG_URL          \http://127.0.0.1:8000/im/service/api/v2.0/users/      Astakos User Catalog URL
35
PITHOS_USER_FEEDBACK_URL         \http://127.0.0.1:8000/im/service/api/v2.0/feedback/   Astakos User Feedback URL
36
PITHOS_USER_LOGIN_URL            \http://127.0.0.1:8000/login/                          Astakos User Login URL
34 37
PITHOS_AUTHENTICATION_URL        \http://127.0.0.1:8000/im/authenticate/                Astakos Authentication URL
35
PITHOS_USER_INFO_URL             \http://127.0.0.1:8000/im/service/api/v2.0/users/      Astakos User Information URL
36 38
PITHOS_AUTHENTICATION_USERS      A dictionary of sample users (token to username)       Set to empty or None to disable
37 39
PITHOS_BACKEND_DB_MODULE         pithos.backends.lib.sqlalchemy
38 40
PITHOS_BACKEND_DB_CONNECTION     sqlite:////tmp/pithos-backend.db                       SQLAlchemy database connection string
......
47 49
PITHOS_BACKEND_FREE_VERSIONING   True                                                   Default versioning debit policy (default free)
48 50
PITHOS_UPDATE_MD5                True                                                   Update object checksums when using hashmaps
49 51
PITHOS_SERVICE_TOKEN             ''                                                     Service token acquired by the identity provider (astakos)
50
===============================  ==================================================     ============================================================
52
===============================  ====================================================   ============================================================
51 53

  
52 54
To update checksums asynchronously, enable the queue, install snf-pithos-tools and use ``pithos-dispatcher``::
53 55

  
b/snf-pithos-app/conf/20-snf-pithos-app-settings.conf
1
#PITHOS_ASTAKOS_URL = 'http://127.0.0.1:8000/im/'
1
#PROXY_USER_SERVICES = True
2
#USER_CATALOG_URL = 'http://127.0.0.1:8000/im/service/api/v2.0/users/'
3
#PITHOS_USER_FEEDBACK_URL = 'http://127.0.0.1:8000/im/service/api/v2.0/feedback/'
4
#PITHOS_USER_LOGIN_URL = 'http://127.0.0.1:8000/login/'
2 5
#PITHOS_AUTHENTICATION_URL = 'http://127.0.0.1:8000/im/authenticate/'
3
#PITHOS_USER_INFO_URL = 'http://127.0.0.1:8000/im/service/api/v2.0/users/'
4 6

  
5 7
# Set local users, or a remote host. To disable local users set them to None.
6 8
#sample_users = {
b/snf-pithos-app/pithos/api/delegate.py
44 44
from django.views.decorators.csrf import csrf_exempt
45 45

  
46 46
from pithos.api.settings import (
47
    AUTHENTICATION_URL, AUTHENTICATION_USERS, SERVICE_TOKEN, USER_INFO_URL)
47
    AUTHENTICATION_USERS, USER_LOGIN_URL, USER_FEEDBACK_URL, USER_CATALOG_URL,
48
    SERVICE_TOKEN)
48 49

  
49
from synnefo.lib.astakos import get_username
50
from synnefo.lib.pool.http import get_http_connection
50 51

  
51 52
logger = logging.getLogger(__name__)
52 53

  
53 54

  
54 55
def delegate_to_login_service(request):
55
    url = AUTHENTICATION_URL
56
    url = USER_LOGIN_URL
56 57
    users = AUTHENTICATION_USERS
57 58
    if users or not url:
58 59
        return HttpResponseNotFound()
......
63 64
    else:
64 65
        proto = 'http://'
65 66
    params = dict([(k, v) for k, v in request.GET.items()])
66
    uri = proto + p.netloc + '/login?' + urlencode(params)
67
    uri = proto + p.netloc + p.path + '?' + urlencode(params)
67 68
    return HttpResponseRedirect(uri)
68 69

  
69 70

  
70
@csrf_exempt
71
def delegate_to_feedback_service(request):
72
    url = AUTHENTICATION_URL
73
    users = AUTHENTICATION_USERS
74
    if users or not url:
75
        return HttpResponseNotFound()
76

  
71
def proxy(request, url, headers={}, body=None):
77 72
    p = urlparse(url)
78
    if request.is_secure():
79
        proto = 'https://'
80
    else:
81
        proto = 'http://'
82 73

  
83
    uri = proto + p.netloc + '/im/service/api/v2.0/feedback'
84
    headers = {'X-Auth-Token': SERVICE_TOKEN}
85
    values = dict([(k, unicode(v).encode('utf-8')) for k, v in request.POST.items()])
86
    data = urllib.urlencode(values)
87
    req = urllib2.Request(uri, data, headers)
74
    kwargs = {}
75
    kwargs['headers'] = headers
76
    kwargs['headers'].update(request.META)
77
    kwargs['body'] = body
78
    kwargs['headers'].setdefault('content-type', 'application/json')
79
    kwargs['headers'].setdefault('content-length', len(body) if body else 0)
80

  
81
    conn = get_http_connection(p.netloc, p.scheme)
88 82
    try:
89
        urllib2.urlopen(req)
90
    except urllib2.HTTPError, e:
91
        logger.exception(e)
92
        return HttpResponse(status=e.code)
93
    except urllib2.URLError, e:
94
        logger.exception(e)
95
        return HttpResponse(status=e.reason)
96
    return HttpResponse()
83
        conn.request(request.method, p.path + '?' + p.query, **kwargs)
84
        response = conn.getresponse()
85
        length = response.getheader('content-length', None)
86
        data = response.read(length)
87
        status = int(response.status)
88
        return HttpResponse(data, status=status)
89
    finally:
90
        conn.close()
97 91

  
92
@csrf_exempt
93
def delegate_to_feedback_service(request):
94
    token = request.META.get('HTTP_X_AUTH_TOKEN')
95
    headers = {'X-Auth-Token': token}
96
    return proxy(
97
        request, USER_FEEDBACK_URL, headers=headers, body=request.raw_post_data)
98

  
99
@csrf_exempt
100
def delegate_to_user_catalogs_service(request):
101
    token = request.META.get('HTTP_X_AUTH_TOKEN')
102
    headers = {'X-Auth-Token': token, 'content-type': 'application/json'}
103
    return proxy(
104
        request, USER_CATALOG_URL, headers=headers, body=request.raw_post_data)
b/snf-pithos-app/pithos/api/functions.py
58 58
    copy_or_move_object, get_int_parameter, get_content_length,
59 59
    get_content_range, socket_read_iterator, SaveToBackendHandler,
60 60
    object_data_response, put_object_block, hashmap_md5, simple_list_response,
61
    api_method, retrieve_username, retrieve_uuid,
62
    put_account_translation_headers)
61
    api_method, retrieve_displayname, retrieve_uuid, retrieve_displaynames,
62
    retrieve_uuids)
63

  
63 64
from pithos.api.settings import UPDATE_MD5
64 65

  
65 66
from pithos.backends.base import (
b/snf-pithos-app/pithos/api/settings.py
15 15
    '0009': 'διογένης'
16 16
}
17 17

  
18
ASTAKOS_URL = getattr(settings, 'PITHOS_ASTAKOS_URL',
19
                             'http://127.0.0.1:8000/im/')
20
from urlparse import urljoin
18
# Set to False if pithos running in the same machine with the identity management
19
PROXY_USER_SERVICES = getattr(settings, 'PITHOS_PROXY_USER_SERVICES', True)
20

  
21
USER_CATALOG_URL = getattr(settings, 'PITHOS_USER_CATALOG_URL',
22
                           'http://127.0.0.1:8000/im/service/api/v2.0/users/')
23
USER_FEEDBACK_URL = getattr(settings, 'PITHOS_USER_FEEDBACK_URL',
24
                            'http://127.0.0.1:8000/im/service/api/v2.0/feedback/')
25
USER_LOGIN_URL = getattr(settings, 'PITHOS_USER_LOGIN_URL',
26
                         'http:127.0.0.1:8000/login/')
21 27
AUTHENTICATION_URL = getattr(settings, 'PITHOS_AUTHENTICATION_URL',
22
                             urljoin(ASTAKOS_URL, 'authenticate/'))
23
USER_INFO_URL = getattr(settings, 'PITHOS_USER_INFO_URL',
24
                             urljoin(ASTAKOS_URL, 'service/api/v2.0/users/'))
28
                             'http://localhost:8000/im/authenticate/')
25 29
AUTHENTICATION_USERS = getattr(settings, 'PITHOS_AUTHENTICATION_USERS', {})
26 30

  
27 31
COOKIE_NAME = getattr(settings, 'ASTAKOS_COOKIE_NAME', '_pithos2_a')
b/snf-pithos-app/pithos/api/urls.py
33 33

  
34 34
from django.conf.urls.defaults import include, patterns
35 35

  
36
import pithos.api.settings as settings
37

  
36 38
# TODO: This only works when in this order.
37 39
api_urlpatterns = patterns(
38 40
    'pithos.api.functions',
......
48 50
    (r'^v1(?:$|/)', include(api_urlpatterns)),
49 51
    (r'^v1\.0(?:$|/)', include(api_urlpatterns)),
50 52
    (r'^public/(?P<v_public>.+?)/?$', 'pithos.api.public.public_demux'),
51
    (r'^login/?$', 'pithos.api.delegate.delegate_to_login_service'),
52
    (r'^feedback/?$', 'pithos.api.delegate.delegate_to_feedback_service'))
53
    (r'^login/?$', 'pithos.api.delegate.delegate_to_login_service'))
54

  
55
if settings.PROXY_USER_SERVICES:
56
    urlpatterns += patterns(
57
        '',
58
        (r'^feedback/?$', 'pithos.api.delegate.delegate_to_feedback_service'),
59
        (r'^user_catalog/?$', 'pithos.api.delegate.delegate_to_user_catalogs_service'))
b/snf-pithos-app/pithos/api/util.py
65 65
                                 BACKEND_QUOTA, BACKEND_VERSIONING,
66 66
                                 BACKEND_FREE_VERSIONING,
67 67
                                 AUTHENTICATION_URL, AUTHENTICATION_USERS,
68
                                 SERVICE_TOKEN, COOKIE_NAME, USER_INFO_URL,
68
                                 SERVICE_TOKEN, COOKIE_NAME, USER_CATALOG_URL,
69 69
                                 RADOS_STORAGE, RADOS_POOL_BLOCKS,
70 70
                                 RADOS_POOL_MAPS)
71 71
from pithos.backends import connect_backend
72 72
from pithos.backends.base import (NotAllowedError, QuotaError, ItemNotExists,
73 73
                                  VersionNotExists)
74
from synnefo.lib.astakos import get_user_uuid, get_username
74
from synnefo.lib.astakos import (get_user_uuid, get_displayname,
75
                                 get_uuids, get_displaynames)
75 76

  
76 77
import logging
77 78
import re
......
240 241
    if not restricted:
241 242
        response['X-Object-Hash'] = meta['hash']
242 243
        response['X-Object-UUID'] = meta['uuid']
243
        modified_by = retrieve_username(meta['modified_by'])
244
        modified_by = retrieve_displayname(meta['modified_by'])
244 245
        response['X-Object-Modified-By'] = smart_str(
245 246
            modified_by, strings_only=True)
246 247
        response['X-Object-Version'] = meta['version']
......
294 295
    else:
295 296
       return True
296 297

  
297
def retrieve_username(uuid):
298
def retrieve_displayname(uuid):
298 299
    try:
299
        return get_username(
300
            SERVICE_TOKEN, uuid, USER_INFO_URL, AUTHENTICATION_USERS)
300
        return get_displayname(
301
            SERVICE_TOKEN, uuid, USER_CATALOG_URL, AUTHENTICATION_USERS)
301 302
    except:
302
        # if it fails just leave the metadata intact
303
        # if it fails just leave the input intact
303 304
        return uuid
304 305

  
305
def retrieve_uuid(username):
306
    if is_uuid(username):
307
        return username
306
def retrieve_displaynames(uuids):
307
    return get_displaynames(
308
        SERVICE_TOKEN, uuids, USER_CATALOG_URL, AUTHENTICATION_USERS)
308 309

  
309
    try:
310
        return get_user_uuid(
311
            SERVICE_TOKEN, username, USER_INFO_URL, AUTHENTICATION_USERS)
312
    except Exception, e:
313
        if e.args:
314
            status = e.args[-1]
315
            if status == 404:
316
                raise ItemNotExists(username)
317
        raise
318

  
319
def replace_permissions_username(holder):
310
def retrieve_uuid(displayname):
311
    if is_uuid(displayname):
312
        return displayname
313

  
314
    uuid = get_user_uuid(
315
        SERVICE_TOKEN, displayname, USER_CATALOG_URL, AUTHENTICATION_USERS)
316
    if not uuid:
317
        raise ItemNotExists(displayname)
318
    return uuid
319

  
320
def retrieve_uuids(displaynames):
321
    return get_uuids(
322
        SERVICE_TOKEN, displaynames, USER_CATALOG_URL, AUTHENTICATION_USERS)
323

  
324
def replace_permissions_displayname(holder):
320 325
    try:
321 326
        # check first for a group permission
322 327
        account, group = holder.split(':')
......
330 335
        # check first for a group permission
331 336
        account, group = holder.split(':')
332 337
    except ValueError:
333
        return retrieve_username(holder)
338
        return retrieve_displayname(holder)
334 339
    else:
335
        return ':'.join([retrieve_username(account), group])
340
        return ':'.join([retrieve_displayname(account), group])
336 341

  
337 342
def update_sharing_meta(request, permissions, v_account, v_container, v_object, meta):
338 343
    if permissions is None:
......
595 600
            raise BadRequest(
596 601
                'Bad X-Object-Sharing header value: missing prefix')
597 602

  
598
    # replace username with uuid
603
    # replace displayname with uuid
599 604
    try:
600 605
        ret['read'] = \
601
            [replace_permissions_username(x) for x in ret.get('read', [])]
606
            [replace_permissions_displayname(x) for x in ret.get('read', [])]
602 607
        ret['write'] = \
603
            [replace_permissions_username(x) for x in ret.get('write', [])]
608
            [replace_permissions_displayname(x) for x in ret.get('write', [])]
604 609
    except ItemNotExists, e:
605 610
        raise BadRequest(
606 611
            'Bad X-Object-Sharing header value: unknown account: %s' % e)
b/snf-pithos-backend/pithos/backends/lib/sqlalchemy/alembic/versions/165ba3fbfe53_update_path_account.py
13 13
from alembic import op
14 14
from sqlalchemy.sql import table, column, literal, and_
15 15

  
16
from synnefo.lib.astakos import get_user_uuid, get_username as get_user_username
16
from synnefo.lib.astakos import get_user_uuid, get_displayname as get_user_displayname
17 17
from pithos.api.settings import (
18
    SERVICE_TOKEN, USER_INFO_URL, AUTHENTICATION_USERS)
18
    SERVICE_TOKEN, USER_CATALOG_URL, AUTHENTICATION_USERS)
19 19

  
20 20
import sqlalchemy as sa
21 21

  
......
27 27
        return uuid
28 28
    try:
29 29
        uuid = get_user_uuid(
30
            SERVICE_TOKEN, account, USER_INFO_URL, AUTHENTICATION_USERS)
30
            SERVICE_TOKEN, account, USER_CATALOG_URL, AUTHENTICATION_USERS)
31 31
    except Exception, e:
32 32
        print 'Unable to retrieve uuid for %s: %s' % (account, e)
33 33
        return
......
35 35
        if uuid:
36 36
            catalog[account] = uuid
37 37
        return uuid
38
    
38

  
39 39
inverse_catalog = {}
40
def get_username(account):
40
def get_displayname(account):
41 41
    global inverse_catalog
42
    username = inverse_catalog.get(account)
43
    if username:
44
        return username
42
    displayname = inverse_catalog.get(account)
43
    if displayname:
44
        return displayname
45 45
    try:
46
        username = get_user_username(
47
            SERVICE_TOKEN, account, USER_INFO_URL, AUTHENTICATION_USERS)
46
        displayname = get_user_displayname(
47
            SERVICE_TOKEN, account, USER_CATALOG_URL, AUTHENTICATION_USERS)
48 48
    except Exception, e:
49
        print 'Unable to retrieve username for %s: %s' % (account, e)
49
        print 'Unable to retrieve displayname for %s: %s' % (account, e)
50 50
        return
51 51
    else:
52
        if username:
53
            catalog[account] = username
54
        return username
52
        if displayname:
53
            catalog[account] = displayname
54
        return displayname
55 55

  
56 56
n = table(
57 57
    'nodes',
......
159 159

  
160 160
def downgrade():
161 161
    connection = op.get_bind()
162
  
162

  
163 163
    s = sa.select([n.c.node, n.c.path])
164 164
    nodes = connection.execute(s).fetchall()
165 165
    for node, path in nodes:
166 166
        account, sep, rest = path.partition('/')
167
        username = get_username(account)
168
        if not username:
167
        displayname = get_displayname(account)
168
        if not displayname:
169 169
            continue
170
        path = sep.join([username, rest])
170
        path = sep.join([displayname, rest])
171 171
        u = n.update().where(n.c.node == node).values({'path':path})
172 172
        connection.execute(u)
173
    
173

  
174 174
    s = sa.select([p.c.public_id, p.c.path])
175 175
    public = connection.execute(s).fetchall()
176 176
    for id, path in public:
177 177
        account, sep, rest = path.partition('/')
178
        username = get_username(account)
179
        if not username:
178
        displayname = get_displayname(account)
179
        if not displayname:
180 180
            continue
181
        path = sep.join([username, rest])
181
        path = sep.join([displayname, rest])
182 182
        u = p.update().where(p.c.public_id == id).values({'path':path})
183 183
        connection.execute(u)
184
    
184

  
185 185
    s = sa.select([x.c.feature_id, x.c.path])
186 186
    xfeatures = connection.execute(s).fetchall()
187 187
    for id, path in xfeatures:
188 188
        account, sep, rest = path.partition('/')
189
        username = get_username(account)
190
        if not username:
189
        displayname = get_displayname(account)
190
        if not displayname:
191 191
            continue
192
        path = sep.join([username, rest])
192
        path = sep.join([displayname, rest])
193 193
        u = x.update().where(x.c.feature_id == id).values({'path':path})
194 194
        connection.execute(u)
195 195

  
......
198 198
    xfeaturevals = connection.execute(s).fetchall()
199 199
    for feature_id, key, value in xfeaturevals:
200 200
        account, sep, group = value.partition(':')
201
        username = get_username(account)
202
        if not username:
201
        displayname = get_displayname(account)
202
        if not displayname:
203 203
            continue
204
        new_value = sep.join([username, group])
204
        new_value = sep.join([displayname, group])
205 205
        u = xvals.update()
206 206
        u = u.where(and_(
207 207
                xvals.c.feature_id == feature_id,
......
213 213
    s = sa.select([g.c.owner, g.c.name, g.c.member])
214 214
    groups = connection.execute(s).fetchall()
215 215
    for owner, name, member in groups:
216
        owner_username = get_username(owner)
217
        member_username = get_username(member)
218
        if owner_username or member_username:
216
        owner_displayname = get_displayname(owner)
217
        member_displayname = get_displayname(member)
218
        if owner_displayname or member_displayname:
219 219
            u = g.update()
220 220
            u = u.where(and_(
221 221
                g.c.owner == owner,
222 222
                g.c.name == name,
223 223
                g.c.member == member))
224 224
            values = {}
225
            if owner_username:
226
                values['owner'] = owner_username
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff