Remove extra 'w' from canonical strings for 'write' and 'all
[invirt/packages/python-afs.git] / afs / acl.py
index b5da255..c49f374 100644 (file)
@@ -4,52 +4,45 @@ from _acl import READ, WRITE, INSERT, LOOKUP, DELETE, LOCK, ADMINISTER, \
 from _acl import getCallerAccess
 
 _canonical = {
-    "read":     "rl",
-    "write":    "rwlidwk",
-    "all":      "rwlidwka",
-    "mail":     "lik",
-    "none":     "",
-}
-_char2bit = {
-    'r': READ,
-    'w': WRITE,
-    'i': INSERT,
-    'l': LOOKUP,
-    'd': DELETE,
-    'k': LOCK,
-    'a': ADMINISTER,
-    'A': USR0,
-    'B': USR1,
-    'C': USR2,
-    'D': USR3,
-    'E': USR4,
-    'F': USR5,
-    'G': USR6,
-    'H': USR7,
+    "read": "rl",
+    "write": "rlidwk",
+    "all": "rlidwka",
+    "mail": "lik",
+    "none": "",
 }
 
-_bit2char = dict([(v,k) for k,v in _char2bit.items()])
+_charBitAssoc = [
+    ('r', READ),
+    ('w', WRITE),
+    ('i', INSERT),
+    ('l', LOOKUP),
+    ('d', DELETE),
+    ('k', LOCK),
+    ('a', ADMINISTER),
+    ('A', USR0),
+    ('B', USR1),
+    ('C', USR2),
+    ('D', USR3),
+    ('E', USR4),
+    ('F', USR5),
+    ('G', USR6),
+    ('H', USR7),
+]
+
+_char2bit = dict(_charBitAssoc)
 
-def crights(s):
+
+def readRights(s):
     """Canonicalizes string rights to bitmask"""
     if s in _canonical: s = _canonical[s]
     return _parseRights(s)
 
-class ACL(object):
-    def __init__(self, pos, neg):
-        """
-        ``pos``
-            Dictionary of usernames to positive ACL bitmasks
-        ``neg``
-            Dictionary of usernames to negative ACL bitmasks
-        """
-        self.pos = pos
-        self.neg = neg
-    @staticmethod
-    def retrieve(dir):
-        """Retrieve the ACL for an AFS directory"""
-        pos, neg = _parseAcl(_acl.getAcl(dir))
-        return ACL(pos, neg)
+def showRights(r):
+    """Takes a bitmask and returns a rwlidka string"""
+    s = ""
+    for char,mask in _charBitAssoc:
+        if r & mask == mask: s += char
+    return s
 
 def _parseRights(s):
     """Parses a rwlid... rights tring to bitmask"""
@@ -77,3 +70,52 @@ def _parseAcl(inp):
             neg[name] = int(acl)
     return (pos, neg)
 
+def _unparseAcl(pos, neg):
+    npos = len(pos)
+    nneg = len(neg)
+    acl = "%d\n%d\n" % (npos, nneg)
+    for p in pos.items():
+        acl += "%s\t%d\n" % p
+    for n in neg.items():
+        acl += "%s\t%d\n" % n
+    return acl
+
+class ACL(object):
+    def __init__(self, pos, neg):
+        """
+        ``pos``
+            Dictionary of usernames to positive ACL bitmasks
+        ``neg``
+            Dictionary of usernames to negative ACL bitmasks
+        """
+        self.pos = pos
+        self.neg = neg
+    @staticmethod
+    def retrieve(dir, follow=True):
+        """Retrieve the ACL for an AFS directory"""
+        pos, neg = _parseAcl(_acl.getAcl(dir, follow))
+        return ACL(pos, neg)
+    def apply(self, dir, follow=True):
+        """Apply the ACL to a directory"""
+        self._clean()
+        _acl.setAcl(dir, _unparseAcl(self.pos, self.neg), follow)
+    def _clean(self):
+        """Clean an ACL by removing any entries whose bitmask is 0"""
+        for n,a in self.pos.items():
+            if a == 0:
+                del self.pos[n]
+        for n,a in self.neg.items():
+            if a == 0:
+                del self.neg[n]
+    def set(self, user, bitmask, negative=False):
+        """Set the bitmask for a given user"""
+        if bitmask < 0 or bitmask > max(_char2bit.values()):
+            raise ValueError, "Invalid bitmask"
+        if negative:
+            self.neg[user] = bitmask
+        else:
+            self.pos[user] = bitmask
+    def remove(self, user, negative=False):
+        """Convenience function to removeSet the bitmask for a given user"""
+        self.set(user, 0, negative)
+