Revision cca6c617

b/pithos/api/functions.py
44 44
    LengthRequired, PreconditionFailed, RangeNotSatisfiable, UnprocessableEntity)
45 45
from pithos.api.util import (format_meta_key, printable_meta_dict, get_account_meta,
46 46
    put_account_meta, get_container_meta, put_container_meta, get_object_meta, put_object_meta,
47
    update_manifest_meta, format_permissions, validate_modification_preconditions,
47
    update_manifest_meta, update_sharing_meta, validate_modification_preconditions,
48 48
    validate_matching_preconditions, split_container_object_string, copy_or_move_object,
49 49
    get_int_parameter, get_content_length, get_content_range, get_sharing, raw_input_socket,
50 50
    socket_read_iterator, object_data_response, put_object_block, hashmap_hash, api_method)
51 51
from pithos.backends import backend
52
from pithos.backends.base import NotAllowedError
52 53

  
53 54

  
54 55
logger = logging.getLogger(__name__)
......
126 127
    #                       badRequest (400)
127 128
    
128 129
    until = get_int_parameter(request, 'until')
129
    meta = backend.get_account_meta(request.user, v_account, until)
130
    try:
131
        meta = backend.get_account_meta(request.user, v_account, until)
132
    except NotAllowedError:
133
        raise Unauthorized('Access denied')
130 134
    
131 135
    response = HttpResponse(status=204)
132 136
    put_account_meta(response, meta)
......
143 147
    replace = True
144 148
    if 'update' in request.GET:
145 149
        replace = False
146
    backend.update_account_meta(request.user, v_account, meta, replace)
150
    try:
151
        backend.update_account_meta(request.user, v_account, meta, replace)
152
    except NotAllowedError:
153
        raise Unauthorized('Access denied')
147 154
    return HttpResponse(status=202)
148 155

  
149 156
@api_method('GET', format_allowed=True)
......
155 162
    #                       badRequest (400)
156 163
    
157 164
    until = get_int_parameter(request, 'until')
158
    meta = backend.get_account_meta(request.user, v_account, until)
165
    try:
166
        meta = backend.get_account_meta(request.user, v_account, until)
167
    except NotAllowedError:
168
        raise Unauthorized('Access denied')
159 169
    
160 170
    validate_modification_preconditions(request, meta)
161 171
    
......
174 184
    
175 185
    try:
176 186
        containers = backend.list_containers(request.user, v_account, marker, limit, until)
187
    except NotAllowedError:
188
        raise Unauthorized('Access denied')
177 189
    except NameError:
178 190
        containers = []
179 191
    
......
192 204
            try:
193 205
                meta = backend.get_container_meta(request.user, v_account, x[0], until)
194 206
                container_meta.append(printable_meta_dict(meta))
207
            except NotAllowedError:
208
                raise Unauthorized('Access denied')
195 209
            except NameError:
196 210
                pass
197 211
    if request.serialization == 'xml':
......
214 228
    try:
215 229
        meta = backend.get_container_meta(request.user, v_account, v_container, until)
216 230
        meta['object_meta'] = backend.list_object_meta(request.user, v_account, v_container, until)
231
    except NotAllowedError:
232
        raise Unauthorized('Access denied')
217 233
    except NameError:
218 234
        raise ItemNotFound('Container does not exist')
219 235
    
......
234 250
    try:
235 251
        backend.put_container(request.user, v_account, v_container)
236 252
        ret = 201
253
    except NotAllowedError:
254
        raise Unauthorized('Access denied')
237 255
    except NameError:
238 256
        ret = 202
239 257
    
240 258
    if len(meta) > 0:
241
        backend.update_container_meta(request.user, v_account, v_container, meta, replace=True)
259
        try:
260
            backend.update_container_meta(request.user, v_account, v_container, meta, replace=True)
261
        except NotAllowedError:
262
            raise Unauthorized('Access denied')
263
        except NameError:
264
            raise ItemNotFound('Container does not exist')
242 265
    
243 266
    return HttpResponse(status=ret)
244 267

  
......
256 279
        replace = False
257 280
    try:
258 281
        backend.update_container_meta(request.user, v_account, v_container, meta, replace)
282
    except NotAllowedError:
283
        raise Unauthorized('Access denied')
259 284
    except NameError:
260 285
        raise ItemNotFound('Container does not exist')
261 286
    return HttpResponse(status=202)
......
271 296
    
272 297
    try:
273 298
        backend.delete_container(request.user, v_account, v_container)
299
    except NotAllowedError:
300
        raise Unauthorized('Access denied')
274 301
    except NameError:
275 302
        raise ItemNotFound('Container does not exist')
276 303
    except IndexError:
......
289 316
    try:
290 317
        meta = backend.get_container_meta(request.user, v_account, v_container, until)
291 318
        meta['object_meta'] = backend.list_object_meta(request.user, v_account, v_container, until)
319
    except NotAllowedError:
320
        raise Unauthorized('Access denied')
292 321
    except NameError:
293 322
        raise ItemNotFound('Container does not exist')
294 323
    
......
334 363
    
335 364
    try:
336 365
        objects = backend.list_objects(request.user, v_account, v_container, prefix, delimiter, marker, limit, virtual, keys, until)
366
    except NotAllowedError:
367
        raise Unauthorized('Access denied')
337 368
    except NameError:
338 369
        raise ItemNotFound('Container does not exist')
339 370
    
......
358 389
                    permissions = backend.get_object_permissions(request.user, v_account, v_container, x[0])
359 390
                else:
360 391
                    permissions = None
392
            except NotAllowedError:
393
                raise Unauthorized('Access denied')
361 394
            except NameError:
362 395
                pass
363
            if permissions:
364
                meta['X-Object-Sharing'] = format_permissions(permissions)
396
            update_sharing_meta(permissions, v_account, v_container, x[0], meta)
365 397
            object_meta.append(printable_meta_dict(meta))
366 398
    if request.serialization == 'xml':
367 399
        data = render_to_string('objects.xml', {'container': v_container, 'objects': object_meta})
......
386 418
            permissions = backend.get_object_permissions(request.user, v_account, v_container, v_object)
387 419
        else:
388 420
            permissions = None
421
    except NotAllowedError:
422
        raise Unauthorized('Access denied')
389 423
    except NameError:
390 424
        raise ItemNotFound('Object does not exist')
391 425
    except IndexError:
392 426
        raise ItemNotFound('Version does not exist')
393 427
    
394
    if permissions:
395
        meta['X-Object-Sharing'] = format_permissions(permissions)
396 428
    update_manifest_meta(request, v_account, meta)
429
    update_sharing_meta(permissions, v_account, v_container, v_object, meta)
397 430
    
398 431
    response = HttpResponse(status=200)
399 432
    put_object_meta(response, meta)
......
417 450
        if request.serialization == 'text':
418 451
            raise BadRequest('No format specified for version list.')
419 452
        
420
        d = {'versions': backend.list_versions(request.user, v_account, v_container, v_object)}
453
        try:
454
            v = backend.list_versions(request.user, v_account, v_container, v_object)
455
        except NotAllowedError:
456
            raise Unauthorized('Access denied')
457
        d = {'versions': v}
421 458
        if request.serialization == 'xml':
422 459
            d['object'] = v_object
423 460
            data = render_to_string('versions.xml', d)
......
434 471
            permissions = backend.get_object_permissions(request.user, v_account, v_container, v_object)
435 472
        else:
436 473
            permissions = None
474
    except NotAllowedError:
475
        raise Unauthorized('Access denied')
437 476
    except NameError:
438 477
        raise ItemNotFound('Object does not exist')
439 478
    except IndexError:
440 479
        raise ItemNotFound('Version does not exist')
441 480
    
442
    if permissions:
443
        meta['X-Object-Sharing'] = format_permissions(permissions)
444 481
    update_manifest_meta(request, v_account, meta)
482
    update_sharing_meta(permissions, v_account, v_container, v_object, meta)
445 483
    
446 484
    # Evaluate conditions.
447 485
    validate_modification_preconditions(request, meta)
......
458 496
        try:
459 497
            src_container, src_name = split_container_object_string('/' + meta['X-Object-Manifest'])
460 498
            objects = backend.list_objects(request.user, v_account, src_container, prefix=src_name, virtual=False)
499
        except NotAllowedError:
500
            raise Unauthorized('Access denied')
461 501
        except ValueError:
462 502
            raise BadRequest('Invalid X-Object-Manifest header')
463 503
        except NameError:
......
468 508
                s, h = backend.get_object_hashmap(request.user, v_account, src_container, x[0], x[1])
469 509
                sizes.append(s)
470 510
                hashmaps.append(h)
511
        except NotAllowedError:
512
            raise Unauthorized('Access denied')
471 513
        except NameError:
472 514
            raise ItemNotFound('Object does not exist')
473 515
        except IndexError:
......
477 519
            s, h = backend.get_object_hashmap(request.user, v_account, v_container, v_object, version)
478 520
            sizes.append(s)
479 521
            hashmaps.append(h)
522
        except NotAllowedError:
523
            raise Unauthorized('Access denied')
480 524
        except NameError:
481 525
            raise ItemNotFound('Object does not exist')
482 526
        except IndexError:
......
558 602
    
559 603
    try:
560 604
        backend.update_object_hashmap(request.user, v_account, v_container, v_object, size, hashmap, meta, True, permissions)
605
    except NotAllowedError:
606
        raise Unauthorized('Access denied')
561 607
    except NameError:
562 608
        raise ItemNotFound('Container does not exist')
563 609
    except ValueError:
......
622 668
    
623 669
    try:
624 670
        prev_meta = backend.get_object_meta(request.user, v_account, v_container, v_object)
671
    except NotAllowedError:
672
        raise Unauthorized('Access denied')
625 673
    except NameError:
626 674
        raise ItemNotFound('Object does not exist')
627 675
    # If replacing, keep previous values of 'Content-Type' and 'hash'.
......
635 683
    
636 684
    # A Content-Type header indicates data updates.
637 685
    if not content_type or content_type != 'application/octet-stream':
638
        # Handle metadata changes.
639
        try:
640
            backend.update_object_meta(request.user, v_account, v_container, v_object, meta, replace)
641
        except NameError:
642
            raise ItemNotFound('Object does not exist')    
643
        # Handle permission changes.
644
        if permissions:
686
        # Do permissions first, as it may fail easier.
687
        if permissions is not None:
645 688
            try:
646 689
                backend.update_object_permissions(request.user, v_account, v_container, v_object, permissions)
690
            except NotAllowedError:
691
                raise Unauthorized('Access denied')
647 692
            except NameError:
648 693
                raise ItemNotFound('Object does not exist')
649 694
            except ValueError:
650 695
                raise BadRequest('Invalid sharing header')
651 696
            except AttributeError:
652 697
                raise Conflict('Sharing already set above or below this path in the hierarchy')
698
        try:
699
            backend.update_object_meta(request.user, v_account, v_container, v_object, meta, replace)
700
        except NotAllowedError:
701
            raise Unauthorized('Access denied')
702
        except NameError:
703
            raise ItemNotFound('Object does not exist')
653 704
        return HttpResponse(status=202)
654 705
    
655 706
    # Single range update. Range must be in Content-Range.
......
668 719
    
669 720
    try:
670 721
        size, hashmap = backend.get_object_hashmap(request.user, v_account, v_container, v_object)
722
    except NotAllowedError:
723
        raise Unauthorized('Access denied')
671 724
    except NameError:
672 725
        raise ItemNotFound('Object does not exist')
673 726
    
......
700 753
    meta.update({'hash': hashmap_hash(hashmap)}) # Update ETag.
701 754
    try:
702 755
        backend.update_object_hashmap(request.user, v_account, v_container, v_object, size, hashmap, meta, replace, permissions)
756
    except NotAllowedError:
757
        raise Unauthorized('Access denied')
703 758
    except NameError:
704 759
        raise ItemNotFound('Container does not exist')
705 760
    except ValueError:
......
721 776
    
722 777
    try:
723 778
        backend.delete_object(request.user, v_account, v_container, v_object)
779
    except NotAllowedError:
780
        raise Unauthorized('Access denied')
724 781
    except NameError:
725 782
        raise ItemNotFound('Object does not exist')
726 783
    return HttpResponse(status=204)
b/pithos/api/util.py
45 45
from pithos.api.faults import (Fault, NotModified, BadRequest, ItemNotFound, LengthRequired,
46 46
                                PreconditionFailed, RangeNotSatisfiable, ServiceUnavailable)
47 47
from pithos.backends import backend
48
from pithos.backends.base import NotAllowedError
48 49

  
49 50
import datetime
50 51
import logging
......
143 144
        response['X-Object-Version-Timestamp'] = meta['version_timestamp']
144 145
        for k in [x for x in meta.keys() if x.startswith('X-Object-Meta-')]:
145 146
            response[k.encode('utf-8')] = meta[k].encode('utf-8')
146
        for k in ('Content-Encoding', 'Content-Disposition', 'X-Object-Manifest', 'X-Object-Sharing'):
147
        for k in ('Content-Encoding', 'Content-Disposition', 'X-Object-Manifest', 'X-Object-Sharing', 'X-Object-Shared-By'):
147 148
            if k in meta:
148 149
                response[k] = meta[k]
149 150
    else:
......
172 173
        md5.update(hash)
173 174
        meta['hash'] = md5.hexdigest().lower()
174 175

  
175
def format_permissions(permissions):
176
def update_sharing_meta(permissions, v_account, v_container, v_object, meta):
177
    if permissions is None:
178
        return
179
    perm_path, perms = permissions
180
    if len(perms) == 0:
181
        return
176 182
    ret = []
177
    if 'private' in permissions:
178
        ret.append('private')
179
    r = ','.join(permissions.get('read', []))
183
    r = ','.join(perms.get('read', []))
180 184
    if r:
181 185
        ret.append('read=' + r)
182
    w = ','.join(permissions.get('write', []))
186
    w = ','.join(perms.get('write', []))
183 187
    if w:
184 188
        ret.append('write=' + w)
185
    return '; '.join(ret)
189
    meta['X-Object-Sharing'] = '; '.join(ret)
190
    if '/'.join((v_account, v_container, v_object)) != perm_path:
191
        meta['X-Object-Shared-By'] = perm_path
186 192

  
187 193
def validate_modification_preconditions(request, meta):
188 194
    """Check that the modified timestamp conforms with the preconditions set."""
......
238 244
            backend.move_object(request.user, v_account, src_container, src_name, dest_container, dest_name, meta, False, permissions)
239 245
        else:
240 246
            backend.copy_object(request.user, v_account, src_container, src_name, dest_container, dest_name, meta, False, permissions, src_version)
247
    except NotAllowedError:
248
        raise Unauthorized('Access denied')
241 249
    except NameError, IndexError:
242 250
        raise ItemNotFound('Container or object does not exist')
243 251
    except ValueError:
......
352 360
    """
353 361
    
354 362
    permissions = request.META.get('HTTP_X_OBJECT_SHARING')
355
    if permissions is None or permissions == '':
363
    if permissions is None:
356 364
        return None
357 365
    
358 366
    ret = {}
359
    for perm in (x.replace(' ','') for x in permissions.split(';')):
360
        if perm == 'private':
361
            ret['private'] = True
362
            continue
363
        elif perm.startswith('read='):
367
    permissions = permissions.replace(' ', '')
368
    if permissions == '':
369
        return ret
370
    for perm in (x for x in permissions.split(';')):
371
        if perm.startswith('read='):
364 372
            ret['read'] = [v.replace(' ','') for v in perm[5:].split(',')]
373
            ret['read'].remove('')
365 374
            if '*' in ret['read']:
366 375
                ret['read'] = ['*']
367 376
            if len(ret['read']) == 0:
368 377
                raise BadRequest('Bad X-Object-Sharing header value')
369 378
        elif perm.startswith('write='):
370 379
            ret['write'] = [v.replace(' ','') for v in perm[6:].split(',')]
380
            ret['write'].remove('')
371 381
            if '*' in ret['write']:
372 382
                ret['write'] = ['*']
373 383
            if len(ret['write']) == 0:
374 384
                raise BadRequest('Bad X-Object-Sharing header value')
375 385
        else:
376 386
            raise BadRequest('Bad X-Object-Sharing header value')
377
    if 'private' in ret:
378
        if 'read' in ret:
379
            del(ret['read'])
380
        if 'write' in ret:
381
            del(ret['write'])
382 387
    return ret
383 388

  
384 389
def raw_input_socket(request):
b/pithos/backends/base.py
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34
class NotAllowedError(Exception):
35
    pass
36

  
34 37
class BaseBackend(object):
35 38
    """Abstract backend class that serves as a reference for actual implementations.
36 39
    
......
50 53
        """Delete the account with the given name.
51 54
        
52 55
        Raises:
56
            NotAllowedError: Operation not permitted
53 57
            IndexError: Account is not empty
54 58
        """
55 59
        return
......
63 67
            'bytes': The total data size (or 0)
64 68
            'modified': Last modification timestamp (overall)
65 69
            'until_timestamp': Last modification until the timestamp provided
70
        
71
        Raises:
72
            NotAllowedError: Operation not permitted
66 73
        """
67 74
        return {}
68 75
    
......
72 79
        Parameters:
73 80
            'meta': Dictionary with metadata to update
74 81
            'replace': Replace instead of update
82
        
83
        Raises:
84
            NotAllowedError: Operation not permitted
75 85
        """
76 86
        return
77 87
    
......
81 91
        Parameters:
82 92
            'marker': Start list from the next item after 'marker'
83 93
            'limit': Number of containers to return
94
        
95
        Raises:
96
            NotAllowedError: Operation not permitted
84 97
        """
85 98
        return []
86 99
    
......
88 101
        """Create a new container with the given name.
89 102
        
90 103
        Raises:
104
            NotAllowedError: Operation not permitted
91 105
            NameError: Container already exists
92 106
        """
93 107
        return
......
96 110
        """Delete the container with the given name.
97 111
        
98 112
        Raises:
113
            NotAllowedError: Operation not permitted
99 114
            NameError: Container does not exist
100 115
            IndexError: Container is not empty
101 116
        """
......
112 127
            'until_timestamp': Last modification until the timestamp provided
113 128
        
114 129
        Raises:
130
            NotAllowedError: Operation not permitted
115 131
            NameError: Container does not exist
116 132
        """
117 133
        return {}
......
124 140
            'replace': Replace instead of update
125 141
        
126 142
        Raises:
143
            NotAllowedError: Operation not permitted
127 144
            NameError: Container does not exist
128 145
        """
129 146
        return
......
144 161
            'keys': Include objects that have meta with the keys in the list
145 162
        
146 163
        Raises:
164
            NotAllowedError: Operation not permitted
147 165
            NameError: Container does not exist
148 166
        """
149 167
        return []
......
152 170
        """Return a list with all the container's object meta keys.
153 171
        
154 172
        Raises:
173
            NotAllowedError: Operation not permitted
155 174
            NameError: Container does not exist
156 175
        """
157 176
        return []
......
167 186
            'version_timestamp': The version's modification timestamp
168 187
        
169 188
        Raises:
189
            NotAllowedError: Operation not permitted
170 190
            NameError: Container/object does not exist
171 191
            IndexError: Version does not exist
172 192
        """
......
180 200
            'replace': Replace instead of update
181 201
        
182 202
        Raises:
203
            NotAllowedError: Operation not permitted
183 204
            NameError: Container/object does not exist
184 205
        """
185 206
        return
186 207
    
187 208
    def get_object_permissions(self, user, account, container, name):
188
        """Return a dictionary with the object permissions.
209
        """Return the path from which this object gets its permissions from,\
210
        along with a dictionary containing the permissions.
189 211
        
190 212
        The keys are:
191
            'public': The object is readable by all and available at a public URL
192
            'private': No permissions set
193 213
            'read': The object is readable by the users/groups in the list
194 214
            'write': The object is writable by the users/groups in the list
195 215
        
196 216
        Raises:
217
            NotAllowedError: Operation not permitted
197 218
            NameError: Container/object does not exist
198 219
        """
199 220
        return {}
......
205 226
            'permissions': Dictionary with permissions to update
206 227
        
207 228
        Raises:
229
            NotAllowedError: Operation not permitted
208 230
            NameError: Container/object does not exist
209 231
            ValueError: Invalid users/groups in permissions
210 232
            AttributeError: Can not set permissions, as this object\
......
218 240
        """Return the object's size and a list with partial hashes.
219 241
        
220 242
        Raises:
243
            NotAllowedError: Operation not permitted
221 244
            NameError: Container/object does not exist
222 245
            IndexError: Version does not exist
223 246
        """
224 247
        return 0, []
225 248
    
226
    def update_object_hashmap(self, user, account, container, name, size, hashmap, meta={}, replace_meta=False, permissions={}):
249
    def update_object_hashmap(self, user, account, container, name, size, hashmap, meta={}, replace_meta=False, permissions=None):
227 250
        """Create/update an object with the specified size and partial hashes.
228 251
        
229 252
        Parameters:
......
232 255
            'permissions': Updated object permissions
233 256
        
234 257
        Raises:
258
            NotAllowedError: Operation not permitted
235 259
            NameError: Container does not exist
236 260
            ValueError: Invalid users/groups in permissions
237 261
            AttributeError: Can not set permissions
238 262
        """
239 263
        return
240 264
    
241
    def copy_object(self, user, account, src_container, src_name, dest_container, dest_name, dest_meta={}, replace_meta=False, permissions={}, src_version=None):
265
    def copy_object(self, user, account, src_container, src_name, dest_container, dest_name, dest_meta={}, replace_meta=False, permissions=None, src_version=None):
242 266
        """Copy an object's data and metadata.
243 267
        
244 268
        Parameters:
......
248 272
            'src_version': Copy from the version provided
249 273
        
250 274
        Raises:
275
            NotAllowedError: Operation not permitted
251 276
            NameError: Container/object does not exist
252 277
            IndexError: Version does not exist
253 278
            ValueError: Invalid users/groups in permissions
......
255 280
        """
256 281
        return
257 282
    
258
    def move_object(self, user, account, src_container, src_name, dest_container, dest_name, dest_meta={}, replace_meta=False, permissions={}):
283
    def move_object(self, user, account, src_container, src_name, dest_container, dest_name, dest_meta={}, replace_meta=False, permissions=None):
259 284
        """Move an object's data and metadata.
260 285
        
261 286
        Parameters:
......
264 289
            'permissions': New object permissions
265 290
        
266 291
        Raises:
292
            NotAllowedError: Operation not permitted
267 293
            NameError: Container/object does not exist
268 294
            ValueError: Invalid users/groups in permissions
269 295
            AttributeError: Can not set permissions
......
274 300
        """Delete an object.
275 301
        
276 302
        Raises:
303
            NotAllowedError: Operation not permitted
277 304
            NameError: Container/object does not exist
278 305
        """
279 306
        return
280 307
    
281 308
    def list_versions(self, user, account, container, name):
282
        """Return a list of all (version, version_timestamp) tuples for an object."""
309
        """Return a list of all (version, version_timestamp) tuples for an object.
310
        
311
        Raises:
312
            NotAllowedError: Operation not permitted
313
        """
283 314
        return []
284 315
    
285 316
    def get_block(self, hash):
b/pithos/backends/simple.py
40 40
import shutil
41 41
import pickle
42 42

  
43
from base import BaseBackend
43
from base import NotAllowedError, BaseBackend
44 44

  
45 45

  
46 46
logger = logging.getLogger(__name__)
......
88 88
        """Delete the account with the given name."""
89 89
        
90 90
        logger.debug("delete_account: %s", account)
91
        if user != account:
92
            raise NotAllowedError
91 93
        count, bytes, tstamp = self._get_pathstats(account)
92 94
        if count > 0:
93 95
            raise IndexError('Account is not empty')
......
97 99
        """Return a dictionary with the account metadata."""
98 100
        
99 101
        logger.debug("get_account_meta: %s %s", account, until)
102
        if user != account:
103
            raise NotAllowedError
100 104
        try:
101 105
            version_id, mtime = self._get_accountinfo(account, until)
102 106
        except NameError:
......
131 135
        """Update the metadata associated with the account."""
132 136
        
133 137
        logger.debug("update_account_meta: %s %s %s", account, meta, replace)
138
        if user != account:
139
            raise NotAllowedError
134 140
        self._put_metadata(account, meta, replace)
135 141
    
136 142
    def list_containers(self, user, account, marker=None, limit=10000, until=None):
137 143
        """Return a list of containers existing under an account."""
138 144
        
139 145
        logger.debug("list_containers: %s %s %s %s", account, marker, limit, until)
146
        if user != account:
147
            raise NotAllowedError
140 148
        return self._list_objects(account, '', '/', marker, limit, False, [], until)
141 149
    
142 150
    def put_container(self, user, account, container):
143 151
        """Create a new container with the given name."""
144 152
        
145 153
        logger.debug("put_container: %s %s", account, container)
154
        if user != account:
155
            raise NotAllowedError
146 156
        try:
147 157
            path, version_id, mtime = self._get_containerinfo(account, container)
148 158
        except NameError:
......
155 165
        """Delete the container with the given name."""
156 166
        
157 167
        logger.debug("delete_container: %s %s", account, container)
168
        if user != account:
169
            raise NotAllowedError
158 170
        path, version_id, mtime = self._get_containerinfo(account, container)
159 171
        count, bytes, tstamp = self._get_pathstats(path)
160 172
        if count > 0:
......
166 178
        """Return a dictionary with the container metadata."""
167 179
        
168 180
        logger.debug("get_container_meta: %s %s %s", account, container, until)
169
        
181
        if user != account:
182
            raise NotAllowedError
170 183
        path, version_id, mtime = self._get_containerinfo(account, container, until)
171 184
        count, bytes, tstamp = self._get_pathstats(path, until)
172 185
        if mtime > tstamp:
......
188 201
        """Update the metadata associated with the container."""
189 202
        
190 203
        logger.debug("update_container_meta: %s %s %s %s", account, container, meta, replace)
204
        if user != account:
205
            raise NotAllowedError
191 206
        path, version_id, mtime = self._get_containerinfo(account, container)
192 207
        self._put_metadata(path, meta, replace)
193 208
    
......
195 210
        """Return a list of objects existing under a container."""
196 211
        
197 212
        logger.debug("list_objects: %s %s %s %s %s %s %s", account, container, prefix, delimiter, marker, limit, until)
213
        if user != account:
214
            raise NotAllowedError
198 215
        path, version_id, mtime = self._get_containerinfo(account, container, until)
199 216
        return self._list_objects(path, prefix, delimiter, marker, limit, virtual, keys, until)
200 217
    
......
202 219
        """Return a list with all the container's object meta keys."""
203 220
        
204 221
        logger.debug("list_object_meta: %s %s %s", account, container, until)
222
        if user != account:
223
            raise NotAllowedError
205 224
        path, version_id, mtime = self._get_containerinfo(account, container, until)
206 225
        sql = '''select distinct m.key from (%s) o, metadata m
207 226
                    where m.version_id = o.version_id and o.name like ?'''
......
213 232
        """Return a dictionary with the object metadata."""
214 233
        
215 234
        logger.debug("get_object_meta: %s %s %s %s", account, container, name, version)
235
        self._can_read(user, account, container, name)
216 236
        path, version_id, mtime, size = self._get_objectinfo(account, container, name, version)
217 237
        if version is None:
218 238
            modified = mtime
......
227 247
        """Update the metadata associated with the object."""
228 248
        
229 249
        logger.debug("update_object_meta: %s %s %s %s %s", account, container, name, meta, replace)
250
        self._can_write(user, account, container, name)
230 251
        path, version_id, mtime, size = self._get_objectinfo(account, container, name)
231 252
        self._put_metadata(path, meta, replace)
232 253
    
233 254
    def get_object_permissions(self, user, account, container, name):
234
        """Return a dictionary with the object permissions."""
255
        """Return the path from which this object gets its permissions from,\
256
        along with a dictionary containing the permissions."""
235 257
        
236 258
        logger.debug("get_object_permissions: %s %s %s", account, container, name)
259
        self._can_read(user, account, container, name)
237 260
        path = self._get_objectinfo(account, container, name)[0]
238
        perm_path, perms = self._get_permissions(path)
239
        if path == perm_path:
240
            return perms
241
        return {}
261
        return self._get_permissions(path)
242 262
    
243 263
    def update_object_permissions(self, user, account, container, name, permissions):
244 264
        """Update the permissions associated with the object."""
245 265
        
246 266
        logger.debug("update_object_permissions: %s %s %s %s", account, container, name, permissions)
267
        if user != account:
268
            raise NotAllowedError
247 269
        path = self._get_objectinfo(account, container, name)[0]
248 270
        r, w = self._check_permissions(path, permissions)
249 271
        self._put_permissions(path, r, w)
......
252 274
        """Return the object's size and a list with partial hashes."""
253 275
        
254 276
        logger.debug("get_object_hashmap: %s %s %s %s", account, container, name, version)
277
        self._can_read(user, account, container, name)
255 278
        path, version_id, mtime, size = self._get_objectinfo(account, container, name, version)
256 279
        sql = 'select block_id from hashmaps where version_id = ? order by pos asc'
257 280
        c = self.con.execute(sql, (version_id,))
258 281
        hashmap = [x[0] for x in c.fetchall()]
259 282
        return size, hashmap
260 283
    
261
    def update_object_hashmap(self, user, account, container, name, size, hashmap, meta={}, replace_meta=False, permissions={}):
284
    def update_object_hashmap(self, user, account, container, name, size, hashmap, meta={}, replace_meta=False, permissions=None):
262 285
        """Create/update an object with the specified size and partial hashes."""
263 286
        
264 287
        logger.debug("update_object_hashmap: %s %s %s %s %s", account, container, name, size, hashmap)
288
        if permissions is not None and user != account:
289
            raise NotAllowedError
290
        self._can_write(user, account, container, name)
265 291
        path = self._get_containerinfo(account, container)[0]
266 292
        path = os.path.join(path, name)
267
        if permissions:
293
        if permissions is not None:
268 294
            r, w = self._check_permissions(path, permissions)
269 295
        src_version_id, dest_version_id = self._copy_version(path, path, not replace_meta, False)
270 296
        sql = 'update versions set size = ? where version_id = ?'
......
276 302
        for k, v in meta.iteritems():
277 303
            sql = 'insert or replace into metadata (version_id, key, value) values (?, ?, ?)'
278 304
            self.con.execute(sql, (dest_version_id, k, v))
279
        if permissions:
305
        if permissions is not None:
280 306
            sql = 'insert or replace into permissions (name, read, write) values (?, ?, ?)'
281 307
            self.con.execute(sql, (path, r, w))
282 308
        self.con.commit()
283 309
    
284
    def copy_object(self, user, account, src_container, src_name, dest_container, dest_name, dest_meta={}, replace_meta=False, permissions={}, src_version=None):
310
    def copy_object(self, user, account, src_container, src_name, dest_container, dest_name, dest_meta={}, replace_meta=False, permissions=None, src_version=None):
285 311
        """Copy an object's data and metadata."""
286 312
        
287 313
        logger.debug("copy_object: %s %s %s %s %s %s %s %s %s", account, src_container, src_name, dest_container, dest_name, dest_meta, replace_meta, permissions, src_version)
314
        if permissions is not None and user != account:
315
            raise NotAllowedError
316
        self._can_read(user, account, src_container, src_name)
317
        self._can_write(user, account, dest_container, dest_name)
288 318
        self._get_containerinfo(account, src_container)
289 319
        if src_version is None:
290 320
            src_path = self._get_objectinfo(account, src_container, src_name)[0]
......
292 322
            src_path = os.path.join(account, src_container, src_name)
293 323
        dest_path = self._get_containerinfo(account, dest_container)[0]
294 324
        dest_path = os.path.join(dest_path, dest_name)
295
        if permissions:
325
        if permissions is not None:
296 326
            r, w = self._check_permissions(dest_path, permissions)
297 327
        src_version_id, dest_version_id = self._copy_version(src_path, dest_path, not replace_meta, True, src_version)
298 328
        for k, v in dest_meta.iteritems():
299 329
            sql = 'insert or replace into metadata (version_id, key, value) values (?, ?, ?)'
300 330
            self.con.execute(sql, (dest_version_id, k, v))
301
        if permissions:
331
        if permissions is not None:
302 332
            sql = 'insert or replace into permissions (name, read, write) values (?, ?, ?)'
303 333
            self.con.execute(sql, (dest_path, r, w))
304 334
        self.con.commit()
305 335
    
306
    def move_object(self, user, account, src_container, src_name, dest_container, dest_name, dest_meta={}, replace_meta=False, permissions={}):
336
    def move_object(self, user, account, src_container, src_name, dest_container, dest_name, dest_meta={}, replace_meta=False, permissions=None):
307 337
        """Move an object's data and metadata."""
308 338
        
309 339
        logger.debug("move_object: %s %s %s %s %s %s %s %s", account, src_container, src_name, dest_container, dest_name, dest_meta, replace_meta, permissions)
......
314 344
        """Delete an object."""
315 345
        
316 346
        logger.debug("delete_object: %s %s %s", account, container, name)
347
        if user != account:
348
            raise NotAllowedError
317 349
        path, version_id, mtime, size = self._get_objectinfo(account, container, name)
318 350
        self._put_version(path, 0, 1)
319 351
        sql = 'delete from permissions where name = ?'
......
324 356
        """Return a list of all (version, version_timestamp) tuples for an object."""
325 357
        
326 358
        logger.debug("list_versions: %s %s %s", account, container, name)
359
        self._can_read(user, account, container, name)
327 360
        # This will even show deleted versions.
328 361
        path = os.path.join(account, container, name)
329 362
        sql = '''select distinct version_id, strftime('%s', tstamp) from versions where name = ? and hide = 0'''
......
487 520
                self.con.execute(sql, (dest_version_id, k, v))
488 521
        self.con.commit()
489 522
    
490
    def _can_read(self, user, path):
491
        return True
523
    def _is_allowed(self, user, account, container, name, op='read'):
524
        if user == account:
525
            return True
526
        path = os.path.join(account, container, name)
527
        perm_path, perms = self._get_permissions(path)
528
        if op == 'read' and user in perms.get('read', []):
529
            return True
530
        if user in perms.get('write', []):
531
            return True
532
        return False
533
    
534
    def _can_read(self, user, account, container, name):
535
        if not self._is_allowed(user, account, container, name, 'read'):
536
            raise NotAllowedError
492 537
    
493
    def _can_write(self, user, path):
494
        return True
538
    def _can_write(self, user, account, container, name):
539
        if not self._is_allowed(user, account, container, name, 'write'):
540
            raise NotAllowedError
495 541
    
496 542
    def _check_permissions(self, path, permissions):
497 543
        # Check for existing permissions.
......
502 548
        if rows:
503 549
            raise AttributeError('Permissions already set')
504 550
        
505
        # Format given permissions set.
551
        # Format given permissions.
552
        if len(permissions) == 0:
553
            return '', ''
506 554
        r = permissions.get('read', [])
507 555
        w = permissions.get('write', [])
508 556
        if True in [False or ',' in x for x in r]:
509 557
            raise ValueError('Bad characters in read permissions')
510 558
        if True in [False or ',' in x for x in w]:
511 559
            raise ValueError('Bad characters in write permissions')
512
        r = ','.join(r)
513
        w = ','.join(w)
514
        if 'private' in permissions:
515
            r = ''
516
            w = ''
517
        return r, w
560
        return ','.join(r), ','.join(w)
518 561
    
519 562
    def _get_permissions(self, path):
520 563
        # Check for permissions at path or above.
......
525 568
            return path, {}
526 569
        
527 570
        name, r, w = row
528
        if r == '' and w == '':
529
            return {'private': True}
530 571
        ret = {}
531 572
        if w != '':
532 573
            ret['write'] = w.split(',')
533 574
        if r != '':
534
            ret['read'] = r.split(',')        
575
            ret['read'] = r.split(',')
535 576
        return name, ret
536 577
    
537 578
    def _put_permissions(self, path, r, w):
538
        sql = 'insert or replace into permissions (name, read, write) values (?, ?, ?)'
539
        self.con.execute(sql, (path, r, w))
579
        if r == '' and w == '':
580
            sql = 'delete from permissions where name = ?'
581
            self.con.execute(sql, (path,))
582
        else:
583
            sql = 'insert or replace into permissions (name, read, write) values (?, ?, ?)'
584
            self.con.execute(sql, (path, r, w))
540 585
        self.con.commit()
541 586
    
542 587
    def _list_objects(self, path, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, keys=[], until=None):
b/pithos/public/functions.py
66 66
    try:
67 67
        meta = backend.get_object_meta(request.user, v_account, v_container, v_object)
68 68
        permissions = backend.get_object_permissions(request.user, v_account, v_container, v_object)
69
    except NameError:
69
    except:
70 70
        raise ItemNotFound('Object does not exist')
71 71
    
72
    # TODO: Fix public indicator.
72 73
    if 'public' not in permissions:
73 74
        raise ItemNotFound('Object does not exist')
74 75
    update_manifest_meta(request, v_account, meta)
......
91 92
    try:
92 93
        meta = backend.get_object_meta(request.user, v_account, v_container, v_object)
93 94
        permissions = backend.get_object_permissions(request.user, v_account, v_container, v_object)
94
    except NameError:
95
    except:
95 96
        raise ItemNotFound('Object does not exist')
96 97
    
98
    # TODO: Fix public indicator.
97 99
    if 'public' not in permissions:
98 100
        raise ItemNotFound('Object does not exist')
99 101
    update_manifest_meta(request, v_account, meta)
......
113 115
        try:
114 116
            src_container, src_name = split_container_object_string('/' + meta['X-Object-Manifest'])
115 117
            objects = backend.list_objects(request.user, v_account, src_container, prefix=src_name, virtual=False)
116
        except ValueError:
117
            raise ItemNotFound('Object does not exist')
118
        except NameError:
118
        except:
119 119
            raise ItemNotFound('Object does not exist')
120 120
        
121 121
        try:
......
123 123
                s, h = backend.get_object_hashmap(request.user, v_account, src_container, x[0], x[1])
124 124
                sizes.append(s)
125 125
                hashmaps.append(h)
126
        except NameError:
126
        except:
127 127
            raise ItemNotFound('Object does not exist')
128 128
    else:
129 129
        try:
130 130
            s, h = backend.get_object_hashmap(request.user, v_account, v_container, v_object, version)
131 131
            sizes.append(s)
132 132
            hashmaps.append(h)
133
        except NameError:
133
        except:
134 134
            raise ItemNotFound('Object does not exist')
135 135
    
136 136
    return object_data_response(request, sizes, hashmaps, meta, True)

Also available in: Unified diff