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