source: trunk/packages/invirt-base/python/invirt/authz/locker.py @ 2572

Last change on this file since 2572 was 2572, checked in by broder, 14 years ago

Take advantage of the newly added symbolic names for PTS connection
encryption levels.

File size: 4.0 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, owner):
52    """Expand an administrator to a list of authorized users.
53
54    Because the interpretation of an administrator might depend on the
55    owner, the owner is passed in as an argument.
56
57    However, in the case of locker-based authentication, the
58    administrator is always interpreted as an AFS entry (either a user
59    or a group) in the home cell (athena.mit.edu for XVM).
60    """
61    cell = config.authz.cells[0].cell
62    auth = _authenticate(cell)
63    return _expandGroup(name, cell=cell, auth=auth)
64
65
66#
67# These are helper functions, and aren't part of the authz API
68#
69
70
71def _authenticate(cell):
72    """Acquire AFS tokens for a cell if encryption is required by config.
73
74    If the Invirt configuration requires connections to this cell to
75    be encrypted, acquires tokens and returns True. Otherwise, returns
76    False. Consumers of this function must still be sure to encrypt
77    their own connections if necessary.
78
79    Cells not listed in the Invirt configuration default to requiring
80    encryption in order to maintain security by default.
81
82    Due to AFS's cross-realm auto-PTS-creation mechanism, using
83    authenticated connections by default should only fail for cells
84    which authenticate directly against the machine's home realm and
85    cells distantly related to the machine's home realm.
86    """
87    for c in config.authz.cells:
88        if c.cell == cell and not c.auth:
89            return False
90
91    remctl.checkKinit()
92    common.captureOutput(['aklog', '-c', cell])
93    return True
94
95
96def _expandGroup(name, cell=None, auth=False):
97    """Expand an AFS group into a list of its members.
98
99    Because groups are not global, but can vary from cell to cell,
100    this function accepts as an optional argument the cell in which
101    this group should be resolved.
102
103    If no cell is specified, it is assumed that the default cell (or
104    ThisCell) should be used.
105
106    If the name is a user, not a group, then a single-element set with
107    the same name is returned.
108
109    As with expandOwner, if a group doesn't exist or if we're unable
110    to retrieve its membership, we assume it's empty.
111    """
112    try:
113        ent = pts.PTS(cell, pts.PTS_ENCRYPT if auth else pts.PTS_UNAUTH).\
114            getEntry(name)
115        if ent.id > 0:
116            return set([ent.name])
117        else:
118            return set([x.name for x in ent.members])
119    except OSError, e:
120        if e.errno in (errno.ENOENT, errno.EACCESS):
121            return set()
122        else:
123            raise
124
125
126def _lockerPath(owner):
127    """Given the name of a locker, return a path to that locker.
128
129    This turns out to be pretty simple, thanks to the /mit
130    automounter.
131    """
132    return '/mit/%s' % owner
Note: See TracBrowser for help on using the repository browser.