Index: /trunk/packages/invirt-base/debian/changelog
===================================================================
--- /trunk/packages/invirt-base/debian/changelog	(revision 2988)
+++ /trunk/packages/invirt-base/debian/changelog	(revision 2989)
@@ -1,2 +1,9 @@
+invirt-base (0.0.32) unstable; urgency=low
+
+  * Add back an invirt.authz module, but restructure it so it uses
+    entrypoints to find an actual backend module.
+
+ -- Evan Broder <broder@mit.edu>  Fri, 05 Feb 2010 09:46:51 -0500
+
 invirt-base (0.0.31) unstable; urgency=low
 
Index: /trunk/packages/invirt-base/python/invirt/authz.py
===================================================================
--- /trunk/packages/invirt-base/python/invirt/authz.py	(revision 2989)
+++ /trunk/packages/invirt-base/python/invirt/authz.py	(revision 2989)
@@ -0,0 +1,31 @@
+"""Invirt authorization.
+
+This module acts as a loader for the pluggable authorization system.
+
+Any Python module which wishes to provide an authorization scheme for
+Invirt should advertise an entry point in the invirt.authz group with
+a unique name. That name can then be configured in
+/etc/invirt/master.yaml as the authz mechanism.
+"""
+
+
+import pkg_resources
+
+from invirt.config import structs as cfg
+
+
+def expandOwner(name):
+    """Expand an "owner" to a list of authorized users."""
+    for ep in pkg_resources.iter_entry_points('invirt.authz', cfg.authz.name):
+        return ep.load().expandOwner(name)
+
+
+def expandAdmin(name):
+    """Expand an "administrator" to a list of authorized users."""
+    for ep in pkg_resources.iter_entry_points('invirt.authz', cfg.authz.name):
+        return ep.load().expandAdmin(name)
+
+
+__all__ = ['expandOwner',
+           'expandAdmin',
+           ]
Index: /trunk/packages/xvm-authz-locker/debian/changelog
===================================================================
--- /trunk/packages/xvm-authz-locker/debian/changelog	(revision 2988)
+++ /trunk/packages/xvm-authz-locker/debian/changelog	(revision 2989)
@@ -1,2 +1,9 @@
+xvm-authz-locker (0.0.3) unstable; urgency=low
+
+  * Go back to having an xvm.authz.locker module, but use entry points to
+    find it.
+
+ -- Evan Broder <broder@mit.edu>  Fri, 05 Feb 2010 09:38:05 -0500
+
 xvm-authz-locker (0.0.2) unstable; urgency=low
 
Index: /trunk/packages/xvm-authz-locker/debian/control
===================================================================
--- /trunk/packages/xvm-authz-locker/debian/control	(revision 2988)
+++ /trunk/packages/xvm-authz-locker/debian/control	(revision 2989)
@@ -10,6 +10,4 @@
 Depends: ${python:Depends}, ${misc:Depends}, invirt-base, python-afs
 Provides: ${python:Provides}, invirt-authz
-Conflicts: invirt-authz
-Replaces: invirt-authz
 Breaks: invirt-base (<< 0.0.28~)
 XB-Python-Version: ${python:Versions}
Index: /trunk/packages/xvm-authz-locker/python/xvm/authz/locker.py
===================================================================
--- /trunk/packages/xvm-authz-locker/python/xvm/authz/locker.py	(revision 2989)
+++ /trunk/packages/xvm-authz-locker/python/xvm/authz/locker.py	(revision 2989)
@@ -0,0 +1,129 @@
+import errno
+
+from afs import acl
+from afs import fs
+from afs import pts
+
+from invirt import common
+from invirt.config import structs as config
+from invirt import remctl
+
+
+#
+# expandOwner and expandAdmin form the API that needs to be exported
+# for all authz modules.
+#
+
+
+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)
+        auth = _authenticate(cell)
+        a = acl.ACL.retrieve(path)
+
+        allowed = set()
+        for ent in a.pos:
+            if a.pos[ent] & acl.ADMINISTER:
+                allowed.update(_expandGroup(ent, cell=cell, auth=auth))
+        for ent in a.neg:
+            if a.neg[ent] & acl.ADMINISTER:
+                allowed.difference_update(_expandGroup(ent, cell=cell, auth=auth))
+
+        return allowed
+    except OSError, e:
+        if e.errno in (errno.ENOENT, errno.EACCES):
+            return []
+        else:
+            raise
+
+
+def expandAdmin(name):
+    """Expand an administrator to a list of authorized users.
+
+    For locker-based authorization, the administrator is always
+    interpreted as an AFS entry (either a user or a group) in the
+    machine's home cell (athena.mit.edu for XVM).
+    """
+    cell = config.authz.afs.cells[0].cell
+    auth = _authenticate(cell)
+    return _expandGroup(name, cell=cell, auth=auth)
+
+
+#
+# These are helper functions, and aren't part of the authz API
+#
+
+
+def _authenticate(cell):
+    """Acquire AFS tokens for a cell if encryption is required by config.
+
+    If the Invirt configuration requires connections to this cell to
+    be encrypted, acquires tokens and returns True. Otherwise, returns
+    False. Consumers of this function must still be sure to encrypt
+    their own connections if necessary.
+
+    Cells not listed in the Invirt configuration default to requiring
+    encryption in order to maintain security by default.
+
+    Due to AFS's cross-realm auto-PTS-creation mechanism, using
+    authenticated connections by default should only fail for cells
+    which authenticate directly against the machine's home realm and
+    cells distantly related to the machine's home realm.
+    """
+    for c in config.authz.afs.cells:
+        if c.cell == cell and not c.auth:
+            return False
+
+    remctl.checkKinit()
+    common.captureOutput(['aklog', '-c', cell])
+    return True
+
+
+def _expandGroup(name, cell=None, auth=False):
+    """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, pts.PTS_ENCRYPT if auth else pts.PTS_UNAUTH).\
+            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
Index: /trunk/packages/xvm-authz-locker/setup.py
===================================================================
--- /trunk/packages/xvm-authz-locker/setup.py	(revision 2988)
+++ /trunk/packages/xvm-authz-locker/setup.py	(revision 2989)
@@ -24,5 +24,11 @@
     maintainer_email=maintainer_email,
     
-    py_modules = ['invirt.authz'],
+    py_modules = ['xvm.authz.locker'],
     package_dir = {'': 'python'},
+
+    entry_points = {
+        'invirt.authz': [
+            'xvm-locker = xvm.authz.locker',
+            ],
+        },
 )
