Revision 469d0997

b/snf-astakos-app/astakos/im/cookie.py
40 40
from django.utils.translation import ugettext as _
41 41

  
42 42
from astakos.im.settings import (
43
    COOKIE_NAME, COOKIE_DOMAIN, COOKIE_SECURE, LOGGING_LEVEL)
43
    COOKIE_NAME, COOKIE_DOMAIN, COOKIE_SECURE, LOGGING_LEVEL, TRANSLATE_UUIDS)
44 44

  
45 45
import astakos.im.messages as astakos_messages
46 46

  
......
69 69

  
70 70
    @property
71 71
    def is_valid(self):
72
        return self.uuid == getattr(self.user, 'uuid', '') and \
73
            self.auth_token == getattr(self.user, 'auth_token', '')
72
        cookie_attribute = 'uuid' if not TRANSLATE_UUIDS else 'username'
73
        return (self.uuid == getattr(self.user, cookie_attribute, '') and
74
                self.auth_token == getattr(self.user, 'auth_token', ''))
74 75

  
75 76
    @property
76 77
    def user(self):
......
81 82
            raise ValueError(_(astakos_messages.NO_RESPONSE))
82 83
        user = self.user
83 84
        expire_fmt = user.auth_token_expires.strftime('%a, %d-%b-%Y %H:%M:%S %Z')
84
        cookie_value = quote(user.uuid + '|' + user.auth_token)
85
        if TRANSLATE_UUIDS:
86
            cookie_value = quote(user.username + '|' + user.auth_token)
87
        else:
88
            cookie_value = quote(user.uuid + '|' + user.auth_token)
85 89
        self.response.set_cookie(
86 90
            COOKIE_NAME, value=cookie_value, expires=expire_fmt, path='/',
87 91
            domain=COOKIE_DOMAIN, secure=COOKIE_SECURE
b/snf-astakos-app/astakos/im/settings.py
341 341
                                  'ASTAKOS_ACTIVATION_REDIRECT_URL',
342 342
                                  "/im/landing")
343 343

  
344
# If true, this enables a ui compatibility layer for the introduction of UUIDs
345
# in identity management. WARNING: Setting to True will break your installation.
346
TRANSLATE_UUIDS = getattr(settings, 'ASTAKOS_TRANSLATE_UUIDS', False)
347

  
344 348
# Users that can approve or deny project applications from the web.
345 349
PROJECT_ADMINS = getattr(settings, 'ASTAKOS_PROJECT_ADMINS', set())
b/snf-astakos-app/conf/20-snf-astakos-app-settings.conf
143 143

  
144 144
# UUIDs of users that can approve or deny project applications from the web.
145 145
# ASTAKOS_PROJECT_ADMINS = set() # e.g. set(['01234567-89ab-cdef-0123-456789abcdef'])
146
#
147
# If true, this enables a ui compatibility layer for the introduction
148
# of UUIDs in identity management.
149
# WARNING: Setting to True will break your installation.
150
# ASTAKOS_TRANSLATE_UUIDS = False
b/snf-cyclades-app/synnefo/app_settings/default/plankton.py
18 18

  
19 19
# The owner of the images that will be marked as "system images" by the UI
20 20
SYSTEM_IMAGES_OWNER = 'okeanos'
21

  
22
# If true, this enables a ui compatibility layer for the introduction of UUIDs
23
# in identity management. WARNING: Setting to True will break your installation.
24
TRANSLATE_UUIDS = False
b/snf-cyclades-app/synnefo/plankton/backend.py
55 55

  
56 56
from operator import itemgetter
57 57
from time import gmtime, strftime
58
from functools import wraps
58
from functools import wraps, partial
59 59

  
60 60
from django.conf import settings
61 61

  
62 62
from pithos.backends.base import NotAllowedError as PithosNotAllowedError
63
import synnefo.lib.astakos as lib_astakos
64
import logging
65

  
66
logger = logging.getLogger(__name__)
63 67

  
64 68

  
65 69
PLANKTON_DOMAIN = 'plankton'
......
69 73
PLANKTON_META = ('container_format', 'disk_format', 'name', 'properties',
70 74
                 'status')
71 75

  
76
TRANSLATE_UUIDS = getattr(settings, 'TRANSLATE_UUIDS', False)
77

  
78
def get_displaynames(names):
79
    try:
80
        auth_url = settings.ASTAKOS_URL
81
        url = auth_url.replace('im/authenticate', 'service/api/user_catalogs')
82
        token = settings.CYCLADES_ASTAKOS_SERVICE_TOKEN
83
        uuids = lib_astakos.get_displaynames(token, names, url=url)
84
    except Exception, e:
85
        logger.exception(e)
86
        return {}
87

  
88
    return uuids
89

  
72 90

  
73 91
def get_location(account, container, object):
74 92
    assert '/' not in account, "Invalid account"
......
158 176
        image['id'] = meta['uuid']
159 177
        image['is_public'] = '*' in permissions.get('read', [])
160 178
        image['location'] = location
161
        image['owner'] = account
179
        if TRANSLATE_UUIDS:
180
            displaynames = get_displaynames([account])
181
            if account in displaynames:
182
                display_account = displaynames[account]
183
            else:
184
                display_account = 'unknown'
185
            image['owner'] = display_account
186
        else:
187
            image['owner'] = account
162 188
        image['size'] = meta['bytes']
163 189
        image['store'] = 'pithos'
164 190
        image['updated_at'] = format_timestamp(meta['modified'])
......
305 331
            # To get shared images, we connect as shared_from member and
306 332
            # get the list shared by us
307 333
            user = shared_from
308
            accounts = [self.user]
309 334
        else:
310 335
            user = None if public else self.user
311 336
            accounts = backend.list_accounts(user)
b/snf-pithos-app/conf/20-snf-pithos-app-settings.conf
45 45

  
46 46
# Service Token acquired by identity provider.
47 47
#PITHOS_SERVICE_TOKEN = ''
48

  
49
# This enables a ui compatibility layer for the introduction of UUIDs in
50
# identity management.  WARNING: Setting to True will break your installation.
51
# PITHOS_TRANSLATE_UUIDS = False
b/snf-pithos-app/pithos/api/functions.py
42 42
from django.utils.encoding import smart_str
43 43
from django.views.decorators.csrf import csrf_exempt
44 44

  
45
from synnefo.lib.astakos import get_user
45
from synnefo.lib.astakos import get_user, get_uuids as _get_uuids
46 46

  
47 47
from pithos.api.faults import (
48 48
    Fault, NotModified, BadRequest, Unauthorized, Forbidden, ItemNotFound,
......
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,
62
#    retrieve_uuid
61
    api_method, is_uuid,
62
    retrieve_uuid, retrieve_displayname, retrieve_uuids, retrieve_displaynames
63 63
)
64 64

  
65
from pithos.api.settings import UPDATE_MD5
65
from pithos.api.settings import (UPDATE_MD5, TRANSLATE_UUIDS,
66
                                 SERVICE_TOKEN, AUTHENTICATION_URL)
66 67

  
67 68
from pithos.backends.base import (
68 69
    NotAllowedError, QuotaError, ContainerNotEmpty, ItemNotExists,
......
73 74
import logging
74 75
import hashlib
75 76

  
76

  
77 77
logger = logging.getLogger(__name__)
78 78

  
79
def get_uuids(names):
80
    try:
81
        uuids = _get_uuids(SERVICE_TOKEN, names,
82
                           url=AUTHENTICATION_URL.replace(
83
                                            'im/authenticate',
84
                                            'service/api/user_catalogs'))
85
    except Exception, e:
86
        logger.exception(e)
87
        return {}
88

  
89
    return uuids
90

  
79 91

  
80 92
@csrf_exempt
81 93
def top_demux(request):
......
94 106

  
95 107
@csrf_exempt
96 108
def account_demux(request, v_account):
109
    if TRANSLATE_UUIDS:
110
        if not is_uuid(v_account):
111
            uuids = get_uuids([v_account])
112
            if not uuids or not v_account in uuids:
113
                return HttpResponse(status=404)
114
            v_account = uuids[v_account]
115

  
97 116
    if request.method == 'HEAD':
98 117
        return account_meta(request, v_account)
99 118
    elif request.method == 'POST':
......
106 125

  
107 126
@csrf_exempt
108 127
def container_demux(request, v_account, v_container):
128
    if TRANSLATE_UUIDS:
129
        if not is_uuid(v_account):
130
            uuids = get_uuids([v_account])
131
            if not uuids or not v_account in uuids:
132
                return HttpResponse(status=404)
133
            v_account = uuids[v_account]
134

  
109 135
    if request.method == 'HEAD':
110 136
        return container_meta(request, v_account, v_container)
111 137
    elif request.method == 'PUT':
......
123 149
@csrf_exempt
124 150
def object_demux(request, v_account, v_container, v_object):
125 151
    # Helper to avoid placing the token in the URL when loading objects from a browser.
152
    if TRANSLATE_UUIDS:
153
        if not is_uuid(v_account):
154
            uuids = get_uuids([v_account])
155
            if not uuids or not v_account in uuids:
156
                return HttpResponse(status=404)
157
            v_account = uuids[v_account]
158

  
126 159
    if request.method == 'HEAD':
127 160
        return object_meta(request, v_account, v_container, v_object)
128 161
    elif request.method == 'GET':
......
181 214
    accounts = request.backend.list_accounts(request.user_uniq, marker, limit)
182 215

  
183 216
    if request.serialization == 'text':
217
        if TRANSLATE_UUIDS:
218
            accounts = retrieve_displaynames(
219
                    getattr(request, 'token', None), accounts)
184 220
        if len(accounts) == 0:
185 221
            # The cloudfiles python bindings expect 200 if json/xml.
186 222
            response.status_code = 204
......
208 244
                meta['X-Account-Group'] = printable_header_dict(
209 245
                    dict([(k, ','.join(v)) for k, v in groups.iteritems()]))
210 246
            account_meta.append(printable_header_dict(meta))
247

  
248
    if TRANSLATE_UUIDS:
249
        uuids = list(d['name'] for d in account_meta)
250
        catalog = retrieve_displaynames(
251
                getattr(request, 'token', None), uuids, return_dict=True)
252
        for meta in account_meta:
253
            meta['name'] = catalog.get(meta.get('name'))
254

  
211 255
    if request.serialization == 'xml':
212 256
        data = render_to_string('accounts.xml', {'accounts': account_meta})
213 257
    elif request.serialization == 'json':
......
231 275
            external_quota=request.user_usage)
232 276
        groups = request.backend.get_account_groups(
233 277
            request.user_uniq, v_account)
278

  
279
        if TRANSLATE_UUIDS:
280
            for k in groups:
281
                groups[k] = retrieve_displaynames(
282
                        getattr(request, 'token', None), groups[k])
234 283
        policy = request.backend.get_account_policy(
235 284
            request.user_uniq, v_account, external_quota=request.user_usage)
236 285
    except NotAllowedError:
......
251 300
    #                       badRequest (400)
252 301

  
253 302
    meta, groups = get_account_headers(request)
254
#    for k in groups:
255
#        try:
256
#            groups[k] = [retrieve_uuid(request.token, x) for x in groups[k]]
257
#        except ItemNotExists, e:
258
#            raise BadRequest(
259
#                'Bad X-Account-Group header value: unknown account: %s' % e)
303
    for k in groups:
304
        if TRANSLATE_UUIDS:
305
            try:
306
                groups[k] = retrieve_uuids(
307
                        getattr(request, 'token', None),
308
                        groups[k],
309
                        fail_silently=False)
310
            except ItemNotExists, e:
311
                raise BadRequest(
312
                        'Bad X-Account-Group header value: %s' % e)
313
        else:
314
            try:
315
                retrieve_displaynames(
316
                    getattr(request, 'token', None),
317
                    groups[k],
318
                    fail_silently=False)
319
            except ItemNotExists, e:
320
                raise BadRequest(
321
                        'Bad X-Account-Group header value: %s'  % e)
260 322
    replace = True
261 323
    if 'update' in request.GET:
262 324
        replace = False
......
636 698

  
637 699
    object_meta = []
638 700
    for meta in objects:
701
        if TRANSLATE_UUIDS:
702
            modified_by = meta.get('modified_by')
703
            if modified_by:
704
                l = retrieve_displaynames(
705
                        getattr(request, 'token', None), [meta['modified_by']])
706
                if l is not None and len(l) == 1:
707
                    meta['modified_by'] = l[0]
708

  
639 709
        if len(meta) == 1:
640 710
            # Virtual objects/directories.
641 711
            object_meta.append(meta)
......
661 731
            if public:
662 732
                update_public_meta(public, meta)
663 733
            object_meta.append(printable_header_dict(meta))
734

  
664 735
    if request.serialization == 'xml':
665 736
        data = render_to_string(
666 737
            'objects.xml', {'container': v_container, 'objects': object_meta})
......
715 786
        return response
716 787

  
717 788
    response = HttpResponse(status=200)
718
    put_object_headers(response, meta)
789
    put_object_headers(response, meta, token=getattr(request, 'token', None))
719 790
    return response
720 791

  
721 792

  
......
851 922
            data = json.dumps(d)
852 923

  
853 924
        response = HttpResponse(data, status=200)
854
        put_object_headers(response, meta)
925
        put_object_headers(
926
                response, meta, token=getattr(request, 'token', None))
855 927
        response['Content-Length'] = len(data)
856 928
        return response
857 929

  
b/snf-pithos-app/pithos/api/settings.py
28 28
                             'http://localhost:8000/im/authenticate/')
29 29
AUTHENTICATION_USERS = getattr(settings, 'PITHOS_AUTHENTICATION_USERS', {})
30 30

  
31
TRANSLATE_UUIDS = getattr(settings, 'PITHOS_TRANSLATE_UUIDS', False)
32

  
31 33
COOKIE_NAME = getattr(settings, 'ASTAKOS_COOKIE_NAME', '_pithos2_a')
32 34

  
33 35
# SQLAlchemy (choose SQLite/MySQL/PostgreSQL).
b/snf-pithos-app/pithos/api/util.py
67 67
                                 AUTHENTICATION_URL, AUTHENTICATION_USERS,
68 68
                                 COOKIE_NAME, USER_CATALOG_URL,
69 69
                                 RADOS_STORAGE, RADOS_POOL_BLOCKS,
70
                                 RADOS_POOL_MAPS)
70
                                 RADOS_POOL_MAPS, TRANSLATE_UUIDS)
71 71
from pithos.backends import connect_backend
72 72
from pithos.backends.base import (NotAllowedError, QuotaError, ItemNotExists,
73 73
                                  VersionNotExists)
......
80 80
import uuid
81 81
import decimal
82 82

  
83

  
84 83
logger = logging.getLogger(__name__)
85 84

  
86 85

  
......
226 225
    return content_type, meta, get_sharing(request), get_public(request)
227 226

  
228 227

  
229
def put_object_headers(response, meta, restricted=False):
228
def put_object_headers(response, meta, restricted=False, token=None):
230 229
    response['ETag'] = meta['checksum']
231 230
    response['Content-Length'] = meta['bytes']
232 231
    response['Content-Type'] = meta.get('type', 'application/octet-stream')
......
234 233
    if not restricted:
235 234
        response['X-Object-Hash'] = meta['hash']
236 235
        response['X-Object-UUID'] = meta['uuid']
237
        response['X-Object-Modified-By'] = smart_str(
238
            meta['modified_by'], strings_only=True)
236
        modified_by = retrieve_displayname(token, meta['modified_by'])
237
        if TRANSLATE_UUIDS:
238
            response['X-Object-Modified-By'] = smart_str(
239
                    modified_by, strings_only=True)
239 240
        response['X-Object-Version'] = meta['version']
240 241
        response['X-Object-Version-Timestamp'] = http_date(
241 242
            int(meta['version_timestamp']))
......
291 292
# USER CATALOG utilities #
292 293
##########################
293 294

  
294
def retrieve_displayname(token, uuid):
295
    try:
296
        return get_displayname(
295
def retrieve_displayname(token, uuid, fail_silently=True):
296
    displayname = get_displayname(
297 297
            token, uuid, USER_CATALOG_URL, AUTHENTICATION_USERS)
298
    except:
299
        # if it fails just leave the input intact
298
    if not displayname and not fail_silently:
299
        raise ItemNotExists(uuid)
300
    elif not displayname:
301
        # just return the uuid
300 302
        return uuid
303
    return displayname
301 304

  
302
def retrieve_displaynames(token, uuids):
303
    return get_displaynames(
304
        token, uuids, USER_CATALOG_URL, AUTHENTICATION_USERS)
305
def retrieve_displaynames(token, uuids, return_dict=False, fail_silently=True):
306
    catalog =  get_displaynames(
307
            token, uuids, USER_CATALOG_URL, AUTHENTICATION_USERS) or {}
308
    missing = list(set(uuids) - set(catalog))
309
    if missing and not fail_silently:
310
        raise ItemNotExists('Unknown displaynames: %s' % ', '.join(missing))
311
    return catalog if return_dict else [catalog.get(i) for i in uuids]
305 312

  
306 313
def retrieve_uuid(token, displayname):
307 314
    if is_uuid(displayname):
......
313 320
        raise ItemNotExists(displayname)
314 321
    return uuid
315 322

  
316
def retrieve_uuids(token, displaynames):
317
    return get_uuids(
318
        token, displaynames, USER_CATALOG_URL, AUTHENTICATION_USERS)
323
def retrieve_uuids(token, displaynames, return_dict=False, fail_silently=True):
324
    catalog = get_uuids(
325
            token, displaynames, USER_CATALOG_URL, AUTHENTICATION_USERS) or {}
326
    missing = list(set(displaynames) - set(catalog))
327
    if missing and not fail_silently:
328
        raise ItemNotExists('Unknown uuids: %s' % ', '.join(missing))
329
    return catalog if return_dict else [catalog.get(i) for i in displaynames]
319 330

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

  
329 342
def replace_permissions_uuid(token, holder):
343
    if holder == '*':
344
        return holder
330 345
    try:
331 346
        # check first for a group permission
332 347
        account, group = holder.split(':')
333 348
    except ValueError:
334
        return retrieve_displayname(holder)
349
        return retrieve_displayname(token, holder)
335 350
    else:
336
        return ':'.join([retrieve_displayname(account), group])
351
        return ':'.join([retrieve_displayname(token, account), group])
337 352

  
338 353
def update_sharing_meta(request, permissions, v_account, v_container, v_object, meta):
339 354
    if permissions is None:
......
343 358
        return
344 359

  
345 360
    # replace uuid with displayname
346
#    perms['read'] = [replace_permissions_uuid(request.token, x) for x in perms.get('read', [])]
347
#    perms['write'] = \
348
#        [replace_permissions_uuid(request.token, x) for x in perms.get('write', [])]
361
    if TRANSLATE_UUIDS:
362
        perms['read'] = [replace_permissions_uuid(
363
                getattr(request, 'token', None), x) \
364
                    for x in perms.get('read', [])]
365
        perms['write'] = [replace_permissions_uuid(
366
                getattr(request, 'token', None), x) \
367
                    for x in perms.get('write', [])]
349 368

  
350 369
    ret = []
351 370

  
......
598 617
                'Bad X-Object-Sharing header value: missing prefix')
599 618

  
600 619
    # replace displayname with uuid
601
#    try:
602
#        ret['read'] = \
603
#            [replace_permissions_displayname(request.token, x) for x in ret.get('read', [])]
604
#        ret['write'] = \
605
#            [replace_permissions_displayname(request.token, x) for x in ret.get('write', [])]
606
#    except ItemNotExists, e:
607
#        raise BadRequest(
608
#            'Bad X-Object-Sharing header value: unknown account: %s' % e)
620
    if TRANSLATE_UUIDS:
621
        try:
622
            ret['read'] = [replace_permissions_displayname(
623
                    getattr(request, 'token', None), x) \
624
                        for x in ret.get('read', [])]
625
            ret['write'] = [replace_permissions_displayname(
626
                    getattr(request, 'token', None), x) \
627
                        for x in ret.get('write', [])]
628
        except ItemNotExists, e:
629
            raise BadRequest(
630
                'Bad X-Object-Sharing header value: unknown account: %s' % e)
609 631

  
610 632
    # Keep duplicates only in write list.
611 633
    dups = [x for x in ret.get(
......
879 901
        boundary = ''
880 902
    wrapper = ObjectWrapper(request.backend, ranges, sizes, hashmaps, boundary)
881 903
    response = HttpResponse(wrapper, status=ret)
882
    put_object_headers(response, meta, public)
904
    put_object_headers(
905
            response, meta, restricted=public, token=getattr(request, 'token', None))
883 906
    if ret == 206:
884 907
        if len(ranges) == 1:
885 908
            offset, length = ranges[0]

Also available in: Unified diff