Add a invirt.authz.locker module to invirt-base for XVM-style
authorEvan Broder <broder@mit.edu>
Mon, 23 Nov 2009 04:14:51 +0000 (23:14 -0500)
committerEvan Broder <broder@mit.edu>
Mon, 23 Nov 2009 04:14:51 +0000 (23:14 -0500)
authorization.

svn path=/trunk/packages/invirt-base/; revision=2559

debian/control
python/invirt/authz/__init__.py [new file with mode: 0644]
python/invirt/authz/locker.py [new file with mode: 0644]

index bab7571..f300956 100644 (file)
@@ -9,7 +9,7 @@ Package: invirt-base
 Architecture: all
 Depends: ${python:Depends}, ${misc:Depends},
  python-json (>= 3.4-2), python-yaml (>= 3.05), python-mako (>=
 Architecture: all
 Depends: ${python:Depends}, ${misc:Depends},
  python-json (>= 3.4-2), python-yaml (>= 3.05), python-mako (>=
- 0.2.2), remctl-client, invirt-config
+ 0.2.2), remctl-client, invirt-config, python-afs
 Provides: ${python:Provides}
 XB-Python-Version: ${python:Versions}
 Description: Base configuration required for all Invirt servers
 Provides: ${python:Provides}
 XB-Python-Version: ${python:Versions}
 Description: Base configuration required for all Invirt servers
diff --git a/python/invirt/authz/__init__.py b/python/invirt/authz/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/python/invirt/authz/locker.py b/python/invirt/authz/locker.py
new file mode 100644 (file)
index 0000000..4e22d2a
--- /dev/null
@@ -0,0 +1,101 @@
+import errno
+
+from afs import acl
+from afs import fs
+from afs import pts
+
+from invirt.config import structs as config
+
+#
+# expandOwner and expandAdmin form the API that needs to be exported
+# for all authz modules.
+#
+
+# TODO: Make expandOwner and expandAdmin deal with acquiring tokens
+# and encrypting the connection to the prdb as necessary/requested by
+# the configuration.
+def expandOwner(name):
+    """Expand an owner to a list of authorized users.
+
+    For the locker authz module, an owner is an Athena locker. Those
+    users who have been given the administrator ('a') bit on the root
+    of a locker are given access to any VM owned by that locker,
+    unless they also have been given a negative administrator bit.
+
+    If a locker doesn't exist, or we can't access the permissions, we
+    assume the ACL is empty.
+    """
+    try:
+        path = _lockerPath(name)
+        cell = fs.whichcell(path)
+        a = acl.ACL.retrieve(path)
+
+        allowed = set()
+        for ent in a.pos:
+            if a.pos[ent] & acl.ADMINISTER:
+                allowed.update(_expandGroup(ent, cell))
+        for ent in a.neg:
+            if a.neg[ent] & acl.ADMINISTER:
+                allowed.difference_update(_expandGroup(ent, cell))
+
+        return allowed
+    except OSError, e:
+        if e.errno in (errno.ENOENT, errno.EACCES):
+            return []
+        else:
+            raise
+
+
+def expandAdmin(name, owner):
+    """Expand an administrator to a list of authorized users.
+
+    Because the interpretation of an administrator might depend on the
+    owner, the owner is passed in as an argument.
+
+    However, in the case of locker-based authentication, the
+    administrator is always interpreted as an AFS entry (either a user
+    or a group) in the home cell (athena.mit.edu for XVM).
+    """
+    return _expandGroup(name)
+
+
+#
+# These are helper functions, and aren't part of the authz API
+#
+
+def _expandGroup(name, cell=None):
+    """Expand an AFS group into a list of its members.
+
+    Because groups are not global, but can vary from cell to cell,
+    this function accepts as an optional argument the cell in which
+    this group should be resolved.
+
+    If no cell is specified, it is assumed that the default cell (or
+    ThisCell) should be used.
+
+    If the name is a user, not a group, then a single-element set with
+    the same name is returned.
+
+    As with expandOwner, if a group doesn't exist or if we're unable
+    to retrieve its membership, we assume it's empty.
+    """
+    try:
+        ent = pts.PTS(cell).getEntry(name)
+        if ent.id > 0:
+            return set([ent.name])
+        else:
+            return set([x.name for x in ent.members])
+    except OSError, e:
+        if e.errno in (errno.ENOENT, errno.EACCESS):
+            return set()
+        else:
+            raise
+
+
+def _lockerPath(owner):
+    """Given the name of a locker, return a path to that locker.
+
+    This turns out to be pretty simple, thanks to the /mit
+    automounter.
+    """
+    return '/mit/%s' % owner