source: trunk/packages/xvm-authz-locker/python/xvm/authz/locker.py @ 3019

Last change on this file since 3019 was 2989, checked in by broder, 15 years ago

Select an authz module using setuptools' entry points mechainsm.

Instead of having each authz package install an invirt.authz module,
have them install modules under their own namespace.

In their setup.py, they should indicate that their authz module
provides a unique name within the invirt.authz entry point group.

The new invirt.authz module (part of invirt-base) then gets a name
from the configuration and uses that to find the module.

File size: 3.8 KB
Line 
1import errno
2
3from afs import acl
4from afs import fs
5from afs import pts
6
7from invirt import common
8from invirt.config import structs as config
9from invirt import remctl
10
11
12#
13# expandOwner and expandAdmin form the API that needs to be exported
14# for all authz modules.
15#
16
17
18def expandOwner(name):
19    """Expand an owner to a list of authorized users.
20
21    For the locker authz module, an owner is an Athena locker. Those
22    users who have been given the administrator ('a') bit on the root
23    of a locker are given access to any VM owned by that locker,
24    unless they also have been given a negative administrator bit.
25
26    If a locker doesn't exist, or we can't access the permissions, we
27    assume the ACL is empty.
28    """
29    try:
30        path = _lockerPath(name)
31        cell = fs.whichcell(path)
32        auth = _authenticate(cell)
33        a = acl.ACL.retrieve(path)
34
35        allowed = set()
36        for ent in a.pos:
37            if a.pos[ent] & acl.ADMINISTER:
38                allowed.update(_expandGroup(ent, cell=cell, auth=auth))
39        for ent in a.neg:
40            if a.neg[ent] & acl.ADMINISTER:
41                allowed.difference_update(_expandGroup(ent, cell=cell, auth=auth))
42
43        return allowed
44    except OSError, e:
45        if e.errno in (errno.ENOENT, errno.EACCES):
46            return []
47        else:
48            raise
49
50
51def expandAdmin(name):
52    """Expand an administrator to a list of authorized users.
53
54    For locker-based authorization, the administrator is always
55    interpreted as an AFS entry (either a user or a group) in the
56    machine's home cell (athena.mit.edu for XVM).
57    """
58    cell = config.authz.afs.cells[0].cell
59    auth = _authenticate(cell)
60    return _expandGroup(name, cell=cell, auth=auth)
61
62
63#
64# These are helper functions, and aren't part of the authz API
65#
66
67
68def _authenticate(cell):
69    """Acquire AFS tokens for a cell if encryption is required by config.
70
71    If the Invirt configuration requires connections to this cell to
72    be encrypted, acquires tokens and returns True. Otherwise, returns
73    False. Consumers of this function must still be sure to encrypt
74    their own connections if necessary.
75
76    Cells not listed in the Invirt configuration default to requiring
77    encryption in order to maintain security by default.
78
79    Due to AFS's cross-realm auto-PTS-creation mechanism, using
80    authenticated connections by default should only fail for cells
81    which authenticate directly against the machine's home realm and
82    cells distantly related to the machine's home realm.
83    """
84    for c in config.authz.afs.cells:
85        if c.cell == cell and not c.auth:
86            return False
87
88    remctl.checkKinit()
89    common.captureOutput(['aklog', '-c', cell])
90    return True
91
92
93def _expandGroup(name, cell=None, auth=False):
94    """Expand an AFS group into a list of its members.
95
96    Because groups are not global, but can vary from cell to cell,
97    this function accepts as an optional argument the cell in which
98    this group should be resolved.
99
100    If no cell is specified, it is assumed that the default cell (or
101    ThisCell) should be used.
102
103    If the name is a user, not a group, then a single-element set with
104    the same name is returned.
105
106    As with expandOwner, if a group doesn't exist or if we're unable
107    to retrieve its membership, we assume it's empty.
108    """
109    try:
110        ent = pts.PTS(cell, pts.PTS_ENCRYPT if auth else pts.PTS_UNAUTH).\
111            getEntry(name)
112        if ent.id > 0:
113            return set([ent.name])
114        else:
115            return set([x.name for x in ent.members])
116    except OSError, e:
117        if e.errno in (errno.ENOENT, errno.EACCESS):
118            return set()
119        else:
120            raise
121
122
123def _lockerPath(owner):
124    """Given the name of a locker, return a path to that locker.
125
126    This turns out to be pretty simple, thanks to the /mit
127    automounter.
128    """
129    return '/mit/%s' % owner
Note: See TracBrowser for help on using the repository browser.