Revision 151e729e

b/snf-pithos-app/pithos/api/functions.py
485 485
    return HttpResponse(status=ret)
486 486

  
487 487

  
488
@api_method('POST', format_allowed=True, user_required=True, logger=logger)
488
@api_method('POST', format_allowed=True, user_required=True, logger=logger,
489
            lock_container_path=True)
489 490
def container_update(request, v_account, v_container):
490 491
    # Normal Response Codes: 202
491 492
    # Error Response Codes: internalServerError (500),
......
536 537
    return response
537 538

  
538 539

  
539
@api_method('DELETE', user_required=True, logger=logger)
540
@api_method('DELETE', user_required=True, logger=logger,
541
            lock_container_path=True)
540 542
def container_delete(request, v_account, v_container):
541 543
    # Normal Response Codes: 204
542 544
    # Error Response Codes: internalServerError (500),
......
1125 1127
    return response
1126 1128

  
1127 1129

  
1128
@api_method('COPY', format_allowed=True, user_required=True, logger=logger,
1129
            lock_container_path=True)
1130
@api_method('COPY', format_allowed=True, user_required=True, logger=logger)
1130 1131
def object_copy(request, v_account, v_container, v_object):
1131 1132
    # Normal Response Codes: 201
1132 1133
    # Error Response Codes: internalServerError (500),
b/snf-pithos-app/pithos/api/util.py
1131 1131

  
1132 1132
                # Add a PithosBackend as attribute of the request object
1133 1133
                request.backend = get_backend(backend_cls)
1134
                request.backend.pre_exec(lock_container_path)
1134

  
1135
                path = strip_prefix(request.path)
1136
                assert path is not None, 'Unexpected path: %s' % request.path
1137
                if request.method in ('HEAD', 'GET'):
1138
                    action = 'read'
1139
                else:
1140
                    action = 'write'
1141

  
1142
                try:
1143
                    request.backend.pre_exec(
1144
                        request.user_uniq,
1145
                        *path.split('/', 2),
1146
                        action=action,
1147
                        lock_container_path=lock_container_path)
1148
                except NotAllowedError:
1149
                    if _public_request:
1150
                        raise faults.ItemNotFound('Object does not exist')
1151
                    raise faults.Forbidden('Not allowed')
1152
                except ItemNotExists:
1153
                    raise faults.ItemNotFound('Container does not exist')
1135 1154

  
1136 1155
                path = strip_prefix(request.path)
1137 1156
                assert path is not None, 'Unexpected path: %s' % request.path
b/snf-pithos-backend/pithos/backends/modular.py
264 264

  
265 265
        self.lock_container_path = False
266 266

  
267
        self.read_allowed = defaultdict(list)
268
        self.write_allowed = defaultdict(list)
267
        self.container_tuple = None
268

  
269
        self._reset_allowed_paths()
269 270

  
270
    def pre_exec(self, lock_container_path=False):
271
    def pre_exec(self, user, account=None, container=None, name=None,
272
                 action='read', lock_container_path=False):
271 273
        self.serials = []
272 274
        self.messages = []
273 275

  
274
        self.read_allowed = defaultdict(list)
275
        self.write_allowed = defaultdict(list)
276
        self._reset_allowed_paths()
276 277

  
277 278
        self.lock_container_path = lock_container_path
278 279
        self.wrapper.execute()
279 280

  
281
        if account is None:     # no path
282
            return
283

  
284
        # check permissions path
285
        if action == 'read':
286
            check_func = self.can_read
287
        else:
288
            check_func = self.can_write
289
        check_func(user, account, container, name)
290

  
291
        # lock container path if it is requested so
292
        if container is not None and self.lock_container_path:
293
            self._lookup_container(account, container)
294

  
280 295
    def post_exec(self, success_status=True):
281 296
        if success_status:
282 297
            # send messages produced
......
315 330
        self.wrapper.close()
316 331
        self.queue.close()
317 332

  
333
        self.lock_container_path = None
334
        self.container_tuple = None
335

  
336
        self._reset_allowed_paths()
337

  
318 338
    @property
319 339
    def using_external_quotaholder(self):
320 340
        return not isinstance(self.astakosclient, DisabledAstakosClient)
......
496 516
                raise NotAllowedError
497 517
            allowed = self.permissions.access_list_paths(
498 518
                user, '/'.join((account, container)))
499
        path, node = self._lookup_container(account, container)
519
        path, node = self._get_container_tuple(account, container)
500 520
        before = until if until is not None else inf
501 521
        allowed = self._get_formatted_paths(allowed)
502 522
        return self.node.latest_attribute_keys(node, domain, before,
......
511 531
        if user != account:
512 532
            if until:
513 533
                raise NotAllowedError
514
        path, node = self._lookup_container(account, container)
534
        path, node = self._get_container_tuple(account, container)
515 535
        props = self._get_properties(node, until)
516 536
        mtime = props[self.MTIME]
517 537
        count, bytes, tstamp = self._get_statistics(node, until)
......
542 562
        """Update the metadata associated with the container for the domain."""
543 563

  
544 564
        self.can_write(user, account, container)
545
        path, node = self._lookup_container(account, container)
565
        path, node = self._get_container_tuple(account, container)
546 566
        src_version_id, dest_version_id = self._put_metadata(
547 567
            user, node, domain, meta, replace,
548 568
            update_statistics_ancestors_depth=0)
......
560 580
        self.can_read(user, account, container)
561 581
        if user != account:
562 582
            return {}
563
        path, node = self._lookup_container(account, container)
583
        path, node = self._get_container_tuple(account, container)
564 584
        return self._get_policy(node, is_account_policy=False)
565 585

  
566 586
    @debug_method
......
569 589
        """Update the policy associated with the container."""
570 590

  
571 591
        self.can_write(user, account, container)
572
        path, node = self._lookup_container(account, container)
592
        path, node = self._get_container_tuple(account, container)
573 593
        self._check_policy(policy, is_account_policy=False)
574 594
        self._put_policy(node, policy, replace, is_account_policy=False)
575 595

  
......
580 600
        self.can_write(user, account, container)
581 601
        policy = policy or {}
582 602
        try:
583
            path, node = self._lookup_container(account, container)
603
            path, node = self._get_container_tuple(account, container)
584 604
        except NameError:
585 605
            pass
586 606
        else:
......
599 619
        """Delete/purge the container with the given name."""
600 620

  
601 621
        self.can_write(user, account, container)
602
        path, node = self._lookup_container(account, container)
622
        path, node = self._get_container_tuple(account, container)
603 623

  
604 624
        if until is not None:
605 625
            hashes, size, serials = self.node.node_purge_children(
......
678 698
                user, account, container, prefix, shared=True, public=False)
679 699
            objects = set()
680 700
            if shared_paths:
681
                path, node = self._lookup_container(account, container)
701
                path, node = self._get_container_tuple(account, container)
682 702
                shared_paths = self._get_formatted_paths(shared_paths)
683 703
                objects |= set(self._list_object_properties(
684 704
                    node, path, prefix, delimiter, marker, limit, virtual,
......
704 724
            user, account, container, prefix, shared, public)
705 725
        if shared and not allowed:
706 726
            return []
707
        path, node = self._lookup_container(account, container)
727
        path, node = self._get_container_tuple(account, container)
708 728
        allowed = self._get_formatted_paths(allowed)
709 729
        objects = self._list_object_properties(
710 730
            node, path, prefix, delimiter, marker, limit, virtual, domain,
......
907 927
        self.permissions.access_set(path, permissions)
908 928
        self._report_sharing_change(user, account, path, {'members':
909 929
                                    self.permissions.access_members(path)})
930
        # reset allowed paths since they may be affected
931
        self._reset_allowed_paths()
910 932

  
911 933
    @debug_method
912 934
    def get_object_public(self, user, account, container, name):
......
953 975
            self._check_permissions(path, permissions)
954 976

  
955 977
        account_path, account_node = self._lookup_account(account, True)
956
        container_path, container_node = self._lookup_container(
978
        container_path, container_node = self._get_container_tuple(
957 979
            account, container)
958 980

  
959 981
        path, node = self._put_object_node(
......
1047 1069
        self.can_write(user, account, container, name)
1048 1070
        # Update objects with greater version and same hashmap
1049 1071
        # and size (fix metadata updates).
1050
        self._can_write(user, account, container, name)
1072
        self.can_write(user, account, container, name)
1051 1073
        path, node = self._lookup_object(account, container, name)
1052 1074
        props = self._get_version(node, version)
1053 1075
        versions = self.node.node_get_versions(node)
......
1067 1089
        report_size_change = not is_move
1068 1090
        dest_meta = dest_meta or {}
1069 1091
        dest_version_ids = []
1070
        self._can_read(user, src_account, src_container, src_name)
1071 1092
        path, node = self._lookup_object(src_account, src_container, src_name)
1072 1093
        # TODO: Will do another fetch of the properties in duplicate version...
1073 1094
        props = self._get_version(
......
1129 1150

  
1130 1151
        self.can_read(user, src_account, src_container, src_name)
1131 1152
        self.can_write(user, dest_account, dest_container, dest_name)
1153

  
1154
        # lock destination container
1155
        self._lookup_container(dest_account, dest_container, lock=True)
1132 1156
        meta = meta or {}
1133 1157
        dest_version_id = self._copy_object(
1134 1158
            user, src_account, src_container, src_name, dest_account,
......
1145 1169

  
1146 1170
        self.can_read(user, src_account, src_container, src_name)
1147 1171
        self.can_write(user, dest_account, dest_container, dest_name)
1172

  
1173
        # lock destination container path if it's not already locked
1174
        if src_account != dest_account or src_container != dest_container:
1175
            self._lookup_container(dest_account, dest_container, lock=True)
1148 1176
        meta = meta or {}
1149 1177
        if user != src_account:
1150 1178
            raise NotAllowedError
......
1269 1297
            raise NameError
1270 1298
        path, serial = info
1271 1299
        account, container, name = path.split('/', 2)
1272
        self._can_read(user, account, container, name)
1300
        self.can_read(user, account, container, name)
1273 1301
        return (account, container, name)
1274 1302

  
1275 1303
    def get_block(self, hash):
......
1325 1353
                update_statistics_ancestors_depth=-1)  # User is account.
1326 1354
        return account, node
1327 1355

  
1328
    def _lookup_container(self, account, container):
1329
        for_update = True if self.lock_container_path else False
1356
    def _lookup_container(self, account, container, lock=False):
1357
        for_update = lock or self.lock_container_path
1330 1358
        path = '/'.join((account, container))
1331 1359
        node = self.node.node_lookup(path, for_update)
1332 1360
        if node is None:
1333 1361
            raise ItemNotExists('Container does not exist')
1362
        self.container_path = path, node
1334 1363
        return path, node
1335 1364

  
1336 1365
    def _lookup_object(self, account, container, name):
......
1587 1616

  
1588 1617
        if version_id is None:
1589 1618
            return 0
1590
        path, node = self._lookup_container(account, container)
1619
        path, node = self._get_container_tuple(account, container)
1591 1620
        versioning = self._get_policy(
1592 1621
            node, is_account_policy=False)['versioning']
1593 1622
        if versioning != 'auto':
......
1645 1674
                        return p
1646 1675
        return None
1647 1676

  
1677
    def _get_container_tuple(self, account, container):
1678
        # Check if container_tuple is already set
1679
        # and if it's not then call _lookup_container
1680
        if self.container_tuple is not None:
1681
            path, node = self.container_tuple
1682
            if path == '/'.join((account, container)):
1683
                return path, node
1684
        return self._lookup_container(account, container)
1685

  
1686
    def _reset_allowed_paths(self):
1687
        self.read_allowed = defaultdict(list)
1688
        self.write_allowed = defaultdict(list)
1689

  
1648 1690
    def can_read(self, user, account, container=None, name=None):
1649
        path = '/'.join((account, container or '', name or ''))
1691
        path = '/'.join([p for p in (account, container, name) if
1692
                         p is not None])
1650 1693
        if path in self.read_allowed[user]:
1651 1694
            return
1652 1695
        self._can_read(user, account, container, name)
1653 1696
        self.read_allowed[user].append(path)
1654 1697

  
1655 1698
    def can_write(self, user, account, container=None, name=None):
1656
        path = '/'.join((account, container or '', name or ''))
1699
        path = '/'.join([p for p in (account, container, name) if
1700
                         p is not None])
1657 1701
        if path in self.write_allowed[user]:
1658 1702
            return True
1659 1703
        self._can_write(user, account, container, name)

Also available in: Unified diff