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