Simplify the extension declarations in setup.py.
[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
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         if sec > 0:
171             strncpy(prin.cell, info.name, sizeof(prin.cell))
172             prin.instance[0] = 0
173             strncpy(prin.name, "afs", sizeof(prin.name))
174
175             code = ktc_GetToken(&prin, &token, sizeof(token), NULL);
176             if code != 0:
177                 if sec >= 2:
178                     # No really - we wanted authentication
179                     pyafs_error(code)
180                 sec = 0
181             else:
182                 if sec == 3:
183                     level = rxkad_crypt
184                 else:
185                     level = rxkad_clear
186                 sc = rxkad_NewClientSecurityObject(level, &token.sessionKey,
187                                                    token.kvno, token.ticketLen,
188                                                    token.ticket)
189
190         if sec == 0:
191             sc = rxnull_NewClientSecurityObject()
192         else:
193             sec = 2
194
195         memset(serverconns, 0, sizeof(serverconns))
196         for 0 <= i < info.numServers:
197             serverconns[i] = rx_NewConnection(info.hostAddr[i].sin_addr.s_addr,
198                                               info.hostAddr[i].sin_port,
199                                               PRSRV,
200                                               sc,
201                                               sec)
202
203         code = ubik_ClientInit(serverconns, &self.client)
204         pyafs_error(code)
205
206         code = rxs_Release(sc)
207
208     def __dealloc__(self):
209         ubik_ClientDestroy(self.client)
210         rx_Finalize()
211
212     def NameOrId(self, ident):
213         """
214         Given an identifier, convert it to a PTS ID by looking up the
215         name if it's a string, or otherwise just converting it to an
216         integer.
217         """
218         if isinstance(ident, (str, unicode)):
219             return self.NameToId(ident)
220         else:
221             return int(ident)
222
223     def NameToId(self, name):
224         """
225         Converts a user or group to an AFS ID.
226         """
227         cdef namelist lnames
228         cdef idlist lids
229         cdef afs_int32 code, id
230         name = name.lower()
231
232         lids.idlist_len = 0
233         lids.idlist_val = NULL
234         lnames.namelist_len = 1
235         lnames.namelist_val = <prname *>malloc(PR_MAXNAMELEN)
236         strncpy(lnames.namelist_val[0], name, PR_MAXNAMELEN)
237         code = ubik_PR_NameToID(self.client, 0, &lnames, &lids)
238         if lids.idlist_val is not NULL:
239             id = lids.idlist_val[0]
240             free(lids.idlist_val)
241         if id == ANONYMOUSID:
242             code = PRNOENT
243         pyafs_error(code)
244         return id
245
246     def IdToName(self, id):
247         """
248         Convert an AFS ID to the name of a user or group.
249         """
250         cdef namelist lnames
251         cdef idlist lids
252         cdef afs_int32 code
253         cdef char name[PR_MAXNAMELEN]
254
255         lids.idlist_len = 1
256         lids.idlist_val = <afs_int32 *>malloc(sizeof(afs_int32))
257         lids.idlist_val[0] = id
258         lnames.namelist_len = 0
259         lnames.namelist_val = NULL
260         code = ubik_PR_IDToName(self.client, 0, &lids, &lnames)
261         if lnames.namelist_val is not NULL:
262             strncpy(name, lnames.namelist_val[0], sizeof(name))
263             free(lnames.namelist_val)
264         if lids.idlist_val is not NULL:
265             free(lids.idlist_val)
266         if name == str(id):
267             code = PRNOENT
268         pyafs_error(code)
269         return name
270
271     def CreateUser(self, name, id=None):
272         """
273         Create a new user in the protection database. If an ID is
274         provided, that one will be used.
275         """
276         cdef afs_int32 code
277         cdef afs_int32 cid
278         name = name[:PR_MAXNAMELEN].lower()
279
280         if id is not None:
281             cid = id
282
283         if id is not None:
284             code = ubik_PR_INewEntry(self.client, 0, name, cid, 0)
285         else:
286             code = ubik_PR_NewEntry(self.client, 0, name, 0, 0, &cid)
287
288         pyafs_error(code)
289         return cid
290
291     def CreateGroup(self, name, owner, id=None):
292         """
293         Create a new group in the protection database. If an ID is
294         provided, that one will be used.
295         """
296         cdef afs_int32 code, cid
297
298         name = name[:PR_MAXNAMELEN].lower()
299         oid = self.NameOrId(owner)
300
301         if id is not None:
302             cid = id
303             code = ubik_PR_INewEntry(self.client, 0, name, cid, oid)
304         else:
305             code = ubik_PR_NewEntry(self.client, 0, name, PRGRP, oid, &cid)
306
307         pyafs_error(code)
308         return cid
309
310     def Delete(self, ident):
311         """
312         Delete the protection database entry with the provided
313         identifier.
314         """
315         cdef afs_int32 code
316         cdef afs_int32 id = self.NameOrId(ident)
317
318         code = ubik_PR_Delete(self.client, 0, id)
319         pyafs_error(code)
320
321     def AddToGroup(self, user, group):
322         """
323         Add the given user to the given group.
324         """
325         cdef afs_int32 code
326         cdef afs_int32 uid = self.NameOrId(user), gid = self.NameOrId(group)
327
328         code = ubik_PR_AddToGroup(self.client, 0, uid, gid)
329         pyafs_error(code)
330
331     def RemoveFromGroup(self, user, group):
332         """
333         Remove the given user from the given group.
334         """
335         cdef afs_int32 code
336         cdef afs_int32 uid = self.NameOrId(user), gid = self.NameOrId(group)
337
338         code = ubik_PR_RemoveFromGroup(self.client, 0, uid, gid)
339         pyafs_error(code)
340
341     def ListMembers(self, ident):
342         """
343         Get the membership of an entity.
344
345         If id is a group, this returns the users that are in that
346         group.
347
348         If id is a user, this returns the list of groups that user is
349         on.
350
351         This returns a list of PTS IDs.
352         """
353         cdef afs_int32 code, over
354         cdef prlist alist
355         cdef int i
356         cdef object members = []
357
358         cdef afs_int32 id = self.NameOrId(ident)
359
360         alist.prlist_len = 0
361         alist.prlist_val = NULL
362
363         code = ubik_PR_ListElements(self.client, 0, id, &alist, &over)
364
365         if alist.prlist_val is not NULL:
366             for i in range(alist.prlist_len):
367                 members.append(alist.prlist_val[i])
368             free(alist.prlist_val)
369
370         if over:
371             code = PRTOOMANY
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         if over:
398             code = PRTOOMANY
399         pyafs_error(code)
400
401         return owned
402
403     def ListEntry(self, ident):
404         """
405         Load a PTEntry instance with information about the provided
406         entity.
407         """
408         cdef afs_int32 code
409         cdef prcheckentry centry
410         cdef object entry = PTEntry()
411
412         cdef afs_int32 id = self.NameOrId(ident)
413
414         code = ubik_PR_ListEntry(self.client, 0, id, &centry)
415         pyafs_error(code)
416
417         _ptentry_from_c(entry, &centry)
418         return entry
419
420     def ChangeEntry(self, ident, newname=None, newid=None, newoid=None):
421         """
422         Change the name, ID, and/or owner of a PTS entity.
423
424         For any of newname, newid, and newoid which aren't specified
425         or ar None, the value isn't changed.
426         """
427         cdef afs_int32 code
428         cdef afs_int32 c_newid = 0, c_newoid = 0
429         cdef char * c_newname
430
431         cdef afs_int32 id = self.NameOrId(ident)
432
433         if newname is None:
434             newname = self.IdToName(id)
435         c_newname = newname
436         if newid is not None:
437             c_newid = newid
438         if newoid is not None:
439             c_newoid = newoid
440
441         code = ubik_PR_ChangeEntry(self.client, 0, id, c_newname, c_newoid, c_newid)
442         pyafs_error(code)
443
444     def IsAMemberOf(self, user, group):
445         """
446         Return True if the given user is a member of the given group.
447         """
448         cdef afs_int32 code
449         cdef afs_int32 flag
450
451         cdef afs_int32 uid = self.NameOrId(user), gid = self.NameOrId(group)
452
453         code = ubik_PR_IsAMemberOf(self.client, 0, uid, gid, &flag)
454         pyafs_error(code)
455
456         return bool(flag)
457
458     def ListMax(self):
459         """
460         Return a tuple of the maximum user ID and the maximum group
461         ID currently assigned.
462         """
463         cdef afs_int32 code, uid, gid
464
465         code = ubik_PR_ListMax(self.client, 0, &uid, &gid)
466         pyafs_error(code)
467
468         return (uid, gid)
469
470     def SetMaxUserId(self, id):
471         """
472         Set the maximum currently assigned user ID (the next
473         automatically assigned UID will be id + 1)
474         """
475         cdef afs_int32 code
476
477         code = ubik_PR_SetMax(self.client, 0, id, 0)
478         pyafs_error(code)
479
480     def SetMaxGroupId(self, id):
481         """
482         Set the maximum currently assigned user ID (the next
483         automatically assigned UID will be id + 1)
484         """
485         cdef afs_int32 code
486
487         code = ubik_PR_SetMax(self.client, 0, id, PRGRP)
488         pyafs_error(code)
489
490     def ListEntries(self, users=None, groups=None):
491         """
492         Return a list of PTEntry instances representing all entries in
493         the PRDB.
494
495         Returns just users by default, but can return just users, just
496         groups, or both.
497         """
498         cdef afs_int32 code
499         cdef afs_int32 flag = 0, startindex = 0, nentries, nextstartindex
500         cdef prentries centries
501         cdef unsigned int i
502
503         cdef object entries = []
504
505         if groups is None or users is True:
506             flag |= PRUSERS
507         if groups:
508             flag |= PRGROUPS
509
510         while startindex != -1:
511             centries.prentries_val = NULL
512             centries.prentries_len = 0
513             nextstartindex = -1
514
515             code = ubik_PR_ListEntries(self.client, 0, flag, startindex, &centries, &nextstartindex)
516             if centries.prentries_val is not NULL:
517                 for i in range(centries.prentries_len):
518                     e = PTEntry()
519                     _ptentry_from_c(e, <prcheckentry *>&centries.prentries_val[i])
520                     entries.append(e)
521                 free(centries.prentries_val)
522             pyafs_error(code)
523
524             startindex = nextstartindex
525
526         return entries
527
528     def SetFields(self, ident, access=None, groups=None, users=None):
529         """
530         Update the fields for an entry.
531
532         Valid fields are the privacy flags (access), the group quota
533         (groups), or the "foreign user quota" (users), which doesn't
534         actually seem to do anything, but is included for
535         completeness.
536         """
537         cdef afs_int32 code
538         cdef afs_int32 mask = 0, flags = 0, nusers = 0, ngroups = 0
539
540         cdef afs_int32 id = self.NameOrId(ident)
541
542         if access is not None:
543             flags = access
544             mask |= PR_SF_ALLBITS
545         if groups is not None:
546             ngroups = groups
547             mask |= PR_SF_NGROUPS
548         if users is not None:
549             nusers = users
550             mask |= PR_SF_NGROUPS
551
552         code = ubik_PR_SetFieldsEntry(self.client, 0, id, mask, flags, ngroups, nusers, 0, 0)
553         pyafs_error(code)