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