Ignore the "over" argument used in _ListMembers and _ListOwned.
[invirt/packages/python-afs.git] / afs / _pts.pyx
1 from afs cimport *
2 from afs import pyafs_error
3
4 cdef import from "afs/ptuser.h":
5     enum:
6         PR_MAXNAMELEN
7         PRGRP
8         PRUSERS
9         PRGROUPS
10         ANONYMOUSID
11         PR_SF_ALLBITS
12         PR_SF_NGROUPS
13         PR_SF_NUSERS
14
15     ctypedef char prname[PR_MAXNAMELEN]
16
17     struct namelist:
18         unsigned int namelist_len
19         prname *namelist_val
20
21     struct prlist:
22         unsigned int prlist_len
23         afs_int32 *prlist_val
24
25     struct idlist:
26         unsigned int idlist_len
27         afs_int32 *idlist_val
28
29     struct prcheckentry:
30         afs_int32 flags
31         afs_int32 id
32         afs_int32 owner
33         afs_int32 creator
34         afs_int32 ngroups
35         afs_int32 nusers
36         afs_int32 count
37         char name[PR_MAXNAMELEN]
38
39     struct prlistentries:
40         afs_int32 flags
41         afs_int32 id
42         afs_int32 owner
43         afs_int32 creator
44         afs_int32 ngroups
45         afs_int32 nusers
46         afs_int32 count
47         char name[PR_MAXNAMELEN]
48
49     struct prentries:
50         unsigned int prentries_len
51         prlistentries *prentries_val
52
53     int ubik_PR_NameToID(ubik_client *, afs_int32, namelist *, idlist *)
54     int ubik_PR_IDToName(ubik_client *, afs_int32, idlist *, namelist *)
55     int ubik_PR_INewEntry(ubik_client *, afs_int32, char *, afs_int32, afs_int32)
56     int ubik_PR_NewEntry(ubik_client *, afs_int32, char *, afs_int32, afs_int32, afs_int32 *)
57     int ubik_PR_Delete(ubik_client *, afs_int32, afs_int32)
58     int ubik_PR_AddToGroup(ubik_client *, afs_int32, afs_int32, afs_int32)
59     int ubik_PR_RemoveFromGroup(ubik_client *, afs_int32, afs_int32, afs_int32)
60     int ubik_PR_ListElements(ubik_client *, afs_int32, afs_int32, prlist *, afs_int32 *)
61     int ubik_PR_ListOwned(ubik_client *, afs_int32, afs_int32, prlist *, afs_int32 *)
62     int ubik_PR_ListEntry(ubik_client *, afs_int32, afs_int32, prcheckentry *)
63     int ubik_PR_ChangeEntry(ubik_client *, afs_int32, afs_int32, char *, afs_int32, afs_int32)
64     int ubik_PR_IsAMemberOf(ubik_client *, afs_int32, afs_int32, afs_int32, afs_int32 *)
65     int ubik_PR_ListMax(ubik_client *, afs_int32, afs_int32 *, afs_int32 *)
66     int ubik_PR_SetMax(ubik_client *, afs_int32, afs_int32, afs_int32)
67     int ubik_PR_ListEntries(ubik_client *, afs_int32, afs_int32, afs_int32, prentries *, afs_int32 *)
68     int ubik_PR_SetFieldsEntry(ubik_client *, afs_int32, afs_int32, afs_int32, afs_int32, afs_int32, afs_int32, afs_int32, afs_int32)
69
70 cdef import from "afs/pterror.h":
71     enum:
72         PRNOENT
73
74 cdef class PTEntry:
75     cdef public afs_int32 flags
76     cdef public afs_int32 id
77     cdef public afs_int32 owner
78     cdef public afs_int32 creator
79     cdef public afs_int32 ngroups
80     cdef public afs_int32 nusers
81     cdef public afs_int32 count
82     cdef public object name
83
84     def __repr__(self):
85         if self.name != '':
86             return '<PTEntry: %s>' % self.name
87         else:
88             return '<PTEntry: PTS ID %s>' % self.id
89
90 cdef int _ptentry_from_c(PTEntry p_entry, prcheckentry * c_entry) except -1:
91     if p_entry is None:
92         raise TypeError
93         return -1
94
95     p_entry.flags = c_entry.flags
96     p_entry.id = c_entry.id
97     p_entry.owner = c_entry.owner
98     p_entry.creator = c_entry.creator
99     p_entry.ngroups = c_entry.ngroups
100     p_entry.nusers = c_entry.nusers
101     p_entry.count = c_entry.count
102     p_entry.name = c_entry.name
103     return 0
104
105 cdef int _ptentry_to_c(prcheckentry * c_entry, PTEntry p_entry) except -1:
106     if p_entry is None:
107         raise TypeError
108         return -1
109
110     c_entry.flags = p_entry.flags
111     c_entry.id = p_entry.id
112     c_entry.owner = p_entry.owner
113     c_entry.creator = p_entry.creator
114     c_entry.ngroups = p_entry.ngroups
115     c_entry.nusers = p_entry.nusers
116     c_entry.count = p_entry.count
117     strncpy(c_entry.name, p_entry.name, sizeof(c_entry.name))
118     return 0
119
120 cdef class PTS:
121     """
122     A PTS object is essentially a handle to talk to the server in a
123     given cell.
124
125     cell defaults to None. If no argument is passed for cell, PTS
126     connects to the home cell.
127
128     sec is the security level, an integer from 0 to 3:
129       - 0: unauthenticated connection
130       - 1: try authenticated, then fall back to unauthenticated
131       - 2: fail if an authenticated connection can't be established
132       - 3: same as 2, plus encrypt all traffic to the protection
133         server
134     """
135     cdef ubik_client * client
136     cdef readonly object cell
137
138     def __cinit__(self, cell=None, sec=1):
139         cdef afs_int32 code
140         cdef afsconf_dir *cdir
141         cdef afsconf_cell info
142         cdef char * c_cell
143         cdef ktc_principal prin
144         cdef ktc_token token
145         cdef rx_securityClass *sc
146         cdef rx_connection *serverconns[MAXSERVERS]
147         cdef int i
148
149         initialize_PT_error_table()
150
151         if cell is None:
152             c_cell = NULL
153         else:
154             c_cell = cell
155
156         self.client = NULL
157
158         code = rx_Init(0)
159         if code != 0:
160             raise Exception(code, "Error initializing Rx")
161
162         cdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH)
163         if cdir is NULL:
164             raise OSError(errno,
165                           "Error opening configuration directory (%s): %s" % \
166                               (AFSDIR_CLIENT_ETC_DIRPATH, strerror(errno)))
167         code = afsconf_GetCellInfo(cdir, c_cell, "afsprot", &info)
168         pyafs_error(code)
169
170         self.cell = info.name
171
172         if sec > 0:
173             strncpy(prin.cell, info.name, sizeof(prin.cell))
174             prin.instance[0] = 0
175             strncpy(prin.name, "afs", sizeof(prin.name))
176
177             code = ktc_GetToken(&prin, &token, sizeof(token), NULL);
178             if code != 0:
179                 if sec >= 2:
180                     # No really - we wanted authentication
181                     pyafs_error(code)
182                 sec = 0
183             else:
184                 if sec == 3:
185                     level = rxkad_crypt
186                 else:
187                     level = rxkad_clear
188                 sc = rxkad_NewClientSecurityObject(level, &token.sessionKey,
189                                                    token.kvno, token.ticketLen,
190                                                    token.ticket)
191
192         if sec == 0:
193             sc = rxnull_NewClientSecurityObject()
194         else:
195             sec = 2
196
197         memset(serverconns, 0, sizeof(serverconns))
198         for 0 <= i < info.numServers:
199             serverconns[i] = rx_NewConnection(info.hostAddr[i].sin_addr.s_addr,
200                                               info.hostAddr[i].sin_port,
201                                               PRSRV,
202                                               sc,
203                                               sec)
204
205         code = ubik_ClientInit(serverconns, &self.client)
206         pyafs_error(code)
207
208         code = rxs_Release(sc)
209
210     def __dealloc__(self):
211         ubik_ClientDestroy(self.client)
212         rx_Finalize()
213
214     def _NameOrId(self, ident):
215         """
216         Given an identifier, convert it to a PTS ID by looking up the
217         name if it's a string, or otherwise just converting it to an
218         integer.
219         """
220         if isinstance(ident, (str, unicode)):
221             return self._NameToId(ident)
222         else:
223             return int(ident)
224
225     def _NameToId(self, name):
226         """
227         Converts a user or group to an AFS ID.
228         """
229         cdef namelist lnames
230         cdef idlist lids
231         cdef afs_int32 code, id = ANONYMOUSID
232         name = name.lower()
233
234         lids.idlist_len = 0
235         lids.idlist_val = NULL
236         lnames.namelist_len = 1
237         lnames.namelist_val = <prname *>malloc(PR_MAXNAMELEN)
238         strncpy(lnames.namelist_val[0], name, PR_MAXNAMELEN)
239         code = ubik_PR_NameToID(self.client, 0, &lnames, &lids)
240         if lids.idlist_val is not NULL:
241             id = lids.idlist_val[0]
242             free(lids.idlist_val)
243         if id == ANONYMOUSID:
244             code = PRNOENT
245         pyafs_error(code)
246         return id
247
248     def _IdToName(self, id):
249         """
250         Convert an AFS ID to the name of a user or group.
251         """
252         cdef namelist lnames
253         cdef idlist lids
254         cdef afs_int32 code
255         cdef char name[PR_MAXNAMELEN]
256
257         lids.idlist_len = 1
258         lids.idlist_val = <afs_int32 *>malloc(sizeof(afs_int32))
259         lids.idlist_val[0] = id
260         lnames.namelist_len = 0
261         lnames.namelist_val = NULL
262         code = ubik_PR_IDToName(self.client, 0, &lids, &lnames)
263         if lnames.namelist_val is not NULL:
264             strncpy(name, lnames.namelist_val[0], sizeof(name))
265             free(lnames.namelist_val)
266         if lids.idlist_val is not NULL:
267             free(lids.idlist_val)
268         if name == str(id):
269             code = PRNOENT
270         pyafs_error(code)
271         return name
272
273     def _CreateUser(self, name, id=None):
274         """
275         Create a new user in the protection database. If an ID is
276         provided, that one will be used.
277         """
278         cdef afs_int32 code
279         cdef afs_int32 cid
280         name = name[:PR_MAXNAMELEN].lower()
281
282         if id is not None:
283             cid = id
284
285         if id is not None:
286             code = ubik_PR_INewEntry(self.client, 0, name, cid, 0)
287         else:
288             code = ubik_PR_NewEntry(self.client, 0, name, 0, 0, &cid)
289
290         pyafs_error(code)
291         return cid
292
293     def _CreateGroup(self, name, owner, id=None):
294         """
295         Create a new group in the protection database. If an ID is
296         provided, that one will be used.
297         """
298         cdef afs_int32 code, cid
299
300         name = name[:PR_MAXNAMELEN].lower()
301         oid = self._NameOrId(owner)
302
303         if id is not None:
304             cid = id
305             code = ubik_PR_INewEntry(self.client, 0, name, cid, oid)
306         else:
307             code = ubik_PR_NewEntry(self.client, 0, name, PRGRP, oid, &cid)
308
309         pyafs_error(code)
310         return cid
311
312     def _Delete(self, ident):
313         """
314         Delete the protection database entry with the provided
315         identifier.
316         """
317         cdef afs_int32 code
318         cdef afs_int32 id = self._NameOrId(ident)
319
320         code = ubik_PR_Delete(self.client, 0, id)
321         pyafs_error(code)
322
323     def _AddToGroup(self, user, group):
324         """
325         Add the given user to the given group.
326         """
327         cdef afs_int32 code
328         cdef afs_int32 uid = self._NameOrId(user), gid = self._NameOrId(group)
329
330         code = ubik_PR_AddToGroup(self.client, 0, uid, gid)
331         pyafs_error(code)
332
333     def _RemoveFromGroup(self, user, group):
334         """
335         Remove the given user from the given group.
336         """
337         cdef afs_int32 code
338         cdef afs_int32 uid = self._NameOrId(user), gid = self._NameOrId(group)
339
340         code = ubik_PR_RemoveFromGroup(self.client, 0, uid, gid)
341         pyafs_error(code)
342
343     def _ListMembers(self, ident):
344         """
345         Get the membership of an entity.
346
347         If id is a group, this returns the users that are in that
348         group.
349
350         If id is a user, this returns the list of groups that user is
351         on.
352
353         This returns a list of PTS IDs.
354         """
355         cdef afs_int32 code, over
356         cdef prlist alist
357         cdef int i
358         cdef object members = []
359
360         cdef afs_int32 id = self._NameOrId(ident)
361
362         alist.prlist_len = 0
363         alist.prlist_val = NULL
364
365         code = ubik_PR_ListElements(self.client, 0, id, &alist, &over)
366
367         if alist.prlist_val is not NULL:
368             for i in range(alist.prlist_len):
369                 members.append(alist.prlist_val[i])
370             free(alist.prlist_val)
371
372         pyafs_error(code)
373
374         return members
375
376     def _ListOwned(self, owner):
377         """
378         Get all groups owned by an entity.
379         """
380         cdef afs_int32 code, over
381         cdef prlist alist
382         cdef int i
383         cdef object owned = []
384
385         cdef afs_int32 oid = self._NameOrId(owner)
386
387         alist.prlist_len = 0
388         alist.prlist_val = NULL
389
390         code = ubik_PR_ListOwned(self.client, 0, oid, &alist, &over)
391
392         if alist.prlist_val is not NULL:
393             for i in range(alist.prlist_len):
394                 owned.append(alist.prlist_val[i])
395             free(alist.prlist_val)
396
397         pyafs_error(code)
398
399         return owned
400
401     def _ListEntry(self, ident):
402         """
403         Load a PTEntry instance with information about the provided
404         entity.
405         """
406         cdef afs_int32 code
407         cdef prcheckentry centry
408         cdef object entry = PTEntry()
409
410         cdef afs_int32 id = self._NameOrId(ident)
411
412         code = ubik_PR_ListEntry(self.client, 0, id, &centry)
413         pyafs_error(code)
414
415         _ptentry_from_c(entry, &centry)
416         return entry
417
418     def _ChangeEntry(self, ident, newname=None, newid=None, newoid=None):
419         """
420         Change the name, ID, and/or owner of a PTS entity.
421
422         For any of newname, newid, and newoid which aren't specified
423         or ar None, the value isn't changed.
424         """
425         cdef afs_int32 code
426         cdef afs_int32 c_newid = 0, c_newoid = 0
427         cdef char * c_newname
428
429         cdef afs_int32 id = self._NameOrId(ident)
430
431         if newname is None:
432             newname = self._IdToName(id)
433         c_newname = newname
434         if newid is not None:
435             c_newid = newid
436         if newoid is not None:
437             c_newoid = newoid
438
439         code = ubik_PR_ChangeEntry(self.client, 0, id, c_newname, c_newoid, c_newid)
440         pyafs_error(code)
441
442     def _IsAMemberOf(self, user, group):
443         """
444         Return True if the given user is a member of the given group.
445         """
446         cdef afs_int32 code
447         cdef afs_int32 flag
448
449         cdef afs_int32 uid = self._NameOrId(user), gid = self._NameOrId(group)
450
451         code = ubik_PR_IsAMemberOf(self.client, 0, uid, gid, &flag)
452         pyafs_error(code)
453
454         return bool(flag)
455
456     def _ListMax(self):
457         """
458         Return a tuple of the maximum user ID and the maximum group
459         ID currently assigned.
460         """
461         cdef afs_int32 code, uid, gid
462
463         code = ubik_PR_ListMax(self.client, 0, &uid, &gid)
464         pyafs_error(code)
465
466         return (uid, gid)
467
468     def _SetMaxUserId(self, id):
469         """
470         Set the maximum currently assigned user ID (the next
471         automatically assigned UID will be id + 1)
472         """
473         cdef afs_int32 code
474
475         code = ubik_PR_SetMax(self.client, 0, id, 0)
476         pyafs_error(code)
477
478     def _SetMaxGroupId(self, id):
479         """
480         Set the maximum currently assigned user ID (the next
481         automatically assigned UID will be id + 1)
482         """
483         cdef afs_int32 code
484
485         code = ubik_PR_SetMax(self.client, 0, id, PRGRP)
486         pyafs_error(code)
487
488     def _ListEntries(self, users=None, groups=None):
489         """
490         Return a list of PTEntry instances representing all entries in
491         the PRDB.
492
493         Returns just users by default, but can return just users, just
494         groups, or both.
495         """
496         cdef afs_int32 code
497         cdef afs_int32 flag = 0, startindex = 0, nentries, nextstartindex
498         cdef prentries centries
499         cdef unsigned int i
500
501         cdef object entries = []
502
503         if groups is None or users is True:
504             flag |= PRUSERS
505         if groups:
506             flag |= PRGROUPS
507
508         while startindex != -1:
509             centries.prentries_val = NULL
510             centries.prentries_len = 0
511             nextstartindex = -1
512
513             code = ubik_PR_ListEntries(self.client, 0, flag, startindex, &centries, &nextstartindex)
514             if centries.prentries_val is not NULL:
515                 for i in range(centries.prentries_len):
516                     e = PTEntry()
517                     _ptentry_from_c(e, <prcheckentry *>&centries.prentries_val[i])
518                     entries.append(e)
519                 free(centries.prentries_val)
520             pyafs_error(code)
521
522             startindex = nextstartindex
523
524         return entries
525
526     def _SetFields(self, ident, access=None, groups=None, users=None):
527         """
528         Update the fields for an entry.
529
530         Valid fields are the privacy flags (access), the group quota
531         (groups), or the "foreign user quota" (users), which doesn't
532         actually seem to do anything, but is included for
533         completeness.
534         """
535         cdef afs_int32 code
536         cdef afs_int32 mask = 0, flags = 0, nusers = 0, ngroups = 0
537
538         cdef afs_int32 id = self._NameOrId(ident)
539
540         if access is not None:
541             flags = access
542             mask |= PR_SF_ALLBITS
543         if groups is not None:
544             ngroups = groups
545             mask |= PR_SF_NGROUPS
546         if users is not None:
547             nusers = users
548             mask |= PR_SF_NGROUPS
549
550         code = ubik_PR_SetFieldsEntry(self.client, 0, id, mask, flags, ngroups, nusers, 0, 0)
551         pyafs_error(code)