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