Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / lib / sqlite / permissions.py @ 3759eddb

History | View | Annotate | Download (9.1 kB)

1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from xfeatures import XFeatures
35
from groups import Groups
36
from public import Public
37
from node import Node
38
from collections import defaultdict
39

    
40

    
41
READ = 0
42
WRITE = 1
43

    
44

    
45
class Permissions(XFeatures, Groups, Public, Node):
46

    
47
    def __init__(self, **params):
48
        XFeatures.__init__(self, **params)
49
        Groups.__init__(self, **params)
50
        Public.__init__(self, **params)
51
        Node.__init__(self, **params)
52

    
53
    def access_grant(self, path, access, members=()):
54
        """Grant members with access to path.
55
           Members can also be '*' (all),
56
           or some group specified as 'owner:group'."""
57

    
58
        if not members:
59
            return
60
        feature = self.xfeature_create(path)
61
        self.feature_setmany(feature, access, members)
62

    
63
    def access_set(self, path, permissions):
64
        """Set permissions for path. The permissions dict
65
           maps 'read', 'write' keys to member lists."""
66

    
67
        r = permissions.get('read', [])
68
        w = permissions.get('write', [])
69
        if not r and not w:
70
            self.xfeature_destroy(path)
71
            return
72
        feature = self.xfeature_create(path)
73
        self.feature_clear(feature, READ)
74
        self.feature_clear(feature, WRITE)
75
        if r:
76
            self.feature_setmany(feature, READ, r)
77
        if w:
78
            self.feature_setmany(feature, WRITE, w)
79

    
80
    def access_get_for_bulk(self, perms):
81
        """Get permissions for paths."""
82

    
83
        allowed = None
84
        d = defaultdict(list)
85
        for value, feature_id, key in perms:
86
            d[key].append(value)
87
        permissions = d
88
        if READ in permissions:
89
            allowed = 0
90
            permissions['read'] = permissions[READ]
91
            del(permissions[READ])
92
        if WRITE in permissions:
93
            allowed = 1
94
            permissions['write'] = permissions[WRITE]
95
            del(permissions[WRITE])
96
        return (permissions, allowed)
97

    
98
    def access_get(self, path):
99
        """Get permissions for path."""
100

    
101
        feature = self.xfeature_get(path)
102
        if not feature:
103
            return {}
104
        permissions = self.feature_dict(feature)
105
        if READ in permissions:
106
            permissions['read'] = permissions[READ]
107
            del(permissions[READ])
108
        if WRITE in permissions:
109
            permissions['write'] = permissions[WRITE]
110
            del(permissions[WRITE])
111
        return permissions
112

    
113
    def access_members(self, path):
114
        feature = self.xfeature_get(path)
115
        if not feature:
116
            return []
117
        permissions = self.feature_dict(feature)
118
        members = set()
119
        members.update(permissions.get(READ, []))
120
        members.update(permissions.get(WRITE, []))
121
        for m in set(members):
122
            parts = m.split(':', 1)
123
            if len(parts) != 2:
124
                continue
125
            user, group = parts
126
            members.remove(m)
127
            members.update(self.group_members(user, group))
128
        return members
129

    
130
    def access_clear(self, path):
131
        """Revoke access to path (both permissions and public)."""
132

    
133
        self.xfeature_destroy(path)
134
        self.public_unset(path)
135

    
136
    def access_clear_bulk(self, paths):
137
        """Revoke access to path (both permissions and public)."""
138

    
139
        self.xfeature_destroy_bulk(paths)
140
        self.public_unset_bulk(paths)
141

    
142
    def access_check(self, path, access, member):
143
        """Return true if the member has this access to the path."""
144

    
145
        feature = self.xfeature_get(path)
146
        if not feature:
147
            return False
148
        members = self.feature_get(feature, access)
149
        if member in members or '*' in members:
150
            return True
151
        for owner, group in self.group_parents(member):
152
            if owner + ':' + group in members:
153
                return True
154
        return False
155

    
156
    def access_check_bulk(self, paths, member):
157
        rows = None
158
        q = ("select x.path, xvals.value, xvals.feature_id, xvals.key "
159
             "from xfeaturevals xvals join xfeatures x "
160
             "on xvals.feature_id = x.feature_id "
161
             "where x.path in (%s)") % ','.join('?' for _ in paths)
162
        self.execute(q, paths)
163
        rows = self.fetchall()
164
        if rows:
165
            access_check_paths = {}
166
            for path, value, feature_id, key in rows:
167
                try:
168
                    access_check_paths[path].append((value, feature_id, key))
169
                except KeyError:
170
                    access_check_paths[path] = [(value, feature_id, key)]
171
            return access_check_paths
172
        return None
173

    
174
    def access_inherit(self, path):
175
        """Return the paths influencing the access for path."""
176

    
177
#         r = self.xfeature_inherit(path)
178
#         if not r:
179
#             return []
180
#         # Compute valid.
181
#         return [x[0] for x in r if x[0] in valid]
182

    
183
        # Only keep path components.
184
        parts = path.rstrip('/').split('/')
185
        valid = []
186
        for i in range(1, len(parts)):
187
            subp = '/'.join(parts[:i + 1])
188
            valid.append(subp)
189
            if subp != path:
190
                valid.append(subp + '/')
191
        return [x for x in valid if self.xfeature_get(x)]
192

    
193
    def access_inherit_bulk(self, paths):
194
        """Return the paths influencing the access for paths."""
195

    
196
        # Only keep path components.
197
        valid = []
198
        for path in paths:
199
            parts = path.rstrip('/').split('/')
200
            for i in range(1, len(parts)):
201
                subp = '/'.join(parts[:i + 1])
202
                valid.append(subp)
203
                if subp != path:
204
                    valid.append(subp + '/')
205
        valid = self.xfeature_get_bulk(valid)
206
        return [x[1] for x in valid]
207

    
208
    def access_list_paths(self, member, prefix=None, include_owned=False,
209
                          include_containers=True):
210
        """Return the list of paths granted to member.
211

212
        Keyword arguments:
213
        prefix -- return only paths starting with prefix (default None)
214
        include_owned -- return also paths owned by member (default False)
215
        include_containers -- return also container paths owned by member
216
                              (default True)
217

218
        """
219

    
220
        q = ("select distinct path from xfeatures inner join "
221
             "  (select distinct feature_id, key from xfeaturevals inner join "
222
             "     (select owner || ':' || name as value from groups "
223
             "      where member = ? union select ? union select '*') "
224
             "   using (value)) "
225
             "using (feature_id)")
226
        p = (member, member)
227
        if prefix:
228
            q += " where "
229
            paths = self.access_inherit(prefix) or [prefix]
230
            q += ' or '.join("path like ? escape '\\'" for _ in paths)
231
            p += tuple(self.escape_like(path) + '%' for path in paths)
232
        self.execute(q, p)
233

    
234
        l = [r[0] for r in self.fetchall()]
235
        if include_owned:
236
            node = self.node_lookup(member)
237
            select_containers = "select node from nodes where parent = ? "
238
            q = ("select path from nodes where parent in (%s) " %
239
                 select_containers)
240
            args = [node]
241
            if include_containers:
242
                q += ("or node in (%s)" % select_containers)
243
                args += [node]
244
            self.execute(q, args)
245
            l += [r[0] for r in self.fetchall() if r[0] not in l]
246
        return l
247

    
248
    def access_list_shared(self, prefix=''):
249
        """Return the list of shared paths."""
250

    
251
        q = "select path from xfeatures where "
252
        paths = self.access_inherit(prefix) or [prefix]
253
        q += ' or '.join("path like ? escape '\\'" for _ in paths)
254
        p = tuple(self.escape_like(path) + '%' for path in paths)
255
        self.execute(q, p)
256
        return [r[0] for r in self.fetchall()]