Revision b0a2d1a6 pithos/backends/simple.py

b/pithos/backends/simple.py
84 84
                    foreign key (version_id) references versions(version_id)
85 85
                    on delete cascade)'''
86 86
        self.con.execute(sql)
87
        
88 87
        sql = '''create table if not exists policy (
89 88
                    name text, key text, value text, primary key (name, key))'''
90 89
        self.con.execute(sql)
91 90
        
91
        # Access control tables.
92 92
        sql = '''create table if not exists groups (
93
                    account text, name text, users text, primary key (account, name))'''
93
                    account text, gname text, user text)'''
94 94
        self.con.execute(sql)
95 95
        sql = '''create table if not exists permissions (
96
                    name text, read text, write text, primary key (name))'''
96
                    name text, op text, user text)'''
97 97
        self.con.execute(sql)
98 98
        sql = '''create table if not exists public (
99 99
                    name text, primary key (name))'''
100 100
        self.con.execute(sql)
101
        
101 102
        self.con.commit()
102 103
        
103 104
        params = {'blocksize': self.block_size,
......
167 168
        logger.debug("update_account_groups: %s %s %s", account, groups, replace)
168 169
        if user != account:
169 170
            raise NotAllowedError
170
        for k, v in groups.iteritems():
171
            if True in [False or ',' in x for x in v]:
172
                raise ValueError('Bad characters in groups')
173
        if replace:
174
            sql = 'delete from groups where account = ?'
175
            self.con.execute(sql, (account,))
176
        for k, v in groups.iteritems():
177
            if len(v) == 0:
178
                if not replace:
179
                    sql = 'delete from groups where account = ? and name = ?'
180
                    self.con.execute(sql, (account, k))
181
            else:
182
                sql = 'insert or replace into groups (account, name, users) values (?, ?, ?)'
183
                self.con.execute(sql, (account, k, ','.join(v)))
184
        self.con.commit()
171
        self._check_groups(groups)
172
        self._put_groups(account, groups, replace)
185 173
    
186 174
    def put_account(self, user, account):
187 175
        """Create a new account with the given name."""
......
209 197
            raise IndexError('Account is not empty')
210 198
        sql = 'delete from versions where name = ?'
211 199
        self.con.execute(sql, (account,))
212
        sql = 'delete from groups where name = ?'
213
        self.con.execute(sql, (account,))
200
        self._del_groups(account)
214 201
        self.con.commit()
215 202
    
216 203
    def list_containers(self, user, account, marker=None, limit=10000, until=None):
......
445 432
            sql = 'insert or replace into metadata (version_id, key, value) values (?, ?, ?)'
446 433
            self.con.execute(sql, (dest_version_id, k, v))
447 434
        if permissions is not None:
448
            sql = 'insert or replace into permissions (name, read, write) values (?, ?, ?)'
449
            self.con.execute(sql, (path, r, w))
435
            self._put_permissions(path, r, w)
450 436
        self.con.commit()
451 437
    
452 438
    def copy_object(self, user, account, src_container, src_name, dest_container, dest_name, dest_meta={}, replace_meta=False, permissions=None, src_version=None):
......
471 457
            sql = 'insert or replace into metadata (version_id, key, value) values (?, ?, ?)'
472 458
            self.con.execute(sql, (dest_version_id, k, v))
473 459
        if permissions is not None:
474
            sql = 'insert or replace into permissions (name, read, write) values (?, ?, ?)'
475
            self.con.execute(sql, (dest_path, r, w))
460
            self._put_permissions(dest_path, r, w)
476 461
        self.con.commit()
477 462
    
478 463
    def move_object(self, user, account, src_container, src_name, dest_container, dest_name, dest_meta={}, replace_meta=False, permissions=None):
......
667 652
                self.con.execute(sql, (dest_version_id, k, v))
668 653
        self.con.commit()
669 654
    
670
    def _get_groups(self, account):
671
        sql = 'select name, users from groups where account = ?'
672
        c = self.con.execute(sql, (account,))
673
        return dict([(x[0], x[1].split(',')) for x in c.fetchall()])
674
    
675 655
    def _check_policy(self, policy):
676 656
        for k in policy.keys():
677 657
            if policy[k] == '':
......
692 672
        c = self.con.execute(sql, (path,))
693 673
        return dict(c.fetchall())
694 674
    
675
    def _list_objects(self, path, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, keys=[], until=None):
676
        cont_prefix = path + '/'
677
        if keys and len(keys) > 0:
678
            sql = '''select distinct o.name, o.version_id from (%s) o, metadata m where o.name like ? and
679
                        m.version_id = o.version_id and m.key in (%s) order by o.name'''
680
            sql = sql % (self._sql_until(until), ', '.join('?' * len(keys)))
681
            param = (cont_prefix + prefix + '%',) + tuple(keys)
682
        else:
683
            sql = 'select name, version_id from (%s) where name like ? order by name'
684
            sql = sql % self._sql_until(until)
685
            param = (cont_prefix + prefix + '%',)
686
        c = self.con.execute(sql, param)
687
        objects = [(x[0][len(cont_prefix):], x[1]) for x in c.fetchall()]
688
        if delimiter:
689
            pseudo_objects = []
690
            for x in objects:
691
                pseudo_name = x[0]
692
                i = pseudo_name.find(delimiter, len(prefix))
693
                if not virtual:
694
                    # If the delimiter is not found, or the name ends
695
                    # with the delimiter's first occurence.
696
                    if i == -1 or len(pseudo_name) == i + len(delimiter):
697
                        pseudo_objects.append(x)
698
                else:
699
                    # If the delimiter is found, keep up to (and including) the delimiter.
700
                    if i != -1:
701
                        pseudo_name = pseudo_name[:i + len(delimiter)]
702
                    if pseudo_name not in [y[0] for y in pseudo_objects]:
703
                        if pseudo_name == x[0]:
704
                            pseudo_objects.append(x)
705
                        else:
706
                            pseudo_objects.append((pseudo_name, None))
707
            objects = pseudo_objects
708
        
709
        start = 0
710
        if marker:
711
            try:
712
                start = [x[0] for x in objects].index(marker) + 1
713
            except ValueError:
714
                pass
715
        if not limit or limit > 10000:
716
            limit = 10000
717
        return objects[start:start + limit]
718
    
719
    def _del_version(self, version):
720
        self.mapper.map_remv(version)
721
        sql = 'delete from versions where version_id = ?'
722
        self.con.execute(sql, (version,))
723
    
724
    # Access control functions.
725
    
726
    def _check_groups(self, groups):
727
        # Example follows.
728
        # for k, v in groups.iteritems():
729
        #     if True in [False or ',' in x for x in v]:
730
        #         raise ValueError('Bad characters in groups')
731
        pass
732
    
733
    def _get_groups(self, account):
734
        sql = 'select gname, user from groups where account = ?'
735
        c = self.con.execute(sql, (account,))
736
        groups = {}
737
        for row in c.fetchall():
738
            if row[0] not in groups:
739
                groups[row[0]] = []
740
            groups[row[0]].append(row[1])
741
        return groups
742
    
743
    def _put_groups(self, account, groups, replace=False):
744
        if replace:
745
            self._del_groups(account)
746
        for k, v in groups.iteritems():
747
            sql = 'delete from groups where account = ? and gname = ?'
748
            self.con.execute(sql, (account, k))
749
            if v:
750
                sql = 'insert into groups (account, gname, user) values (?, ?, ?)'
751
                self.con.executemany(sql, [(account, k, x) for x in v])
752
        self.con.commit()
753
    
754
    def _del_groups(self, account):
755
        sql = 'delete from groups where account = ?'
756
        self.con.execute(sql, (account,))
757
    
695 758
    def _is_allowed(self, user, account, container, name, op='read'):
696 759
        if user == account:
697 760
            return True
......
702 765
        
703 766
        # Expand groups.
704 767
        for x in ('read', 'write'):
705
            g_perms = []
768
            g_perms = set()
706 769
            for y in perms.get(x, []):
707
                groups = self._get_groups(account)
708
                if y in groups: #it's a group
709
                    for g_name in groups[y]:
710
                        g_perms.append(g_name)
711
                else: #it's a user
712
                    g_perms.append(y)
770
                if ':' in y:
771
                    g_account, g_name = y.split(':', 1)
772
                    groups = self._get_groups(g_account)
773
                    if g_name in groups:
774
                        g_perms.update(groups[g_name])
775
                else:
776
                    g_perms.add(y)
713 777
            perms[x] = g_perms
714 778
        
715
        if op == 'read' and user in perms.get('read', []):
779
        if op == 'read' and ('*' in perms['read'] or user in perms['read']):
716 780
            return True
717
        if user in perms.get('write', []):
781
        if '*' in perms['write'] or user in perms['write']:
718 782
            return True
719 783
        return False
720 784
    
......
739 803
        
740 804
        # Format given permissions.
741 805
        if len(permissions) == 0:
742
            return '', ''
806
            return [], []
743 807
        r = permissions.get('read', [])
744 808
        w = permissions.get('write', [])
745
        if True in [False or ',' in x for x in r]:
746
            raise ValueError('Bad characters in read permissions')
747
        if True in [False or ',' in x for x in w]:
748
            raise ValueError('Bad characters in write permissions')
749
        return ','.join(r), ','.join(w)
809
        # Examples follow.
810
        # if True in [False or ',' in x for x in r]:
811
        #     raise ValueError('Bad characters in read permissions')
812
        # if True in [False or ',' in x for x in w]:
813
        #     raise ValueError('Bad characters in write permissions')
814
        return r, w
750 815
    
751 816
    def _get_permissions(self, path):
752 817
        # Check for permissions at path or above.
753
        sql = 'select name, read, write from permissions where ? like name || ?'
818
        sql = 'select name, op, user from permissions where ? like name || ?'
754 819
        c = self.con.execute(sql, (path, '%'))
755
        row = c.fetchone()
756
        if not row:
757
            return path, {}
758
        
759
        name, r, w = row
760
        ret = {}
761
        if w != '':
762
            ret['write'] = w.split(',')
763
        if r != '':
764
            ret['read'] = r.split(',')
765
        return name, ret
820
        name = path
821
        perms = {} # Return nothing, if nothing is set.
822
        for row in c.fetchall():
823
            name = row[0]
824
            if row[1] not in perms:
825
                perms[row[1]] = []
826
            perms[row[1]].append(row[2])
827
        return name, perms
766 828
    
767 829
    def _put_permissions(self, path, r, w):
768
        if r == '' and w == '':
769
            sql = 'delete from permissions where name = ?'
770
            self.con.execute(sql, (path,))
771
        else:
772
            sql = 'insert or replace into permissions (name, read, write) values (?, ?, ?)'
773
            self.con.execute(sql, (path, r, w))
830
        sql = 'delete from permissions where name = ?'
831
        self.con.execute(sql, (path,))
832
        sql = 'insert into permissions (name, op, user) values (?, ?, ?)'
833
        if r:
834
            self.con.executemany(sql, [(path, 'read', x) for x in r])
835
        if w:
836
            self.con.executemany(sql, [(path, 'write', x) for x in w])
774 837
        self.con.commit()
775 838
    
776 839
    def _get_public(self, path):
......
795 858
        sql = 'delete from public where name = ?'
796 859
        self.con.execute(sql, (path,))
797 860
        self.con.commit()
798
    
799
    def _list_objects(self, path, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, keys=[], until=None):
800
        cont_prefix = path + '/'
801
        if keys and len(keys) > 0:
802
            sql = '''select distinct o.name, o.version_id from (%s) o, metadata m where o.name like ? and
803
                        m.version_id = o.version_id and m.key in (%s) order by o.name'''
804
            sql = sql % (self._sql_until(until), ', '.join('?' * len(keys)))
805
            param = (cont_prefix + prefix + '%',) + tuple(keys)
806
        else:
807
            sql = 'select name, version_id from (%s) where name like ? order by name'
808
            sql = sql % self._sql_until(until)
809
            param = (cont_prefix + prefix + '%',)
810
        c = self.con.execute(sql, param)
811
        objects = [(x[0][len(cont_prefix):], x[1]) for x in c.fetchall()]
812
        if delimiter:
813
            pseudo_objects = []
814
            for x in objects:
815
                pseudo_name = x[0]
816
                i = pseudo_name.find(delimiter, len(prefix))
817
                if not virtual:
818
                    # If the delimiter is not found, or the name ends
819
                    # with the delimiter's first occurence.
820
                    if i == -1 or len(pseudo_name) == i + len(delimiter):
821
                        pseudo_objects.append(x)
822
                else:
823
                    # If the delimiter is found, keep up to (and including) the delimiter.
824
                    if i != -1:
825
                        pseudo_name = pseudo_name[:i + len(delimiter)]
826
                    if pseudo_name not in [y[0] for y in pseudo_objects]:
827
                        if pseudo_name == x[0]:
828
                            pseudo_objects.append(x)
829
                        else:
830
                            pseudo_objects.append((pseudo_name, None))
831
            objects = pseudo_objects
832
        
833
        start = 0
834
        if marker:
835
            try:
836
                start = [x[0] for x in objects].index(marker) + 1
837
            except ValueError:
838
                pass
839
        if not limit or limit > 10000:
840
            limit = 10000
841
        return objects[start:start + limit]
842
    
843
    def _del_version(self, version):
844
        self.mapper.map_remv(version)
845
        sql = 'delete from versions where version_id = ?'
846
        self.con.execute(sql, (version,))

Also available in: Unified diff