5 SetMixin = collections.MutableSet
9 class PTRelationSet(SetMixin):
10 """Collection class for the groups/members of a PTEntry.
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.
17 _ent: The PTEntry whose groups/members this instance
19 _set: If defined, the set of either groups or members for this
22 def __init__(self, ent):
23 """Initialize a PTRelationSet class.
26 ent: The PTEntry this instance should be associated with.
28 super(PTRelationSet, self).__init__()
33 """Load the membership/groups for this instance's PTEntry.
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.
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))
44 """Add a new PTEntry to this instance's internal representation.
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.
51 elt: The element to add.
53 if hasattr(self, '_set'):
54 self._set.add(self._ent._pts.getEntry(elt))
56 def _discard(self, elt):
57 """Remove a PTEntry to this instance's internal representation.
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.
64 elt: The element to discard.
66 if hasattr(self, '_set'):
67 self._set.discard(self._ent._pts.getEntry(elt))
70 """Count the members/groups in this set.
73 The number of entities in this instance.
79 """Iterate over members/groups in this set
82 An iterator that loops over the members/groups of this
86 return iter(self._set)
88 def __contains__(self, name):
89 """Test if a PTEntry is connected to this instance.
91 If the membership of the group hasn't already been loaded,
92 this method takes advantage of the IsAMemberOf lookup to test
95 This has the convenient advantage of working even when the
96 user doens't have permission to enumerate the group's
100 name: The element whose membership is being tested.
103 True, if name is a member of self (or if self is a member
104 of name); otherwise, False
106 name = self._ent._pts.getEntry(name)
107 if hasattr(self, '_set'):
108 return name in self._set
111 return self._ent._pts._IsAMemberOf(name.id, self._ent.id)
113 return self._ent._pts._IsAMemberOf(self._ent.id, name.id)
117 return repr(self._set)
120 """Add one new entity to a group.
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.
127 TypeError: If you try to add a grop group to a group, or a
130 elt = self._ent._pts.getEntry(elt)
137 "Adding group '%s' to group '%s' is not supported." %
140 self._ent._pts._AddToGroup(elt.id, self._ent.id)
142 elt.groups._add(self._ent)
146 "Can't add user '%s' to user '%s'." %
149 self._ent._pts._AddToGroup(self._ent.id, elt.id)
151 elt.members._add(self._ent)
155 def discard(self, elt):
156 """Remove one entity from a group.
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.
162 elt = self._ent._pts.getEntry(elt)
167 self._ent._pts._RemoveFromGroup(elt.id, self._ent.id)
168 elt.groups._discard(self._ent)
170 self._ent._pts._RemoveFromGroup(self._ent.id, elt.id)
171 elt.members._discard(self._ent)
175 def remove(self, elt):
176 """Remove an entity from a group; it must already be a member.
178 If the entity is not a member, raise a KeyError.
186 class PTEntry(object):
187 """An entry in the AFS protection database.
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.
193 PTEntry instances should not be created directly. Instead, use the
194 "getEntry" method of the PTS object.
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.
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
207 ngroups: The number of additional groups this entry is allowed
209 nusers: Only meaningful for foreign-cell groups, where it
210 indicates the ID of the next entry to be created from that
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.
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.
221 _attrs = ('id', 'name', 'count', 'flags', 'ngroups', 'nusers')
222 _entry_attrs = ('owner', 'creator')
224 def __new__(cls, pts, id=None, name=None):
227 raise TypeError('Must specify either a name or an id.')
229 id = pts._NameToId(name)
231 if id not in pts._cache:
233 name = pts._IdToName(id)
235 inst = super(PTEntry, cls).__new__(cls)
240 inst.members = PTRelationSet(inst)
242 inst.groups = PTRelationSet(inst)
243 pts._cache[id] = inst
244 return pts._cache[id]
248 return '<PTEntry: %s>' % self.name
250 return '<PTEntry: PTS ID %s>' % self.id
254 def _set_id(self, val):
255 del self._pts._cache[self._id]
256 self._pts._ChangeEntry(self.id, newid=val)
258 self._pts._cache[val] = self
259 id = property(_get_id, _set_id)
263 def _set_name(self, val):
264 self._pts._ChangeEntry(self.id, newname=val)
266 name = property(_get_name, _set_name)
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)
274 def _get_count(self):
277 count = property(_get_count)
279 def _get_flags(self):
282 def _set_flags(self, val):
283 self._pts._SetFields(self.id, access=val)
285 flags = property(_get_flags, _set_flags)
287 def _get_ngroups(self):
290 def _set_ngroups(self, val):
291 self._pts._SetFields(self.id, groups=val)
293 ngroups = property(_get_ngroups, _set_ngroups)
295 def _get_nusers(self):
298 def _set_nusers(self, val):
299 self._pts._SetFields(self.id, users=val)
301 nusers = property(_get_nusers, _set_nusers)
303 def _get_owner(self):
306 def _set_owner(self, val):
307 self._pts._ChangeEntry(self.id, newoid=self._pts.getEntry(val).id)
309 owner = property(_get_owner, _set_owner)
311 def _get_creator(self):
314 creator = property(_get_creator)
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)))
332 """A connection to an AFS protection database.
334 This class represents a connection to the AFS protection database
335 for a particular cell.
337 Both the umax and gmax attributes can be changed if the connection
338 was authenticated by a principal on system:administrators for the
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.
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
352 - PTS_FORCEAUTH: fail if an authenticated connection can't be
354 - PTS_ENCRYPT: same as PTS_FORCEAUTH, plus encrypt all traffic
355 to the protection server
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
365 def __init__(self, *args, **kwargs):
369 for pte in self._ListEntries():
370 yield self.getEntry(pte.id)
372 def getEntry(self, ident):
373 """Retrieve a particular PTEntry from this cell.
375 getEntry accepts either a name or PTS ID as an argument, and
376 returns a PTEntry object with that name or ID.
378 if isinstance(ident, PTEntry):
379 if ident._pts is not self:
380 raise TypeError("Entry '%s' is from a different cell." %
384 elif isinstance(ident, basestring):
385 return PTEntry(self, name=ident)
387 return PTEntry(self, id=ident)
389 def getEntryFromKrbname(self, ident):
390 """Retrieve a PTEntry matching a given Kerberos v5 principal.
392 getEntryFromKrb accepts a krb5 principal, converts it to the
393 equivalent AFS principal, and returns a PTEntry for that
395 return self.getEntry(self._Krb5ToAfs(ident))
398 """Flush the cache of PTEntry objects.
400 This method will disconnect all PTEntry objects from this PTS
401 object and flush the cache.
403 for elt in self._cache.keys():
404 del self._cache[elt]._pts
408 return self._ListMax()[0]
409 def _set_umax(self, val):
410 self._SetMaxUserId(val)
411 umax = property(_get_umax, _set_umax)
414 return self._ListMax()[1]
415 def _set_gmax(self, val):
416 self._SetMaxGroupId(val)
417 gmax = property(_get_gmax, _set_gmax)