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