Add a mechanism for converting an identifier to a PTS ID.
[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.NameToId(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, id):
318         """
319         Delete the protection database entry with the provided ID.
320         """
321         cdef afs_int32 code
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, uid, gid):
328         """
329         Add the user with the given ID to the group with the given ID.
330         """
331         cdef afs_int32 code
332
333         code = ubik_PR_AddToGroup(self.client, 0, uid, gid)
334         if code != 0:
335             raise Exception("Failed to add user to group: %s" % afs_error_message(code))
336
337     def RemoveFromGroup(self, uid, gid):
338         """
339         Remove the user with the given ID from the group with the given ID.
340         """
341         cdef afs_int32 code
342
343         code = ubik_PR_RemoveFromGroup(self.client, 0, uid, gid)
344         if code != 0:
345             raise Exception("Failed to remove user from group: %s" % afs_error_message(code))
346
347     def ListMembers(self, id):
348         """
349         Get the membership of an entity.
350
351         If id is a group ID, this returns the users that are in that
352         group.
353
354         If id is a user ID, this returns the list of groups that user
355         is on.
356
357         This returns a list of PTS IDs.
358         """
359         cdef afs_int32 code, over
360         cdef prlist alist
361         cdef int i
362         cdef object members = []
363
364         alist.prlist_len = 0
365         alist.prlist_val = NULL
366
367         code = ubik_PR_ListElements(self.client, 0, id, &alist, &over)
368
369         if alist.prlist_val is not NULL:
370             for i in range(alist.prlist_len):
371                 members.append(alist.prlist_val[i])
372             free(alist.prlist_val)
373
374         if over:
375             code = PRTOOMANY
376         if code != 0:
377             raise Exception("Failed to get group membership: %s" % afs_error_message(code))
378
379         return members
380
381     def ListOwned(self, oid):
382         """
383         Get all groups owned by an entity.
384         """
385         cdef afs_int32 code, over
386         cdef prlist alist
387         cdef int i
388         cdef object owned = []
389
390         alist.prlist_len = 0
391         alist.prlist_val = NULL
392
393         code = ubik_PR_ListOwned(self.client, 0, oid, &alist, &over)
394
395         if alist.prlist_val is not NULL:
396             for i in range(alist.prlist_len):
397                 owned.append(alist.prlist_val[i])
398             free(alist.prlist_val)
399
400         if over:
401             code = PRTOOMANY
402         if code != 0:
403             raise Exception("Failed to get owned entities: %s" % afs_error_message(code))
404
405         return owned
406
407     def ListEntry(self, id):
408         """
409         Load a PTEntry instance with information about the provided
410         ID.
411         """
412         cdef afs_int32 code
413         cdef prcheckentry centry
414         cdef object entry = PTEntry()
415
416         code = ubik_PR_ListEntry(self.client, 0, id, &centry)
417         if code != 0:
418             raise Exception("Error getting entity info: %s" % afs_error_message(code))
419
420         _ptentry_from_c(entry, &centry)
421         return entry
422
423     def ChangeEntry(self, id, newname=None, newid=None, newoid=None):
424         """
425         Change the name, ID, and/or owner of a PTS entity.
426
427         For any of newname, newid, and newoid which aren't specified
428         or ar None, the value isn't changed.
429         """
430         cdef afs_int32 code
431         cdef afs_int32 c_newid = 0, c_newoid = 0
432         cdef char * c_newname
433
434         if newname is None:
435             newname = self.IdToName(id)
436         c_newname = newname
437         if newid is not None:
438             c_newid = newid
439         if newoid is not None:
440             c_newoid = newoid
441
442         code = ubik_PR_ChangeEntry(self.client, 0, id, c_newname, c_newoid, c_newid)
443         if code != 0:
444             raise Exception("Error changing entity info: %s" % afs_error_message(code))
445
446     def IsAMemberOf(self, uid, gid):
447         """
448         Return True if the given uid is a member of the given gid.
449         """
450         cdef afs_int32 code
451         cdef afs_int32 flag
452
453         code = ubik_PR_IsAMemberOf(self.client, 0, uid, gid, &flag)
454         if code != 0:
455             raise Exception("Error testing membership: %s" % afs_error_message(code))
456
457         return bool(flag)
458
459     def ListMax(self):
460         """
461         Return a tuple of the maximum user ID and the maximum group
462         ID currently assigned.
463         """
464         cdef afs_int32 code, uid, gid
465
466         code = ubik_PR_ListMax(self.client, 0, &uid, &gid)
467         if code != 0:
468             raise Exception("Error looking up max uid/gid: %s" % afs_error_message(code))
469
470         return (uid, gid)
471
472     def SetMaxUserId(self, id):
473         """
474         Set the maximum currently assigned user ID (the next
475         automatically assigned UID will be id + 1)
476         """
477         cdef afs_int32 code
478
479         code = ubik_PR_SetMax(self.client, 0, id, 0)
480         if code != 0:
481             raise Exception("Error setting max uid: %s" % afs_error_message(code))
482
483     def SetMaxGroupId(self, id):
484         """
485         Set the maximum currently assigned user ID (the next
486         automatically assigned UID will be id + 1)
487         """
488         cdef afs_int32 code
489
490         code = ubik_PR_SetMax(self.client, 0, id, PRGRP)
491         if code != 0:
492             raise Exception("Error setting max gid: %s" % afs_error_message(code))
493
494     def ListEntries(self, users=None, groups=None):
495         """
496         Return a list of PTEntry instances representing all entries in
497         the PRDB.
498
499         Returns just users by default, but can return just users, just
500         groups, or both.
501         """
502         cdef afs_int32 code
503         cdef afs_int32 flag = 0, startindex = 0, nentries, nextstartindex
504         cdef prentries centries
505         cdef unsigned int i
506
507         cdef object entries = []
508
509         if groups is None or users is True:
510             flag |= PRUSERS
511         if groups:
512             flag |= PRGROUPS
513
514         while startindex != -1:
515             centries.prentries_val = NULL
516             centries.prentries_len = 0
517             nextstartindex = -1
518
519             code = ubik_PR_ListEntries(self.client, 0, flag, startindex, &centries, &nextstartindex)
520             if centries.prentries_val is not NULL:
521                 for i in range(centries.prentries_len):
522                     e = PTEntry()
523                     _ptentry_from_c(e, <prcheckentry *>&centries.prentries_val[i])
524                     entries.append(e)
525                 free(centries.prentries_val)
526             if code != 0:
527                 raise Exception("Unable to list entries: %s" % afs_error_message(code))
528
529             startindex = nextstartindex
530
531         return entries
532
533     def SetFields(self, id, access=None, groups=None, users=None):
534         """
535         Update the fields for an entry.
536
537         Valid fields are the privacy flags (access), the group quota
538         (groups), or the "foreign user quota" (users), which doesn't
539         actually seem to do anything, but is included for
540         completeness.
541         """
542         cdef afs_int32 code
543         cdef afs_int32 mask = 0, flags = 0, nusers = 0, ngroups = 0
544
545         if access is not None:
546             flags = access
547             mask |= PR_SF_ALLBITS
548         if groups is not None:
549             ngroups = groups
550             mask |= PR_SF_NGROUPS
551         if users is not None:
552             nusers = users
553             mask |= PR_SF_NGROUPS
554
555         code = ubik_PR_SetFieldsEntry(self.client, 0, id, mask, flags, ngroups, nusers, 0, 0)
556         if code != 0:
557             raise Exception("Unable to set fields: %s" % afs_error_message(code))