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