Abuse casting to avoid needing two sets of functions for converting
[invirt/packages/python-afs.git] / afs / _pts.pyx
1 from afs cimport *
2
3 cdef import from "afs/ptuser.h":
4     enum:
5         PR_MAXNAMELEN
6         PRGRP
7         PRUSERS
8         PRGROUPS
9         ANONYMOUSID
10         PR_SF_ALLBITS
11         PR_SF_NGROUPS
12         PR_SF_NUSERS
13
14     ctypedef char prname[PR_MAXNAMELEN]
15
16     struct namelist:
17         unsigned int namelist_len
18         prname *namelist_val
19
20     struct prlist:
21         unsigned int prlist_len
22         afs_int32 *prlist_val
23
24     struct idlist:
25         unsigned int idlist_len
26         afs_int32 *idlist_val
27
28     struct prcheckentry:
29         afs_int32 flags
30         afs_int32 id
31         afs_int32 owner
32         afs_int32 creator
33         afs_int32 ngroups
34         afs_int32 nusers
35         afs_int32 count
36         char name[PR_MAXNAMELEN]
37
38     struct prlistentries:
39         afs_int32 flags
40         afs_int32 id
41         afs_int32 owner
42         afs_int32 creator
43         afs_int32 ngroups
44         afs_int32 nusers
45         afs_int32 count
46         char name[PR_MAXNAMELEN]
47
48     struct prentries:
49         unsigned int prentries_len
50         prlistentries *prentries_val
51
52     int ubik_PR_NameToID(ubik_client *, afs_int32, namelist *, idlist *)
53     int ubik_PR_IDToName(ubik_client *, afs_int32, idlist *, namelist *)
54     int ubik_PR_INewEntry(ubik_client *, afs_int32, char *, afs_int32, afs_int32)
55     int ubik_PR_NewEntry(ubik_client *, afs_int32, char *, afs_int32, afs_int32, afs_int32 *)
56     int ubik_PR_Delete(ubik_client *, afs_int32, afs_int32)
57     int ubik_PR_AddToGroup(ubik_client *, afs_int32, afs_int32, afs_int32)
58     int ubik_PR_RemoveFromGroup(ubik_client *, afs_int32, afs_int32, afs_int32)
59     int ubik_PR_ListElements(ubik_client *, afs_int32, afs_int32, prlist *, afs_int32 *)
60     int ubik_PR_ListOwned(ubik_client *, afs_int32, afs_int32, prlist *, afs_int32 *)
61     int ubik_PR_ListEntry(ubik_client *, afs_int32, afs_int32, prcheckentry *)
62     int ubik_PR_ChangeEntry(ubik_client *, afs_int32, afs_int32, char *, afs_int32, afs_int32)
63     int ubik_PR_IsAMemberOf(ubik_client *, afs_int32, afs_int32, afs_int32, afs_int32 *)
64     int ubik_PR_ListMax(ubik_client *, afs_int32, afs_int32 *, afs_int32 *)
65     int ubik_PR_SetMax(ubik_client *, afs_int32, afs_int32, afs_int32)
66     int ubik_PR_ListEntries(ubik_client *, afs_int32, afs_int32, afs_int32, prentries *, afs_int32 *)
67     int ubik_PR_SetFieldsEntry(ubik_client *, afs_int32, afs_int32, afs_int32, afs_int32, afs_int32, afs_int32, afs_int32, afs_int32)
68
69 cdef import from "afs/pterror.h":
70     enum:
71         PRNOENT
72         PRTOOMANY
73
74     void initialize_PT_error_table()
75
76 cdef class PTEntry:
77     cdef public afs_int32 flags
78     cdef public afs_int32 id
79     cdef public afs_int32 owner
80     cdef public afs_int32 creator
81     cdef public afs_int32 ngroups
82     cdef public afs_int32 nusers
83     cdef public afs_int32 count
84     cdef public object name
85
86     def __repr__(self):
87         if self.name != '':
88             return '<PTEntry: %s>' % self.name
89         else:
90             return '<PTEntry: PTS ID %s>' % self.id
91
92 cdef int _ptentry_from_c(PTEntry p_entry, prcheckentry * c_entry) except -1:
93     if p_entry is None:
94         raise TypeError
95         return -1
96
97     p_entry.flags = c_entry.flags
98     p_entry.id = c_entry.id
99     p_entry.owner = c_entry.owner
100     p_entry.creator = c_entry.creator
101     p_entry.ngroups = c_entry.ngroups
102     p_entry.nusers = c_entry.nusers
103     p_entry.count = c_entry.count
104     p_entry.name = c_entry.name
105     return 0
106
107 cdef int _ptentry_to_c(prcheckentry * c_entry, PTEntry p_entry) except -1:
108     if p_entry is None:
109         raise TypeError
110         return -1
111
112     c_entry.flags = p_entry.flags
113     c_entry.id = p_entry.id
114     c_entry.owner = p_entry.owner
115     c_entry.creator = p_entry.creator
116     c_entry.ngroups = p_entry.ngroups
117     c_entry.nusers = p_entry.nusers
118     c_entry.count = p_entry.count
119     strncpy(c_entry.name, p_entry.name, sizeof(c_entry.name))
120     return 0
121
122 cdef class PTS:
123     """
124     A PTS object is essentially a handle to talk to the server in a
125     given cell.
126
127     cell defaults to None. If no argument is passed for cell, PTS
128     connects to the home cell.
129
130     sec is the security level, an integer from 0 to 3:
131       - 0: unauthenticated connection
132       - 1: try authenticated, then fall back to unauthenticated
133       - 2: fail if an authenticated connection can't be established
134       - 3: same as 2, plus encrypt all traffic to the protection
135         server
136     """
137     cdef ubik_client * client
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         if code != 0:
170             raise Exception(code, "GetCellInfo: %s" % afs_error_message(code))
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                     raise Exception(code, "Failed to get token for service AFS: %s" % afs_error_message(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         if code != 0:
207             raise Exception("Failed to initialize ubik connection to Protection server: %s" % afs_error_message(code))
208
209         code = rxs_Release(sc)
210
211     def __dealloc__(self):
212         ubik_ClientDestroy(self.client)
213         rx_Finalize()
214
215     def NameToId(self, name):
216         """
217         Converts a user or group to an AFS ID.
218         """
219         cdef namelist lnames
220         cdef idlist lids
221         cdef afs_int32 code, id
222         name = name.lower()
223
224         lids.idlist_len = 0
225         lids.idlist_val = NULL
226         lnames.namelist_len = 1
227         lnames.namelist_val = <prname *>malloc(PR_MAXNAMELEN)
228         strncpy(lnames.namelist_val[0], name, PR_MAXNAMELEN)
229         code = ubik_PR_NameToID(self.client, 0, &lnames, &lids)
230         if lids.idlist_val is not NULL:
231             id = lids.idlist_val[0]
232             free(lids.idlist_val)
233         if id == ANONYMOUSID:
234             code = PRNOENT
235         if code != 0:
236             raise Exception("Failed to lookup PTS name: %s" % afs_error_message(code))
237         return id
238
239     def IdToName(self, id):
240         """
241         Convert an AFS ID to the name of a user or group.
242         """
243         cdef namelist lnames
244         cdef idlist lids
245         cdef afs_int32 code
246         cdef char name[PR_MAXNAMELEN]
247
248         lids.idlist_len = 1
249         lids.idlist_val = <afs_int32 *>malloc(sizeof(afs_int32))
250         lids.idlist_val[0] = id
251         lnames.namelist_len = 0
252         lnames.namelist_val = NULL
253         code = ubik_PR_IDToName(self.client, 0, &lids, &lnames)
254         if lnames.namelist_val is not NULL:
255             strncpy(name, lnames.namelist_val[0], sizeof(name))
256             free(lnames.namelist_val)
257         if lids.idlist_val is not NULL:
258             free(lids.idlist_val)
259         if name == str(id):
260             code = PRNOENT
261         if code != 0:
262             raise Exception("Failed to lookup PTS ID: %s" % afs_error_message(code))
263         return name
264
265     def CreateUser(self, name, id=None):
266         """
267         Create a new user in the protection database. If an ID is
268         provided, that one will be used.
269         """
270         cdef afs_int32 code
271         cdef afs_int32 cid
272         name = name[:PR_MAXNAMELEN].lower()
273
274         if id is not None:
275             cid = id
276
277         if id is not None:
278             code = ubik_PR_INewEntry(self.client, 0, name, cid, 0)
279         else:
280             code = ubik_PR_NewEntry(self.client, 0, name, 0, 0, &cid)
281
282         if code != 0:
283             raise Exception("Failed to create user: %s" % afs_error_message(code))
284         return cid
285
286     def CreateGroup(self, name, owner, id=None):
287         """
288         Create a new group in the protection database. If an ID is
289         provided, that one will be used.
290         """
291         cdef afs_int32 code, cid
292
293         name = name[:PR_MAXNAMELEN].lower()
294         oid = self.NameToId(owner)
295
296         if id is not None:
297             cid = id
298             code = ubik_PR_INewEntry(self.client, 0, name, cid, oid)
299         else:
300             code = ubik_PR_NewEntry(self.client, 0, name, PRGRP, oid, &cid)
301
302         if code != 0:
303             raise Exception("Failed to create group: %s" % afs_error_message(code))
304         return cid
305
306     def Delete(self, id):
307         """
308         Delete the protection database entry with the provided ID.
309         """
310         cdef afs_int32 code
311
312         code = ubik_PR_Delete(self.client, 0, id)
313         if code != 0:
314             raise Exception("Failed to delete user: %s" % afs_error_message(code))
315
316     def AddToGroup(self, uid, gid):
317         """
318         Add the user with the given ID to the group with the given ID.
319         """
320         cdef afs_int32 code
321
322         code = ubik_PR_AddToGroup(self.client, 0, uid, gid)
323         if code != 0:
324             raise Exception("Failed to add user to group: %s" % afs_error_message(code))
325
326     def RemoveFromGroup(self, uid, gid):
327         """
328         Remove the user with the given ID from the group with the given ID.
329         """
330         cdef afs_int32 code
331
332         code = ubik_PR_RemoveFromGroup(self.client, 0, uid, gid)
333         if code != 0:
334             raise Exception("Failed to remove user from group: %s" % afs_error_message(code))
335
336     def ListMembers(self, id):
337         """
338         Get the membership of an entity.
339
340         If id is a group ID, this returns the users that are in that
341         group.
342
343         If id is a user ID, this returns the list of groups that user
344         is on.
345
346         This returns a list of PTS IDs.
347         """
348         cdef afs_int32 code, over
349         cdef prlist alist
350         cdef int i
351         cdef object members = []
352
353         alist.prlist_len = 0
354         alist.prlist_val = NULL
355
356         code = ubik_PR_ListElements(self.client, 0, id, &alist, &over)
357
358         if alist.prlist_val is not NULL:
359             for i in range(alist.prlist_len):
360                 members.append(alist.prlist_val[i])
361             free(alist.prlist_val)
362
363         if over:
364             code = PRTOOMANY
365         if code != 0:
366             raise Exception("Failed to get group membership: %s" % afs_error_message(code))
367
368         return members
369
370     def ListOwned(self, oid):
371         """
372         Get all groups owned by an entity.
373         """
374         cdef afs_int32 code, over
375         cdef prlist alist
376         cdef int i
377         cdef object owned = []
378
379         alist.prlist_len = 0
380         alist.prlist_val = NULL
381
382         code = ubik_PR_ListOwned(self.client, 0, oid, &alist, &over)
383
384         if alist.prlist_val is not NULL:
385             for i in range(alist.prlist_len):
386                 owned.append(alist.prlist_val[i])
387             free(alist.prlist_val)
388
389         if over:
390             code = PRTOOMANY
391         if code != 0:
392             raise Exception("Failed to get owned entities: %s" % afs_error_message(code))
393
394         return owned
395
396     def ListEntry(self, id):
397         """
398         Load a PTEntry instance with information about the provided
399         ID.
400         """
401         cdef afs_int32 code
402         cdef prcheckentry centry
403         cdef object entry = PTEntry()
404
405         code = ubik_PR_ListEntry(self.client, 0, id, &centry)
406         if code != 0:
407             raise Exception("Error getting entity info: %s" % afs_error_message(code))
408
409         _ptentry_from_c(entry, &centry)
410         return entry
411
412     def ChangeEntry(self, id, newname=None, newid=None, newoid=None):
413         """
414         Change the name, ID, and/or owner of a PTS entity.
415
416         For any of newname, newid, and newoid which aren't specified
417         or ar None, the value isn't changed.
418         """
419         cdef afs_int32 code
420         cdef afs_int32 c_newid = 0, c_newoid = 0
421         cdef char * c_newname
422
423         if newname is None:
424             newname = self.IdToName(id)
425         c_newname = newname
426         if newid is not None:
427             c_newid = newid
428         if newoid is not None:
429             c_newoid = newoid
430
431         code = ubik_PR_ChangeEntry(self.client, 0, id, c_newname, c_newoid, c_newid)
432         if code != 0:
433             raise Exception("Error changing entity info: %s" % afs_error_message(code))
434
435     def IsAMemberOf(self, uid, gid):
436         """
437         Return True if the given uid is a member of the given gid.
438         """
439         cdef afs_int32 code
440         cdef afs_int32 flag
441
442         code = ubik_PR_IsAMemberOf(self.client, 0, uid, gid, &flag)
443         if code != 0:
444             raise Exception("Error testing membership: %s" % afs_error_message(code))
445
446         return bool(flag)
447
448     def ListMax(self):
449         """
450         Return a tuple of the maximum user ID and the maximum group
451         ID currently assigned.
452         """
453         cdef afs_int32 code, uid, gid
454
455         code = ubik_PR_ListMax(self.client, 0, &uid, &gid)
456         if code != 0:
457             raise Exception("Error looking up max uid/gid: %s" % afs_error_message(code))
458
459         return (uid, gid)
460
461     def SetMaxUserId(self, id):
462         """
463         Set the maximum currently assigned user ID (the next
464         automatically assigned UID will be id + 1)
465         """
466         cdef afs_int32 code
467
468         code = ubik_PR_SetMax(self.client, 0, id, 0)
469         if code != 0:
470             raise Exception("Error setting max uid: %s" % afs_error_message(code))
471
472     def SetMaxGroupId(self, id):
473         """
474         Set the maximum currently assigned user ID (the next
475         automatically assigned UID will be id + 1)
476         """
477         cdef afs_int32 code
478
479         code = ubik_PR_SetMax(self.client, 0, id, PRGRP)
480         if code != 0:
481             raise Exception("Error setting max gid: %s" % afs_error_message(code))
482
483     def ListEntries(self, users=None, groups=None):
484         """
485         Return a list of PTEntry instances representing all entries in
486         the PRDB.
487
488         Returns just users by default, but can return just users, just
489         groups, or both.
490         """
491         cdef afs_int32 code
492         cdef afs_int32 flag = 0, startindex = 0, nentries, nextstartindex
493         cdef prentries centries
494         cdef unsigned int i
495
496         cdef object entries = []
497
498         if groups is None or users is True:
499             flag |= PRUSERS
500         if groups:
501             flag |= PRGROUPS
502
503         while startindex != -1:
504             centries.prentries_val = NULL
505             centries.prentries_len = 0
506             nextstartindex = -1
507
508             code = ubik_PR_ListEntries(self.client, 0, flag, startindex, &centries, &nextstartindex)
509             if centries.prentries_val is not NULL:
510                 for i in range(centries.prentries_len):
511                     e = PTEntry()
512                     _ptentry_from_c(e, <prcheckentry *>&centries.prentries_val[i])
513                     entries.append(e)
514                 free(centries.prentries_val)
515             if code != 0:
516                 raise Exception("Unable to list entries: %s" % afs_error_message(code))
517
518             startindex = nextstartindex
519
520         return entries
521
522     def SetFields(self, id, access=None, groups=None, users=None):
523         """
524         Update the fields for an entry.
525
526         Valid fields are the privacy flags (access), the group quota
527         (groups), or the "foreign user quota" (users), which doesn't
528         actually seem to do anything, but is included for
529         completeness.
530         """
531         cdef afs_int32 code
532         cdef afs_int32 mask = 0, flags = 0, nusers = 0, ngroups = 0
533
534         if access is not None:
535             flags = access
536             mask |= PR_SF_ALLBITS
537         if groups is not None:
538             ngroups = groups
539             mask |= PR_SF_NGROUPS
540         if users is not None:
541             nusers = users
542             mask |= PR_SF_NGROUPS
543
544         code = ubik_PR_SetFieldsEntry(self.client, 0, id, mask, flags, ngroups, nusers, 0, 0)
545         if code != 0:
546             raise Exception("Unable to set fields: %s" % afs_error_message(code))