| [2599] | 1 | import collections | 
|---|
 | 2 | from afs import _pts | 
|---|
 | 3 |  | 
|---|
 | 4 | try: | 
|---|
 | 5 |     SetMixin = collections.MutableSet | 
|---|
 | 6 | except AttributeError: | 
|---|
 | 7 |     SetMixin = object | 
|---|
 | 8 |  | 
|---|
 | 9 | class PTRelationSet(SetMixin): | 
|---|
 | 10 |     """Collection class for the groups/members of a PTEntry. | 
|---|
 | 11 |  | 
|---|
 | 12 |     This class, which acts like a set, is actually a view of the | 
|---|
 | 13 |     groups or members associated with a PTS Entry. Changes to this | 
|---|
 | 14 |     class are immediately reflected to the PRDB. | 
|---|
 | 15 |  | 
|---|
 | 16 |     Attributes: | 
|---|
 | 17 |         _ent: The PTEntry whose groups/members this instance | 
|---|
 | 18 |             represents | 
|---|
 | 19 |         _set: If defined, the set of either groups or members for this | 
|---|
 | 20 |             instance's PTEntry | 
|---|
 | 21 |     """ | 
|---|
 | 22 |     def __init__(self, ent): | 
|---|
 | 23 |         """Initialize a PTRelationSet class. | 
|---|
 | 24 |  | 
|---|
 | 25 |         Args: | 
|---|
 | 26 |             ent: The PTEntry this instance should be associated with. | 
|---|
 | 27 |         """ | 
|---|
 | 28 |         super(PTRelationSet, self).__init__() | 
|---|
 | 29 |  | 
|---|
 | 30 |         self._ent = ent | 
|---|
 | 31 |  | 
|---|
 | 32 |     def _loadSet(self): | 
|---|
 | 33 |         """Load the membership/groups for this instance's PTEntry. | 
|---|
 | 34 |  | 
|---|
 | 35 |         If they have not previously been loaded, this method updates | 
|---|
 | 36 |         self._set with the set of PTEntries that are either members of | 
|---|
 | 37 |         this group, or the groups that this entry is a member of. | 
|---|
 | 38 |         """ | 
|---|
 | 39 |         if not hasattr(self, '_set'): | 
|---|
 | 40 |             self._set = set(self._ent._pts.getEntry(m) for m in | 
|---|
 | 41 |                             self._ent._pts._ListMembers(self._ent.id)) | 
|---|
 | 42 |  | 
|---|
 | 43 |     def _add(self, elt): | 
|---|
 | 44 |         """Add a new PTEntry to this instance's internal representation. | 
|---|
 | 45 |  | 
|---|
 | 46 |         This method adds a new entry to this instance's set of | 
|---|
 | 47 |         members/groups, but unlike PTRelationSet.add, it doesn't add | 
|---|
 | 48 |         itself to the other instance's set. | 
|---|
 | 49 |  | 
|---|
 | 50 |         Args: | 
|---|
 | 51 |             elt: The element to add. | 
|---|
 | 52 |         """ | 
|---|
 | 53 |         if hasattr(self, '_set'): | 
|---|
 | 54 |             self._set.add(self._ent._pts.getEntry(elt)) | 
|---|
 | 55 |  | 
|---|
 | 56 |     def _discard(self, elt): | 
|---|
 | 57 |         """Remove a PTEntry to this instance's internal representation. | 
|---|
 | 58 |  | 
|---|
 | 59 |         This method removes an entry from this instance's set of | 
|---|
 | 60 |         members/groups, but unlike PTRelationSet.discard, it doesn't | 
|---|
 | 61 |         remove itself from the other instance's set. | 
|---|
 | 62 |  | 
|---|
 | 63 |         Args: | 
|---|
 | 64 |             elt: The element to discard. | 
|---|
 | 65 |         """ | 
|---|
 | 66 |         if hasattr(self, '_set'): | 
|---|
 | 67 |             self._set.discard(self._ent._pts.getEntry(elt)) | 
|---|
 | 68 |  | 
|---|
 | 69 |     def __len__(self): | 
|---|
 | 70 |         """Count the members/groups in this set. | 
|---|
 | 71 |  | 
|---|
 | 72 |         Returns: | 
|---|
 | 73 |             The number of entities in this instance. | 
|---|
 | 74 |         """ | 
|---|
 | 75 |         self._loadSet() | 
|---|
 | 76 |         return len(self._set) | 
|---|
 | 77 |  | 
|---|
 | 78 |     def __iter__(self): | 
|---|
 | 79 |         """Iterate over members/groups in this set | 
|---|
 | 80 |  | 
|---|
 | 81 |         Returns: | 
|---|
 | 82 |             An iterator that loops over the members/groups of this | 
|---|
 | 83 |                 set. | 
|---|
 | 84 |         """ | 
|---|
 | 85 |         self._loadSet() | 
|---|
 | 86 |         return iter(self._set) | 
|---|
 | 87 |  | 
|---|
 | 88 |     def __contains__(self, name): | 
|---|
 | 89 |         """Test if a PTEntry is connected to this instance. | 
|---|
 | 90 |  | 
|---|
 | 91 |         If the membership of the group hasn't already been loaded, | 
|---|
 | 92 |         this method takes advantage of the IsAMemberOf lookup to test | 
|---|
 | 93 |         for membership. | 
|---|
 | 94 |  | 
|---|
 | 95 |         This has the convenient advantage of working even when the | 
|---|
 | 96 |         user doens't have permission to enumerate the group's | 
|---|
 | 97 |         membership. | 
|---|
 | 98 |  | 
|---|
 | 99 |         Args: | 
|---|
 | 100 |             name: The element whose membership is being tested. | 
|---|
 | 101 |  | 
|---|
 | 102 |         Returns: | 
|---|
 | 103 |             True, if name is a member of self (or if self is a member | 
|---|
 | 104 |                 of name); otherwise, False | 
|---|
 | 105 |         """ | 
|---|
 | 106 |         name = self._ent._pts.getEntry(name) | 
|---|
 | 107 |         if hasattr(self, '_set'): | 
|---|
 | 108 |             return name in self._set | 
|---|
 | 109 |         else: | 
|---|
 | 110 |             if self._ent.id < 0: | 
|---|
 | 111 |                 return self._ent._pts._IsAMemberOf(name.id, self._ent.id) | 
|---|
 | 112 |             else: | 
|---|
 | 113 |                 return self._ent._pts._IsAMemberOf(self._ent.id, name.id) | 
|---|
 | 114 |  | 
|---|
 | 115 |     def __repr__(self): | 
|---|
 | 116 |         self._loadSet() | 
|---|
 | 117 |         return repr(self._set) | 
|---|
 | 118 |  | 
|---|
 | 119 |     def add(self, elt): | 
|---|
 | 120 |         """Add one new entity to a group. | 
|---|
 | 121 |  | 
|---|
 | 122 |         This method will add a new user to a group, regardless of | 
|---|
 | 123 |         whether this instance represents a group or a user. The change | 
|---|
 | 124 |         is also immediately reflected to the PRDB. | 
|---|
 | 125 |  | 
|---|
 | 126 |         Raises: | 
|---|
 | 127 |             TypeError: If you try to add a grop group to a group, or a | 
|---|
 | 128 |                 user to a user | 
|---|
 | 129 |         """ | 
|---|
 | 130 |         elt = self._ent._pts.getEntry(elt) | 
|---|
 | 131 |         if elt in self: | 
|---|
 | 132 |             return | 
|---|
 | 133 |  | 
|---|
 | 134 |         if self._ent.id < 0: | 
|---|
 | 135 |             if elt.id < 0: | 
|---|
 | 136 |                 raise TypeError( | 
|---|
 | 137 |                     "Adding group '%s' to group '%s' is not supported." % | 
|---|
 | 138 |                     (elt, self._ent)) | 
|---|
 | 139 |  | 
|---|
 | 140 |             self._ent._pts._AddToGroup(elt.id, self._ent.id) | 
|---|
 | 141 |  | 
|---|
 | 142 |             elt.groups._add(self._ent) | 
|---|
 | 143 |         else: | 
|---|
 | 144 |             if elt.id > 0: | 
|---|
 | 145 |                 raise TypeError( | 
|---|
 | 146 |                     "Can't add user '%s' to user '%s'." % | 
|---|
 | 147 |                     (elt, self._ent)) | 
|---|
 | 148 |  | 
|---|
 | 149 |             self._ent._pts._AddToGroup(self._ent.id, elt.id) | 
|---|
 | 150 |  | 
|---|
 | 151 |             elt.members._add(self._ent) | 
|---|
 | 152 |  | 
|---|
 | 153 |         self._add(elt) | 
|---|
 | 154 |  | 
|---|
 | 155 |     def discard(self, elt): | 
|---|
 | 156 |         """Remove one entity from a group. | 
|---|
 | 157 |  | 
|---|
 | 158 |         This method will remove a user from a group, regardless of | 
|---|
 | 159 |         whether this instance represents a group or a user. The change | 
|---|
 | 160 |         is also immediately reflected to the PRDB. | 
|---|
 | 161 |         """ | 
|---|
 | 162 |         elt = self._ent._pts.getEntry(elt) | 
|---|
 | 163 |         if elt not in self: | 
|---|
 | 164 |             return | 
|---|
 | 165 |  | 
|---|
 | 166 |         if self._ent.id < 0: | 
|---|
 | 167 |             self._ent._pts._RemoveFromGroup(elt.id, self._ent.id) | 
|---|
 | 168 |             elt.groups._discard(self._ent) | 
|---|
 | 169 |         else: | 
|---|
 | 170 |             self._ent._pts._RemoveFromGroup(self._ent.id, elt.id) | 
|---|
 | 171 |             elt.members._discard(self._ent) | 
|---|
 | 172 |  | 
|---|
 | 173 |         self._discard(elt) | 
|---|
 | 174 |  | 
|---|
 | 175 |     def remove(self, elt): | 
|---|
 | 176 |         """Remove an entity from a group; it must already be a member. | 
|---|
 | 177 |  | 
|---|
 | 178 |         If the entity is not a member, raise a KeyError. | 
|---|
 | 179 |         """ | 
|---|
 | 180 |         if elt not in self: | 
|---|
 | 181 |             raise KeyError(elt) | 
|---|
 | 182 |  | 
|---|
 | 183 |         self.discard(elt) | 
|---|
 | 184 |  | 
|---|
 | 185 |  | 
|---|
 | 186 | class PTEntry(object): | 
|---|
 | 187 |     """An entry in the AFS protection database. | 
|---|
 | 188 |  | 
|---|
 | 189 |     PTEntry represents a user or group in the AFS protection | 
|---|
 | 190 |     database. Each PTEntry is associated with a particular connection | 
|---|
 | 191 |     to the protection database. | 
|---|
 | 192 |  | 
|---|
 | 193 |     PTEntry instances should not be created directly. Instead, use the | 
|---|
 | 194 |     "getEntry" method of the PTS object. | 
|---|
 | 195 |  | 
|---|
 | 196 |     If a PTS connection is authenticated, it should be possible to | 
|---|
 | 197 |     change most attributes on a PTEntry. These changes are immediately | 
|---|
 | 198 |     propogated to the protection database. | 
|---|
 | 199 |  | 
|---|
 | 200 |     Attributes: | 
|---|
 | 201 |       id: The PTS ID of the entry | 
|---|
 | 202 |       name: The username or group name of the entry | 
|---|
 | 203 |       count: For users, the number of groups they are a member of; for | 
|---|
 | 204 |         groups, the number of users in that group | 
|---|
 | 205 |       flags: An integer representation of the flags set on a given | 
|---|
 | 206 |         entry | 
|---|
 | 207 |       ngroups: The number of additional groups this entry is allowed | 
|---|
 | 208 |         to create | 
|---|
 | 209 |       nusers: Only meaningful for foreign-cell groups, where it | 
|---|
 | 210 |         indicates the ID of the next entry to be created from that | 
|---|
 | 211 |         cell. | 
|---|
 | 212 |       owner: A PTEntry object representing the owner of a given entry. | 
|---|
 | 213 |       creator: A PTEntry object representing the creator of a given | 
|---|
 | 214 |         entry. This field is read-only. | 
|---|
 | 215 |  | 
|---|
 | 216 |       groups: For users, this contains a collection class representing | 
|---|
 | 217 |         the set of groups the user is a member of. | 
|---|
 | 218 |       users: For groups, this contains a collection class representing | 
|---|
 | 219 |         the members of this group. | 
|---|
 | 220 |     """ | 
|---|
 | 221 |     _attrs = ('id', 'name', 'count', 'flags', 'ngroups', 'nusers') | 
|---|
 | 222 |     _entry_attrs = ('owner', 'creator') | 
|---|
 | 223 |  | 
|---|
 | 224 |     def __new__(cls, pts, id=None, name=None): | 
|---|
 | 225 |         if id is None: | 
|---|
 | 226 |             if name is None: | 
|---|
 | 227 |                 raise TypeError('Must specify either a name or an id.') | 
|---|
 | 228 |             else: | 
|---|
 | 229 |                 id = pts._NameToId(name) | 
|---|
 | 230 |  | 
|---|
 | 231 |         if id not in pts._cache: | 
|---|
 | 232 |             if name is None: | 
|---|
 | 233 |                 name = pts._IdToName(id) | 
|---|
 | 234 |  | 
|---|
 | 235 |             inst = super(PTEntry, cls).__new__(cls) | 
|---|
 | 236 |             inst._pts = pts | 
|---|
 | 237 |             inst._id = id | 
|---|
 | 238 |             inst._name = name | 
|---|
 | 239 |             if id < 0: | 
|---|
 | 240 |                 inst.members = PTRelationSet(inst) | 
|---|
 | 241 |             else: | 
|---|
 | 242 |                 inst.groups = PTRelationSet(inst) | 
|---|
 | 243 |             pts._cache[id] = inst | 
|---|
 | 244 |         return pts._cache[id] | 
|---|
 | 245 |  | 
|---|
 | 246 |     def __repr__(self): | 
|---|
 | 247 |         if self.name != '': | 
|---|
 | 248 |             return '<PTEntry: %s>' % self.name | 
|---|
 | 249 |         else: | 
|---|
 | 250 |             return '<PTEntry: PTS ID %s>' % self.id | 
|---|
 | 251 |  | 
|---|
 | 252 |     def _get_id(self): | 
|---|
 | 253 |         return self._id | 
|---|
 | 254 |     def _set_id(self, val): | 
|---|
 | 255 |         del self._pts._cache[self._id] | 
|---|
 | 256 |         self._pts._ChangeEntry(self.id, newid=val) | 
|---|
 | 257 |         self._id = val | 
|---|
 | 258 |         self._pts._cache[val] = self | 
|---|
 | 259 |     id = property(_get_id, _set_id) | 
|---|
 | 260 |  | 
|---|
 | 261 |     def _get_name(self): | 
|---|
 | 262 |         return self._name | 
|---|
 | 263 |     def _set_name(self, val): | 
|---|
 | 264 |         self._pts._ChangeEntry(self.id, newname=val) | 
|---|
 | 265 |         self._name = val | 
|---|
 | 266 |     name = property(_get_name, _set_name) | 
|---|
 | 267 |  | 
|---|
 | 268 |     def _get_krbname(self): | 
|---|
 | 269 |         return self._pts._AfsToKrb5(self.name) | 
|---|
 | 270 |     def _set_krbname(self, val): | 
|---|
 | 271 |         self.name = self._pts._Krb5ToAfs(val) | 
|---|
 | 272 |     krbname = property(_get_krbname, _set_krbname) | 
|---|
 | 273 |  | 
|---|
 | 274 |     def _get_count(self): | 
|---|
 | 275 |         self._loadEntry() | 
|---|
 | 276 |         return self._count | 
|---|
 | 277 |     count = property(_get_count) | 
|---|
 | 278 |  | 
|---|
 | 279 |     def _get_flags(self): | 
|---|
 | 280 |         self._loadEntry() | 
|---|
 | 281 |         return self._flags | 
|---|
 | 282 |     def _set_flags(self, val): | 
|---|
 | 283 |         self._pts._SetFields(self.id, access=val) | 
|---|
 | 284 |         self._flags = val | 
|---|
 | 285 |     flags = property(_get_flags, _set_flags) | 
|---|
 | 286 |  | 
|---|
 | 287 |     def _get_ngroups(self): | 
|---|
 | 288 |         self._loadEntry() | 
|---|
 | 289 |         return self._ngroups | 
|---|
 | 290 |     def _set_ngroups(self, val): | 
|---|
 | 291 |         self._pts._SetFields(self.id, groups=val) | 
|---|
 | 292 |         self._ngroups = val | 
|---|
 | 293 |     ngroups = property(_get_ngroups, _set_ngroups) | 
|---|
 | 294 |  | 
|---|
 | 295 |     def _get_nusers(self): | 
|---|
 | 296 |         self._loadEntry() | 
|---|
 | 297 |         return self._nusers | 
|---|
 | 298 |     def _set_nusers(self, val): | 
|---|
 | 299 |         self._pts._SetFields(self.id, users=val) | 
|---|
 | 300 |         self._nusers = val | 
|---|
 | 301 |     nusers = property(_get_nusers, _set_nusers) | 
|---|
 | 302 |  | 
|---|
 | 303 |     def _get_owner(self): | 
|---|
 | 304 |         self._loadEntry() | 
|---|
 | 305 |         return self._owner | 
|---|
 | 306 |     def _set_owner(self, val): | 
|---|
 | 307 |         self._pts._ChangeEntry(self.id, newoid=self._pts.getEntry(val).id) | 
|---|
 | 308 |         self._owner = val | 
|---|
 | 309 |     owner = property(_get_owner, _set_owner) | 
|---|
 | 310 |  | 
|---|
 | 311 |     def _get_creator(self): | 
|---|
 | 312 |         self._loadEntry() | 
|---|
 | 313 |         return self._creator | 
|---|
 | 314 |     creator = property(_get_creator) | 
|---|
 | 315 |  | 
|---|
 | 316 |     def _loadEntry(self): | 
|---|
 | 317 |         if not hasattr(self, '_flags'): | 
|---|
 | 318 |             info = self._pts._ListEntry(self._id) | 
|---|
 | 319 |             for field in self._attrs: | 
|---|
 | 320 |                 setattr(self, '_%s' % field, getattr(info, field)) | 
|---|
 | 321 |             for field in self._entry_attrs: | 
|---|
 | 322 |                 setattr(self, '_%s' % field, self._pts.getEntry(getattr(info, field))) | 
|---|
 | 323 |  | 
|---|
 | 324 |  | 
|---|
 | 325 | PTS_UNAUTH = 0 | 
|---|
 | 326 | PTS_AUTH = 1 | 
|---|
 | 327 | PTS_FORCEAUTH = 2 | 
|---|
 | 328 | PTS_ENCRYPT = 3 | 
|---|
 | 329 |  | 
|---|
 | 330 |  | 
|---|
 | 331 | class PTS(_pts.PTS): | 
|---|
 | 332 |     """A connection to an AFS protection database. | 
|---|
 | 333 |  | 
|---|
 | 334 |     This class represents a connection to the AFS protection database | 
|---|
 | 335 |     for a particular cell. | 
|---|
 | 336 |  | 
|---|
 | 337 |     Both the umax and gmax attributes can be changed if the connection | 
|---|
 | 338 |     was authenticated by a principal on system:administrators for the | 
|---|
 | 339 |     cell. | 
|---|
 | 340 |  | 
|---|
 | 341 |     For sufficiently privileged and authenticated connections, | 
|---|
 | 342 |     iterating over a PTS object will yield all entries in the | 
|---|
 | 343 |     protection database, in no particular order. | 
|---|
 | 344 |  | 
|---|
 | 345 |     Args: | 
|---|
 | 346 |       cell: The cell to connect to. If None (the default), PTS | 
|---|
 | 347 |         connects to the workstations home cell. | 
|---|
 | 348 |       sec: The security level to connect with: | 
|---|
 | 349 |         - PTS_UNAUTH: unauthenticated connection | 
|---|
 | 350 |         - PTS_AUTH: try authenticated, then fall back to | 
|---|
 | 351 |           unauthenticated | 
|---|
 | 352 |         - PTS_FORCEAUTH: fail if an authenticated connection can't be | 
|---|
 | 353 |           established | 
|---|
 | 354 |         - PTS_ENCRYPT: same as PTS_FORCEAUTH, plus encrypt all traffic | 
|---|
 | 355 |           to the protection server | 
|---|
 | 356 |  | 
|---|
 | 357 |     Attributes: | 
|---|
 | 358 |       realm: The Kerberos realm against which this cell authenticates | 
|---|
 | 359 |       umax: The maximum user ID currently assigned (the next ID | 
|---|
 | 360 |         assigned will be umax + 1) | 
|---|
 | 361 |       gmax: The maximum (actually minimum) group ID currently assigned | 
|---|
 | 362 |         (the next ID assigned will be gmax - 1, since group IDs are | 
|---|
 | 363 |         negative) | 
|---|
 | 364 |     """ | 
|---|
 | 365 |     def __init__(self, *args, **kwargs): | 
|---|
 | 366 |         self._cache = {} | 
|---|
 | 367 |  | 
|---|
 | 368 |     def __iter__(self): | 
|---|
 | 369 |         for pte in self._ListEntries(): | 
|---|
 | 370 |             yield self.getEntry(pte.id) | 
|---|
 | 371 |  | 
|---|
 | 372 |     def getEntry(self, ident): | 
|---|
 | 373 |         """Retrieve a particular PTEntry from this cell. | 
|---|
 | 374 |  | 
|---|
 | 375 |         getEntry accepts either a name or PTS ID as an argument, and | 
|---|
 | 376 |         returns a PTEntry object with that name or ID. | 
|---|
 | 377 |         """ | 
|---|
 | 378 |         if isinstance(ident, PTEntry): | 
|---|
 | 379 |             if ident._pts is not self: | 
|---|
 | 380 |                 raise TypeError("Entry '%s' is from a different cell." % | 
|---|
 | 381 |                                 elt) | 
|---|
 | 382 |             return ident | 
|---|
 | 383 |  | 
|---|
 | 384 |         elif isinstance(ident, basestring): | 
|---|
 | 385 |             return PTEntry(self, name=ident) | 
|---|
 | 386 |         else: | 
|---|
 | 387 |             return PTEntry(self, id=ident) | 
|---|
 | 388 |  | 
|---|
 | 389 |     def getEntryFromKrbname(self, ident): | 
|---|
 | 390 |         """Retrieve a PTEntry matching a given Kerberos v5 principal. | 
|---|
 | 391 |  | 
|---|
 | 392 |         getEntryFromKrb accepts a krb5 principal, converts it to the | 
|---|
 | 393 |         equivalent AFS principal, and returns a PTEntry for that | 
|---|
 | 394 |         principal.""" | 
|---|
 | 395 |         return self.getEntry(self._Krb5ToAfs(ident)) | 
|---|
 | 396 |  | 
|---|
 | 397 |     def expire(self): | 
|---|
 | 398 |         """Flush the cache of PTEntry objects. | 
|---|
 | 399 |  | 
|---|
 | 400 |         This method will disconnect all PTEntry objects from this PTS | 
|---|
 | 401 |         object and flush the cache. | 
|---|
 | 402 |         """ | 
|---|
 | 403 |         for elt in self._cache.keys(): | 
|---|
 | 404 |             del self._cache[elt]._pts | 
|---|
 | 405 |             del self._cache[elt] | 
|---|
 | 406 |  | 
|---|
 | 407 |     def _get_umax(self): | 
|---|
 | 408 |         return self._ListMax()[0] | 
|---|
 | 409 |     def _set_umax(self, val): | 
|---|
 | 410 |         self._SetMaxUserId(val) | 
|---|
 | 411 |     umax = property(_get_umax, _set_umax) | 
|---|
 | 412 |  | 
|---|
 | 413 |     def _get_gmax(self): | 
|---|
 | 414 |         return self._ListMax()[1] | 
|---|
 | 415 |     def _set_gmax(self, val): | 
|---|
 | 416 |         self._SetMaxGroupId(val) | 
|---|
 | 417 |     gmax = property(_get_gmax, _set_gmax) | 
|---|