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