4 class PTRelationSet(collections.MutableSet):
5 """Collection class for the groups/members of a PTEntry.
7 This class, which acts like a set, is actually a view of the
8 groups or members associated with a PTS Entry. Changes to this
9 class are immediately reflected to the PRDB.
12 _ent: The PTEntry whose groups/members this instance
14 _set: If defined, the set of either groups or members for this
17 def __init__(self, ent):
18 """Initialize a PTRelationSet class.
21 ent: The PTEntry this instance should be associated with.
23 super(PTRelationSet, self).__init__()
28 """Load the membership/groups for this instance's PTEntry.
30 If they have not previously been loaded, this method updates
31 self._set with the set of PTEntries that are either members of
32 this group, or the groups that this entry is a member of.
34 if not hasattr(self, '_set'):
35 self._set = set(self._ent._pts.getEntry(m) for m in
36 self._ent._pts._ListMembers(self._ent.id))
39 """Add a new PTEntry to this instance's internal representation.
41 This method adds a new entry to this instance's set of
42 members/groups, but unlike PTRelationSet.add, it doesn't add
43 itself to the other instance's set.
46 elt: The element to add.
48 if hasattr(self, '_set'):
49 self._set.add(self._ent._pts.getEntry(elt))
51 def _discard(self, elt):
52 """Remove a PTEntry to this instance's internal representation.
54 This method removes an entry from this instance's set of
55 members/groups, but unlike PTRelationSet.discard, it doesn't
56 remove itself from the other instance's set.
59 elt: The element to discard.
61 if hasattr(self, '_set'):
62 self._set.discard(self._ent._pts.getEntry(elt))
65 """Count the members/groups in this set.
68 The number of entities in this instance.
74 """Iterate over members/groups in this set
77 An iterator that loops over the members/groups of this
81 return iter(self._set)
83 def __contains__(self, name):
84 """Test if a PTEntry is connected to this instance.
86 If the membership of the group hasn't already been loaded,
87 this method takes advantage of the IsAMemberOf lookup to test
90 This has the convenient advantage of working even when the
91 user doens't have permission to enumerate the group's
95 name: The element whose membership is being tested.
98 True, if name is a member of self (or if self is a member
99 of name); otherwise, False
101 name = self._ent._pts.getEntry(name)
102 if hasattr(self, '_set'):
103 return name in self._set
106 return self._ent._pts._IsAMemberOf(name.id, self._ent.id)
108 return self._ent._pts._IsAMemberOf(self._ent.id, name.id)
112 return repr(self._set)
115 """Add one new entity to a group.
117 This method will add a new user to a group, regardless of
118 whether this instance represents a group or a user. The change
119 is also immediately reflected to the PRDB.
122 TypeError: If you try to add a grop group to a group, or a
125 elt = self._ent._pts.getEntry(elt)
132 "Adding group '%s' to group '%s' is not supported." %
135 self._ent._pts._AddToGroup(elt.id, self._ent.id)
137 elt.groups._add(self._ent)
141 "Can't add user '%s' to user '%s'." %
144 self._ent._pts._AddToGroup(self._ent.id, elt.id)
146 elt.members._add(self._ent)
150 def discard(self, elt):
151 """Remove one entity from a group.
153 This method will remove a user from a group, regardless of
154 whether this instance represents a group or a user. The change
155 is also immediately reflected to the PRDB.
157 elt = self._ent._pts.getEntry(elt)
162 self._ent._pts._RemoveFromGroup(elt.id, self._ent.id)
163 elt.groups._discard(self._ent)
165 self._ent._pts._RemoveFromGroup(self._ent.id, elt.id)
166 elt.members._discard(self._ent)
171 class PTEntry(object):
172 """An entry in the AFS protection database.
174 PTEntry represents a user or group in the AFS protection
175 database. Each PTEntry is associated with a particular connection
176 to the protection database.
178 PTEntry instances should not be created directly. Instead, use the
179 "getEntry" method of the PTS object.
181 If a PTS connection is authenticated, it should be possible to
182 change most attributes on a PTEntry. These changes are immediately
183 propogated to the protection database.
186 id: The PTS ID of the entry
187 name: The username or group name of the entry
188 count: For users, the number of groups they are a member of; for
189 groups, the number of users in that group
190 flags: An integer representation of the flags set on a given
192 ngroups: The number of additional groups this entry is allowed
194 nusers: Only meaningful for foreign-cell groups, where it
195 indicates the ID of the next entry to be created from that
197 owner: A PTEntry object representing the owner of a given entry.
198 creator: A PTEntry object representing the creator of a given
199 entry. This field is read-only.
201 groups: For users, this contains a collection class representing
202 the set of groups the user is a member of.
203 users: For groups, this contains a collection class representing
204 the members of this group.
206 _attrs = ('id', 'name', 'count', 'flags', 'ngroups', 'nusers')
207 _entry_attrs = ('owner', 'creator')
209 def __new__(cls, pts, id=None, name=None):
212 raise TypeError('Must specify either a name or an id.')
214 id = pts._NameToId(name)
216 if id not in pts._cache:
218 name = pts._IdToName(id)
220 inst = super(PTEntry, cls).__new__(cls)
225 inst.members = PTRelationSet(inst)
227 inst.groups = PTRelationSet(inst)
228 pts._cache[id] = inst
229 return pts._cache[id]
233 return '<PTEntry: %s>' % self.name
235 return '<PTEntry: PTS ID %s>' % self.id
239 def _set_id(self, val):
240 del self._pts._cache[self._id]
241 self._pts._ChangeEntry(self.id, newid=val)
243 self._pts._cache[val] = self
244 id = property(_get_id, _set_id)
248 def _set_name(self, val):
249 self._pts._ChangeEntry(self.id, newname=val)
251 name = property(_get_name, _set_name)
253 def _get_krbname(self):
254 return self._pts._AfsToKrb5(self.name)
255 def _set_krbname(self, val):
256 self.name = self._pts._Krb5ToAfs(val)
257 krbname = property(_get_krbname, _set_krbname)
259 def _get_count(self):
262 count = property(_get_count)
264 def _get_flags(self):
267 def _set_flags(self, val):
268 self._pts._SetFields(self.id, access=val)
270 flags = property(_get_flags, _set_flags)
272 def _get_ngroups(self):
275 def _set_ngroups(self, val):
276 self._pts._SetFields(self.id, groups=val)
278 ngroups = property(_get_ngroups, _set_ngroups)
280 def _get_nusers(self):
283 def _set_nusers(self, val):
284 self._pts._SetFields(self.id, users=val)
286 nusers = property(_get_nusers, _set_nusers)
288 def _get_owner(self):
291 def _set_owner(self, val):
292 self._pts._ChangeEntry(self.id, newoid=self._pts.getEntry(val).id)
294 owner = property(_get_owner, _set_owner)
296 def _get_creator(self):
299 creator = property(_get_creator)
301 def _loadEntry(self):
302 if not hasattr(self, '_flags'):
303 info = self._pts._ListEntry(self._id)
304 for field in self._attrs:
305 setattr(self, '_%s' % field, getattr(info, field))
306 for field in self._entry_attrs:
307 setattr(self, '_%s' % field, self._pts.getEntry(getattr(info, field)))
310 """A connection to an AFS protection database.
312 This class represents a connection to the AFS protection database
313 for a particular cell.
315 Both the umax and gmax attributes can be changed if the connection
316 was authenticated by a principal on system:administrators for the
319 For sufficiently privileged and authenticated connections,
320 iterating over a PTS object will yield all entries in the
321 protection database, in no particular order.
324 cell: The cell to connect to. If None (the default), PTS
325 connects to the workstations home cell.
326 sec: The security level to connect with, an integer from 0 to 3:
327 - 0: unauthenticated connection
328 - 1: try authenticated, then fall back to unauthenticated
329 - 2: fail if an authenticated connection can't be established
330 - 3: same as 2, plus encrypt all traffic to the protection
334 realm: The Kerberos realm against which this cell authenticates
335 umax: The maximum user ID currently assigned (the next ID
336 assigned will be umax + 1)
337 gmax: The maximum (actually minimum) group ID currently assigned
338 (the next ID assigned will be gmax - 1, since group IDs are
341 def __init__(self, *args, **kwargs):
345 for pte in self._ListEntries():
346 yield self.getEntry(pte.id)
348 def getEntry(self, ident):
349 """Retrieve a particular PTEntry from this cell.
351 getEntry accepts either a name or PTS ID as an argument, and
352 returns a PTEntry object with that name or ID.
354 if isinstance(ident, PTEntry):
355 if ident._pts is not self:
356 raise TypeError("Entry '%s' is from a different cell." %
360 elif isinstance(ident, basestring):
361 return PTEntry(self, name=ident)
363 return PTEntry(self, id=ident)
365 def getEntryFromKrbname(self, ident):
366 """Retrieve a PTEntry matching a given Kerberos v5 principal.
368 getEntryFromKrb accepts a krb5 principal, converts it to the
369 equivalent AFS principal, and returns a PTEntry for that
371 return self.getEntry(self._Krb5ToAfs(ident))
374 """Flush the cache of PTEntry objects.
376 This method will disconnect all PTEntry objects from this PTS
377 object and flush the cache.
379 for elt in self._cache.keys():
380 del self._cache[elt]._pts
384 return self._ListMax()[0]
385 def _set_umax(self, val):
386 self._SetMaxUserId(val)
387 umax = property(_get_umax, _set_umax)
390 return self._ListMax()[1]
391 def _set_gmax(self, val):
392 self._SetMaxGroupId(val)
393 gmax = property(_get_gmax, _set_gmax)