Kill off the additional message string in pyafs_error - that's what
[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 cdef class PTEntry:
75     cdef public afs_int32 flags
76     cdef public afs_int32 id
77     cdef public afs_int32 owner
78     cdef public afs_int32 creator
79     cdef public afs_int32 ngroups
80     cdef public afs_int32 nusers
81     cdef public afs_int32 count
82     cdef public object name
83
84     def __repr__(self):
85         if self.name != '':
86             return '<PTEntry: %s>' % self.name
87         else:
88             return '<PTEntry: PTS ID %s>' % self.id
89
90 cdef int _ptentry_from_c(PTEntry p_entry, prcheckentry * c_entry) except -1:
91     if p_entry is None:
92         raise TypeError
93         return -1
94
95     p_entry.flags = c_entry.flags
96     p_entry.id = c_entry.id
97     p_entry.owner = c_entry.owner
98     p_entry.creator = c_entry.creator
99     p_entry.ngroups = c_entry.ngroups
100     p_entry.nusers = c_entry.nusers
101     p_entry.count = c_entry.count
102     p_entry.name = c_entry.name
103     return 0
104
105 cdef int _ptentry_to_c(prcheckentry * c_entry, PTEntry p_entry) except -1:
106     if p_entry is None:
107         raise TypeError
108         return -1
109
110     c_entry.flags = p_entry.flags
111     c_entry.id = p_entry.id
112     c_entry.owner = p_entry.owner
113     c_entry.creator = p_entry.creator
114     c_entry.ngroups = p_entry.ngroups
115     c_entry.nusers = p_entry.nusers
116     c_entry.count = p_entry.count
117     strncpy(c_entry.name, p_entry.name, sizeof(c_entry.name))
118     return 0
119
120 cdef class PTS:
121     """
122     A PTS object is essentially a handle to talk to the server in a
123     given cell.
124
125     cell defaults to None. If no argument is passed for cell, PTS
126     connects to the home cell.
127
128     sec is the security level, an integer from 0 to 3:
129       - 0: unauthenticated connection
130       - 1: try authenticated, then fall back to unauthenticated
131       - 2: fail if an authenticated connection can't be established
132       - 3: same as 2, plus encrypt all traffic to the protection
133         server
134     """
135     cdef ubik_client * client
136
137     def __cinit__(self, cell=None, sec=1):
138         cdef afs_int32 code
139         cdef afsconf_dir *cdir
140         cdef afsconf_cell info
141         cdef char * c_cell
142         cdef ktc_principal prin
143         cdef ktc_token token
144         cdef rx_securityClass *sc
145         cdef rx_connection *serverconns[MAXSERVERS]
146         cdef int i
147
148         initialize_PT_error_table()
149
150         if cell is None:
151             c_cell = NULL
152         else:
153             c_cell = cell
154
155         self.client = NULL
156
157         code = rx_Init(0)
158         if code != 0:
159             raise Exception(code, "Error initializing Rx")
160
161         cdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH)
162         if cdir is NULL:
163             raise OSError(errno,
164                           "Error opening configuration directory (%s): %s" % \
165                               (AFSDIR_CLIENT_ETC_DIRPATH, strerror(errno)))
166         code = afsconf_GetCellInfo(cdir, c_cell, "afsprot", &info)
167         if code != 0:
168             raise Exception(code, "GetCellInfo: %s" % afs_error_message(code))
169
170         if sec > 0:
171             strncpy(prin.cell, info.name, sizeof(prin.cell))
172             prin.instance[0] = 0
173             strncpy(prin.name, "afs", sizeof(prin.name))
174
175             code = ktc_GetToken(&prin, &token, sizeof(token), NULL);
176             if code != 0:
177                 if sec >= 2:
178                     # No really - we wanted authentication
179                     raise Exception(code, "Failed to get token for service AFS: %s" % afs_error_message(code))
180                 sec = 0
181             else:
182                 if sec == 3:
183                     level = rxkad_crypt
184                 else:
185                     level = rxkad_clear
186                 sc = rxkad_NewClientSecurityObject(level, &token.sessionKey,
187                                                    token.kvno, token.ticketLen,
188                                                    token.ticket)
189
190         if sec == 0:
191             sc = rxnull_NewClientSecurityObject()
192         else:
193             sec = 2
194
195         memset(serverconns, 0, sizeof(serverconns))
196         for 0 <= i < info.numServers:
197             serverconns[i] = rx_NewConnection(info.hostAddr[i].sin_addr.s_addr,
198                                               info.hostAddr[i].sin_port,
199                                               PRSRV,
200                                               sc,
201                                               sec)
202
203         code = ubik_ClientInit(serverconns, &self.client)
204         if code != 0:
205             raise Exception("Failed to initialize ubik connection to Protection server: %s" % afs_error_message(code))
206
207         code = rxs_Release(sc)
208
209     def __dealloc__(self):
210         ubik_ClientDestroy(self.client)
211         rx_Finalize()
212
213     def NameOrId(self, ident):
214         """
215         Given an identifier, convert it to a PTS ID by looking up the
216         name if it's a string, or otherwise just converting it to an
217         integer.
218         """
219         if isinstance(ident, (str, unicode)):
220             return self.NameToId(ident)
221         else:
222             return int(ident)
223
224     def NameToId(self, name):
225         """
226         Converts a user or group to an AFS ID.
227         """
228         cdef namelist lnames
229         cdef idlist lids
230         cdef afs_int32 code, id
231         name = name.lower()
232
233         lids.idlist_len = 0
234         lids.idlist_val = NULL
235         lnames.namelist_len = 1
236         lnames.namelist_val = <prname *>malloc(PR_MAXNAMELEN)
237         strncpy(lnames.namelist_val[0], name, PR_MAXNAMELEN)
238         code = ubik_PR_NameToID(self.client, 0, &lnames, &lids)
239         if lids.idlist_val is not NULL:
240             id = lids.idlist_val[0]
241             free(lids.idlist_val)
242         if id == ANONYMOUSID:
243             code = PRNOENT
244         if code != 0:
245             raise Exception("Failed to lookup PTS name: %s" % afs_error_message(code))
246         return id
247
248     def IdToName(self, id):
249         """
250         Convert an AFS ID to the name of a user or group.
251         """
252         cdef namelist lnames
253         cdef idlist lids
254         cdef afs_int32 code
255         cdef char name[PR_MAXNAMELEN]
256
257         lids.idlist_len = 1
258         lids.idlist_val = <afs_int32 *>malloc(sizeof(afs_int32))
259         lids.idlist_val[0] = id
260         lnames.namelist_len = 0
261         lnames.namelist_val = NULL
262         code = ubik_PR_IDToName(self.client, 0, &lids, &lnames)
263         if lnames.namelist_val is not NULL:
264             strncpy(name, lnames.namelist_val[0], sizeof(name))
265             free(lnames.namelist_val)
266         if lids.idlist_val is not NULL:
267             free(lids.idlist_val)
268         if name == str(id):
269             code = PRNOENT
270         if code != 0:
271             raise Exception("Failed to lookup PTS ID: %s" % afs_error_message(code))
272         return name
273
274     def CreateUser(self, name, id=None):
275         """
276         Create a new user in the protection database. If an ID is
277         provided, that one will be used.
278         """
279         cdef afs_int32 code
280         cdef afs_int32 cid
281         name = name[:PR_MAXNAMELEN].lower()
282
283         if id is not None:
284             cid = id
285
286         if id is not None:
287             code = ubik_PR_INewEntry(self.client, 0, name, cid, 0)
288         else:
289             code = ubik_PR_NewEntry(self.client, 0, name, 0, 0, &cid)
290
291         if code != 0:
292             raise Exception("Failed to create user: %s" % afs_error_message(code))
293         return cid
294
295     def CreateGroup(self, name, owner, id=None):
296         """
297         Create a new group in the protection database. If an ID is
298         provided, that one will be used.
299         """
300         cdef afs_int32 code, cid
301
302         name = name[:PR_MAXNAMELEN].lower()
303         oid = self.NameOrId(owner)
304
305         if id is not None:
306             cid = id
307             code = ubik_PR_INewEntry(self.client, 0, name, cid, oid)
308         else:
309             code = ubik_PR_NewEntry(self.client, 0, name, PRGRP, oid, &cid)
310
311         if code != 0:
312             raise Exception("Failed to create group: %s" % afs_error_message(code))
313         return cid
314
315     def Delete(self, ident):
316         """
317         Delete the protection database entry with the provided
318         identifier.
319         """
320         cdef afs_int32 code
321         cdef afs_int32 id = self.NameOrId(ident)
322
323         code = ubik_PR_Delete(self.client, 0, id)
324         if code != 0:
325             raise Exception("Failed to delete user: %s" % afs_error_message(code))
326
327     def AddToGroup(self, user, group):
328         """
329         Add the given user to the given group.
330         """
331         cdef afs_int32 code
332         cdef afs_int32 uid = self.NameOrId(user), gid = self.NameOrId(group)
333
334         code = ubik_PR_AddToGroup(self.client, 0, uid, gid)
335         if code != 0:
336             raise Exception("Failed to add user to group: %s" % afs_error_message(code))
337
338     def RemoveFromGroup(self, user, group):
339         """
340         Remove the given user from the given group.
341         """
342         cdef afs_int32 code
343         cdef afs_int32 uid = self.NameOrId(user), gid = self.NameOrId(group)
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, ident):
350         """
351         Get the membership of an entity.
352
353         If id is a group, this returns the users that are in that
354         group.
355
356         If id is a user, this returns the list of groups that user is
357         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         cdef afs_int32 id = self.NameOrId(ident)
367
368         alist.prlist_len = 0
369         alist.prlist_val = NULL
370
371         code = ubik_PR_ListElements(self.client, 0, id, &alist, &over)
372
373         if alist.prlist_val is not NULL:
374             for i in range(alist.prlist_len):
375                 members.append(alist.prlist_val[i])
376             free(alist.prlist_val)
377
378         if over:
379             code = PRTOOMANY
380         if code != 0:
381             raise Exception("Failed to get group membership: %s" % afs_error_message(code))
382
383         return members
384
385     def ListOwned(self, owner):
386         """
387         Get all groups owned by an entity.
388         """
389         cdef afs_int32 code, over
390         cdef prlist alist
391         cdef int i
392         cdef object owned = []
393
394         cdef afs_int32 oid = self.NameOrId(owner)
395
396         alist.prlist_len = 0
397         alist.prlist_val = NULL
398
399         code = ubik_PR_ListOwned(self.client, 0, oid, &alist, &over)
400
401         if alist.prlist_val is not NULL:
402             for i in range(alist.prlist_len):
403                 owned.append(alist.prlist_val[i])
404             free(alist.prlist_val)
405
406         if over:
407             code = PRTOOMANY
408         if code != 0:
409             raise Exception("Failed to get owned entities: %s" % afs_error_message(code))
410
411         return owned
412
413     def ListEntry(self, ident):
414         """
415         Load a PTEntry instance with information about the provided
416         entity.
417         """
418         cdef afs_int32 code
419         cdef prcheckentry centry
420         cdef object entry = PTEntry()
421
422         cdef afs_int32 id = self.NameOrId(ident)
423
424         code = ubik_PR_ListEntry(self.client, 0, id, &centry)
425         if code != 0:
426             raise Exception("Error getting entity info: %s" % afs_error_message(code))
427
428         _ptentry_from_c(entry, &centry)
429         return entry
430
431     def ChangeEntry(self, ident, newname=None, newid=None, newoid=None):
432         """
433         Change the name, ID, and/or owner of a PTS entity.
434
435         For any of newname, newid, and newoid which aren't specified
436         or ar None, the value isn't changed.
437         """
438         cdef afs_int32 code
439         cdef afs_int32 c_newid = 0, c_newoid = 0
440         cdef char * c_newname
441
442         cdef afs_int32 id = self.NameOrId(ident)
443
444         if newname is None:
445             newname = self.IdToName(id)
446         c_newname = newname
447         if newid is not None:
448             c_newid = newid
449         if newoid is not None:
450             c_newoid = newoid
451
452         code = ubik_PR_ChangeEntry(self.client, 0, id, c_newname, c_newoid, c_newid)
453         if code != 0:
454             raise Exception("Error changing entity info: %s" % afs_error_message(code))
455
456     def IsAMemberOf(self, user, group):
457         """
458         Return True if the given user is a member of the given group.
459         """
460         cdef afs_int32 code
461         cdef afs_int32 flag
462
463         cdef afs_int32 uid = self.NameOrId(user), gid = self.NameOrId(group)
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_c(e, <prcheckentry *>&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
544
545     def SetFields(self, ident, access=None, groups=None, users=None):
546         """
547         Update the fields for an entry.
548
549         Valid fields are the privacy flags (access), the group quota
550         (groups), or the "foreign user quota" (users), which doesn't
551         actually seem to do anything, but is included for
552         completeness.
553         """
554         cdef afs_int32 code
555         cdef afs_int32 mask = 0, flags = 0, nusers = 0, ngroups = 0
556
557         cdef afs_int32 id = self.NameOrId(ident)
558
559         if access is not None:
560             flags = access
561             mask |= PR_SF_ALLBITS
562         if groups is not None:
563             ngroups = groups
564             mask |= PR_SF_NGROUPS
565         if users is not None:
566             nusers = users
567             mask |= PR_SF_NGROUPS
568
569         code = ubik_PR_SetFieldsEntry(self.client, 0, id, mask, flags, ngroups, nusers, 0, 0)
570         if code != 0:
571             raise Exception("Unable to set fields: %s" % afs_error_message(code))