Remove extra 'w' from canonical strings for 'write' and 'all
[invirt/packages/python-afs.git] / afs / acl.py
1 import _acl
2 from _acl import READ, WRITE, INSERT, LOOKUP, DELETE, LOCK, ADMINISTER, \
3     USR0, USR1, USR2, USR3, USR4, USR5, USR6, USR7
4 from _acl import getCallerAccess
5
6 _canonical = {
7     "read": "rl",
8     "write": "rlidwk",
9     "all": "rlidwka",
10     "mail": "lik",
11     "none": "",
12 }
13
14 _charBitAssoc = [
15     ('r', READ),
16     ('w', WRITE),
17     ('i', INSERT),
18     ('l', LOOKUP),
19     ('d', DELETE),
20     ('k', LOCK),
21     ('a', ADMINISTER),
22     ('A', USR0),
23     ('B', USR1),
24     ('C', USR2),
25     ('D', USR3),
26     ('E', USR4),
27     ('F', USR5),
28     ('G', USR6),
29     ('H', USR7),
30 ]
31
32 _char2bit = dict(_charBitAssoc)
33
34
35 def readRights(s):
36     """Canonicalizes string rights to bitmask"""
37     if s in _canonical: s = _canonical[s]
38     return _parseRights(s)
39
40 def showRights(r):
41     """Takes a bitmask and returns a rwlidka string"""
42     s = ""
43     for char,mask in _charBitAssoc:
44         if r & mask == mask: s += char
45     return s
46
47 def _parseRights(s):
48     """Parses a rwlid... rights tring to bitmask"""
49     r = 0
50     try:
51         for c in s:
52             r = r | _char2bit[c]
53     except KeyError:
54         raise ValueError
55     return r
56
57 def _parseAcl(inp):
58     lines = inp.split("\n")
59     npos = int(lines[0].split(" ")[0])
60     pos = {}
61     neg = {}
62     for l in lines[2:]:
63         if l == "": continue
64         name, acl = l.split()
65         if npos:
66             npos -= 1
67             pos[name] = int(acl)
68         else:
69             # negative acl
70             neg[name] = int(acl)
71     return (pos, neg)
72
73 def _unparseAcl(pos, neg):
74     npos = len(pos)
75     nneg = len(neg)
76     acl = "%d\n%d\n" % (npos, nneg)
77     for p in pos.items():
78         acl += "%s\t%d\n" % p
79     for n in neg.items():
80         acl += "%s\t%d\n" % n
81     return acl
82
83 class ACL(object):
84     def __init__(self, pos, neg):
85         """
86         ``pos``
87             Dictionary of usernames to positive ACL bitmasks
88         ``neg``
89             Dictionary of usernames to negative ACL bitmasks
90         """
91         self.pos = pos
92         self.neg = neg
93     @staticmethod
94     def retrieve(dir, follow=True):
95         """Retrieve the ACL for an AFS directory"""
96         pos, neg = _parseAcl(_acl.getAcl(dir, follow))
97         return ACL(pos, neg)
98     def apply(self, dir, follow=True):
99         """Apply the ACL to a directory"""
100         self._clean()
101         _acl.setAcl(dir, _unparseAcl(self.pos, self.neg), follow)
102     def _clean(self):
103         """Clean an ACL by removing any entries whose bitmask is 0"""
104         for n,a in self.pos.items():
105             if a == 0:
106                 del self.pos[n]
107         for n,a in self.neg.items():
108             if a == 0:
109                 del self.neg[n]
110     def set(self, user, bitmask, negative=False):
111         """Set the bitmask for a given user"""
112         if bitmask < 0 or bitmask > max(_char2bit.values()):
113             raise ValueError, "Invalid bitmask"
114         if negative:
115             self.neg[user] = bitmask
116         else:
117             self.pos[user] = bitmask
118     def remove(self, user, negative=False):
119         """Convenience function to removeSet the bitmask for a given user"""
120         self.set(user, 0, negative)
121