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