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