change backend to raise custom exceptions
[pithos] / snf-pithos-app / pithos / api / functions.py
index 3476e57..37df0db 100644 (file)
@@ -32,6 +32,7 @@
 # or implied, of GRNET S.A.
 
 from xml.dom import minidom
+from urllib import unquote
 
 from django.conf import settings
 from django.http import HttpResponse
@@ -51,9 +52,10 @@ from pithos.api.util import (json_encode_decimal, rename_meta_key, format_header
     validate_modification_preconditions, validate_matching_preconditions, split_container_object_string,
     copy_or_move_object, get_int_parameter, get_content_length, get_content_range, socket_read_iterator,
     SaveToBackendHandler, object_data_response, put_object_block, hashmap_md5, simple_list_response, api_method)
-from pithos.api.settings import AUTHENTICATION_URL, AUTHENTICATION_USERS, COOKIE_NAME
+from pithos.api.settings import UPDATE_MD5
+
+from pithos.backends.base import NotAllowedError, QuotaError, ContainerNotEmpty, ItemNotExists, VersionNotExists
 
-from pithos.backends.base import NotAllowedError, QuotaError
 from pithos.backends.filter import parse_filters
 
 import logging
@@ -65,17 +67,13 @@ logger = logging.getLogger(__name__)
 
 @csrf_exempt
 def top_demux(request):
-    get_user(request, AUTHENTICATION_URL, AUTHENTICATION_USERS)
     if request.method == 'GET':
-        if getattr(request, 'user', None) is not None:
-            return account_list(request)
-        return authenticate(request)
+        return account_list(request)
     else:
         return method_not_allowed(request)
 
 @csrf_exempt
 def account_demux(request, v_account):
-    get_user(request, AUTHENTICATION_URL, AUTHENTICATION_USERS)
     if request.method == 'HEAD':
         return account_meta(request, v_account)
     elif request.method == 'POST':
@@ -87,7 +85,6 @@ def account_demux(request, v_account):
 
 @csrf_exempt
 def container_demux(request, v_account, v_container):
-    get_user(request, AUTHENTICATION_URL, AUTHENTICATION_USERS)
     if request.method == 'HEAD':
         return container_meta(request, v_account, v_container)
     elif request.method == 'PUT':
@@ -104,12 +101,6 @@ def container_demux(request, v_account, v_container):
 @csrf_exempt
 def object_demux(request, v_account, v_container, v_object):
     # Helper to avoid placing the token in the URL when loading objects from a browser.
-    token = None
-    if request.method in ('HEAD', 'GET') and COOKIE_NAME in request.COOKIES:
-        cookie_value = unquote(request.COOKIES.get('COOKIE_NAME', ''))
-        if cookie_value and '|' in cookie_value:
-            token = cookie_value.split('|', 1)[1]
-    get_user(request, AUTHENTICATION_URL, AUTHENTICATION_USERS, token)
     if request.method == 'HEAD':
         return object_meta(request, v_account, v_container, v_object)
     elif request.method == 'GET':
@@ -155,6 +146,8 @@ def account_list(request):
     # Normal Response Codes: 200, 204
     # Error Response Codes: internalServerError (500),
     #                       badRequest (400)
+    if getattr(request, 'user', None) is None:
+        return authenticate(request)
     
     response = HttpResponse()
     
@@ -274,10 +267,13 @@ def container_list(request, v_account):
     shared = False
     if 'shared' in request.GET:
         shared = True
+    public = False
+    if 'public' in request.GET:
+        public = True
     
     try:
         containers = request.backend.list_containers(request.user_uniq, v_account,
-                                                marker, limit, shared, until)
+                                                marker, limit, shared, until, public)
     except NotAllowedError:
         raise Forbidden('Not allowed')
     except NameError:
@@ -335,7 +331,7 @@ def container_meta(request, v_account, v_container):
                                                         v_container)
     except NotAllowedError:
         raise Forbidden('Not allowed')
-    except NameError:
+    except ItemNotExists:
         raise ItemNotFound('Container does not exist')
     
     validate_modification_preconditions(request, meta)
@@ -370,7 +366,7 @@ def container_create(request, v_account, v_container):
                                             v_container, policy, replace=False)
         except NotAllowedError:
             raise Forbidden('Not allowed')
-        except NameError:
+        except ItemNotExists:
             raise ItemNotFound('Container does not exist')
         except ValueError:
             raise BadRequest('Invalid policy header')
@@ -380,7 +376,7 @@ def container_create(request, v_account, v_container):
                                             v_container, 'pithos', meta, replace=False)
         except NotAllowedError:
             raise Forbidden('Not allowed')
-        except NameError:
+        except ItemNotExists:
             raise ItemNotFound('Container does not exist')
     
     return HttpResponse(status=ret)
@@ -403,7 +399,7 @@ def container_update(request, v_account, v_container):
                                                 v_container, policy, replace)
         except NotAllowedError:
             raise Forbidden('Not allowed')
-        except NameError:
+        except ItemNotExists:
             raise ItemNotFound('Container does not exist')
         except ValueError:
             raise BadRequest('Invalid policy header')
@@ -413,7 +409,7 @@ def container_update(request, v_account, v_container):
                                                     v_container, 'pithos', meta, replace)
         except NotAllowedError:
             raise Forbidden('Not allowed')
-        except NameError:
+        except ItemNotExists:
             raise ItemNotFound('Container does not exist')
     
     content_length = -1
@@ -448,9 +444,9 @@ def container_delete(request, v_account, v_container):
                                             until)
     except NotAllowedError:
         raise Forbidden('Not allowed')
-    except NameError:
+    except ItemNotExists:
         raise ItemNotFound('Container does not exist')
-    except IndexError:
+    except ContainerNotEmpty:
         raise Conflict('Container is not empty')
     return HttpResponse(status=204)
 
@@ -472,7 +468,7 @@ def object_list(request, v_account, v_container):
                                                         v_container)
     except NotAllowedError:
         raise Forbidden('Not allowed')
-    except NameError:
+    except ItemNotExists:
         raise ItemNotFound('Container does not exist')
     
     validate_modification_preconditions(request, meta)
@@ -492,7 +488,7 @@ def object_list(request, v_account, v_container):
         virtual = False
     
     # Naming policy.
-    if prefix and delimiter:
+    if prefix and delimiter and not prefix.endswith(delimiter):
         prefix = prefix + delimiter
     if not prefix:
         prefix = ''
@@ -517,15 +513,19 @@ def object_list(request, v_account, v_container):
     shared = False
     if 'shared' in request.GET:
         shared = True
+    public = False
+    if 'public' in request.GET:
+        public = True
     
     if request.serialization == 'text':
         try:
             objects = request.backend.list_objects(request.user_uniq, v_account,
                                         v_container, prefix, delimiter, marker,
-                                        limit, virtual, 'pithos', keys, shared, until)
+                                        limit, virtual, 'pithos', keys, shared,
+                                        until, None, public)
         except NotAllowedError:
             raise Forbidden('Not allowed')
-        except NameError:
+        except ItemNotExists:
             raise ItemNotFound('Container does not exist')
         
         if len(objects) == 0:
@@ -535,11 +535,11 @@ def object_list(request, v_account, v_container):
         response.status_code = 200
         response.content = '\n'.join([x[0] for x in objects]) + '\n'
         return response
-    
+
     try:
         objects = request.backend.list_object_meta(request.user_uniq, v_account,
                                     v_container, prefix, delimiter, marker,
-                                    limit, virtual, 'pithos', keys, shared, until)
+                                    limit, virtual, 'pithos', keys, shared, until, None, public)
         object_permissions = {}
         object_public = {}
         if until is None:
@@ -554,7 +554,7 @@ def object_list(request, v_account, v_container):
                 object_public[k[name_idx:]] = v
     except NotAllowedError:
         raise Forbidden('Not allowed')
-    except NameError:
+    except ItemNotExists:
         raise ItemNotFound('Container does not exist')
     
     object_meta = []
@@ -611,9 +611,9 @@ def object_meta(request, v_account, v_container, v_object):
             public = None
     except NotAllowedError:
         raise Forbidden('Not allowed')
-    except NameError:
+    except ItemNotExists:
         raise ItemNotFound('Object does not exist')
-    except IndexError:
+    except VersionNotExists:
         raise ItemNotFound('Version does not exist')
     
     update_manifest_meta(request, v_account, meta)
@@ -680,9 +680,9 @@ def object_read(request, v_account, v_container, v_object):
             public = None
     except NotAllowedError:
         raise Forbidden('Not allowed')
-    except NameError:
+    except ItemNotExists:
         raise ItemNotFound('Object does not exist')
-    except IndexError:
+    except VersionNotExists:
         raise ItemNotFound('Version does not exist')
     
     update_manifest_meta(request, v_account, meta)
@@ -713,7 +713,7 @@ def object_read(request, v_account, v_container, v_object):
             raise Forbidden('Not allowed')
         except ValueError:
             raise BadRequest('Invalid X-Object-Manifest header')
-        except NameError:
+        except ItemNotExists:
             raise ItemNotFound('Container does not exist')
         
         try:
@@ -724,9 +724,9 @@ def object_read(request, v_account, v_container, v_object):
                 hashmaps.append(h)
         except NotAllowedError:
             raise Forbidden('Not allowed')
-        except NameError:
+        except ItemNotExists:
             raise ItemNotFound('Object does not exist')
-        except IndexError:
+        except VersionNotExists:
             raise ItemNotFound('Version does not exist')
     else:
         try:
@@ -736,9 +736,9 @@ def object_read(request, v_account, v_container, v_object):
             hashmaps.append(h)
         except NotAllowedError:
             raise Forbidden('Not allowed')
-        except NameError:
+        except ItemNotExists:
             raise ItemNotFound('Object does not exist')
-        except IndexError:
+        except VersionNotExists:
             raise ItemNotFound('Version does not exist')
     
     # Reply with the hashmap.
@@ -789,6 +789,7 @@ def object_write(request, v_account, v_container, v_object):
     copy_from = request.META.get('HTTP_X_COPY_FROM')
     move_from = request.META.get('HTTP_X_MOVE_FROM')
     if copy_from or move_from:
+        delimiter = request.GET.get('delimiter')
         content_length = get_content_length(request) # Required by the API.
         
         src_account = request.META.get('HTTP_X_SOURCE_ACCOUNT')
@@ -800,14 +801,14 @@ def object_write(request, v_account, v_container, v_object):
             except ValueError:
                 raise BadRequest('Invalid X-Move-From header')
             version_id = copy_or_move_object(request, src_account, src_container, src_name,
-                                                v_account, v_container, v_object, move=True)
+                                                v_account, v_container, v_object, move=True, delimiter=delimiter)
         else:
             try:
                 src_container, src_name = split_container_object_string(copy_from)
             except ValueError:
                 raise BadRequest('Invalid X-Copy-From header')
             version_id = copy_or_move_object(request, src_account, src_container, src_name,
-                                                v_account, v_container, v_object, move=False)
+                                                v_account, v_container, v_object, move=False, delimiter=delimiter)
         response = HttpResponse(status=201)
         response['X-Object-Version'] = version_id
         return response
@@ -817,7 +818,7 @@ def object_write(request, v_account, v_container, v_object):
     if request.META.get('HTTP_TRANSFER_ENCODING') != 'chunked':
         content_length = get_content_length(request)
     # Should be BadRequest, but API says otherwise.
-    if not content_type:
+    if content_type is None:
         raise LengthRequired('Missing Content-Type header')
     
     if 'hashmap' in request.GET:
@@ -877,18 +878,18 @@ def object_write(request, v_account, v_container, v_object):
         raise Forbidden('Not allowed')
     except IndexError, e:
         raise Conflict(simple_list_response(request, e.data))
-    except NameError:
+    except ItemNotExists:
         raise ItemNotFound('Container does not exist')
     except ValueError:
         raise BadRequest('Invalid sharing header')
     except QuotaError:
         raise RequestEntityTooLarge('Quota exceeded')
-    if not checksum:
+    if not checksum and UPDATE_MD5:
         # Update the MD5 after the hashmap, as there may be missing hashes.
-        checksum = hashmap_md5(request, hashmap, size)
+        checksum = hashmap_md5(request.backend, hashmap, size)
         try:
-            version_id = request.backend.update_object_checksum(request.user_uniq,
-                            v_account, v_container, v_object, version_id, checksum)
+            request.backend.update_object_checksum(request.user_uniq,
+              v_account, v_container, v_object, version_id, checksum)
         except NotAllowedError:
             raise Forbidden('Not allowed')
     if public is not None:
@@ -897,7 +898,7 @@ def object_write(request, v_account, v_container, v_object):
                                                 v_container, v_object, public)
         except NotAllowedError:
             raise Forbidden('Not allowed')
-        except NameError:
+        except ItemNotExists:
             raise ItemNotFound('Object does not exist')
     
     response = HttpResponse(status=201)
@@ -926,7 +927,7 @@ def object_write_form(request, v_account, v_container, v_object):
                         file.hashmap, checksum, 'pithos', {}, True)
     except NotAllowedError:
         raise Forbidden('Not allowed')
-    except NameError:
+    except ItemNotExists:
         raise ItemNotFound('Container does not exist')
     except QuotaError:
         raise RequestEntityTooLarge('Quota exceeded')
@@ -964,12 +965,14 @@ def object_copy(request, v_account, v_container, v_object):
                                             v_container, v_object, 'pithos', src_version)
         except NotAllowedError:
             raise Forbidden('Not allowed')
-        except (NameError, IndexError):
+        except (ItemNotExists, VersionNotExists):
             raise ItemNotFound('Container or object does not exist')
         validate_matching_preconditions(request, meta)
     
+    delimiter = request.GET.get('delimiter')
+    
     version_id = copy_or_move_object(request, v_account, v_container, v_object,
-                                        dest_account, dest_container, dest_name, move=False)
+                                        dest_account, dest_container, dest_name, move=False, delimiter=delimiter)
     response = HttpResponse(status=201)
     response['X-Object-Version'] = version_id
     return response
@@ -1000,12 +1003,14 @@ def object_move(request, v_account, v_container, v_object):
                                                     v_container, v_object, 'pithos')
         except NotAllowedError:
             raise Forbidden('Not allowed')
-        except NameError:
+        except ItemNotExists:
             raise ItemNotFound('Container or object does not exist')
         validate_matching_preconditions(request, meta)
     
+    delimiter = request.GET.get('delimiter')
+    
     version_id = copy_or_move_object(request, v_account, v_container, v_object,
-                                        dest_account, dest_container, dest_name, move=True)
+                                        dest_account, dest_container, dest_name, move=True, delimiter=delimiter)
     response = HttpResponse(status=201)
     response['X-Object-Version'] = version_id
     return response
@@ -1026,7 +1031,7 @@ def object_update(request, v_account, v_container, v_object):
                                                     v_container, v_object, 'pithos')
     except NotAllowedError:
         raise Forbidden('Not allowed')
-    except NameError:
+    except ItemNotExists:
         raise ItemNotFound('Object does not exist')
     
     # Evaluate conditions.
@@ -1049,7 +1054,7 @@ def object_update(request, v_account, v_container, v_object):
                                 v_account, v_container, v_object, permissions)
             except NotAllowedError:
                 raise Forbidden('Not allowed')
-            except NameError:
+            except ItemNotExists:
                 raise ItemNotFound('Object does not exist')
             except ValueError:
                 raise BadRequest('Invalid sharing header')
@@ -1059,7 +1064,7 @@ def object_update(request, v_account, v_container, v_object):
                                                 v_container, v_object, public)
             except NotAllowedError:
                 raise Forbidden('Not allowed')
-            except NameError:
+            except ItemNotExists:
                 raise ItemNotFound('Object does not exist')
         if meta or replace:
             try:
@@ -1067,7 +1072,7 @@ def object_update(request, v_account, v_container, v_object):
                                 v_account, v_container, v_object, 'pithos', meta, replace)
             except NotAllowedError:
                 raise Forbidden('Not allowed')
-            except NameError:
+            except ItemNotExists:
                 raise ItemNotFound('Object does not exist')        
             response['X-Object-Version'] = version_id
         
@@ -1088,7 +1093,7 @@ def object_update(request, v_account, v_container, v_object):
                                             v_account, v_container, v_object)
     except NotAllowedError:
         raise Forbidden('Not allowed')
-    except NameError:
+    except ItemNotExists:
         raise ItemNotFound('Object does not exist')
     
     offset, length, total = ranges
@@ -1107,7 +1112,7 @@ def object_update(request, v_account, v_container, v_object):
                                         src_account, src_container, src_name, src_version)
         except NotAllowedError:
             raise Forbidden('Not allowed')
-        except NameError:
+        except ItemNotExists:
             raise ItemNotFound('Source object does not exist')
         
         if length is None:
@@ -1186,14 +1191,14 @@ def object_update(request, v_account, v_container, v_object):
     if dest_bytes is not None and dest_bytes < size:
         size = dest_bytes
         hashmap = hashmap[:(int((size - 1) / request.backend.block_size) + 1)]
-    checksum = hashmap_md5(request, hashmap, size)
+    checksum = hashmap_md5(request.backend, hashmap, size) if UPDATE_MD5 else ''
     try:
         version_id = request.backend.update_object_hashmap(request.user_uniq,
                         v_account, v_container, v_object, size, prev_meta['type'],
                         hashmap, checksum, 'pithos', meta, replace, permissions)
     except NotAllowedError:
         raise Forbidden('Not allowed')
-    except NameError:
+    except ItemNotExists:
         raise ItemNotFound('Container does not exist')
     except ValueError:
         raise BadRequest('Invalid sharing header')
@@ -1205,7 +1210,7 @@ def object_update(request, v_account, v_container, v_object):
                                                 v_container, v_object, public)
         except NotAllowedError:
             raise Forbidden('Not allowed')
-        except NameError:
+        except ItemNotExists:
             raise ItemNotFound('Object does not exist')
     
     response = HttpResponse(status=204)
@@ -1222,12 +1227,14 @@ def object_delete(request, v_account, v_container, v_object):
     #                       badRequest (400)
     
     until = get_int_parameter(request.GET.get('until'))
+    delimiter = request.GET.get('delimiter')
+    
     try:
         request.backend.delete_object(request.user_uniq, v_account, v_container,
-                                        v_object, until)
+                                        v_object, until, delimiter=delimiter)
     except NotAllowedError:
         raise Forbidden('Not allowed')
-    except NameError:
+    except ItemNotExists:
         raise ItemNotFound('Object does not exist')
     return HttpResponse(status=204)