Add the nusers field back to PTEntry; it does vary, especially when
[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         ANONYMOUSID
8
9     ctypedef char prname[PR_MAXNAMELEN]
10
11     struct namelist:
12         unsigned int namelist_len
13         prname *namelist_val
14
15     struct prlist:
16         unsigned int prlist_len
17         afs_int32 *prlist_val
18
19     struct idlist:
20         unsigned int idlist_len
21         afs_int32 *idlist_val
22
23     struct prcheckentry:
24         afs_int32 flags
25         afs_int32 id
26         afs_int32 owner
27         afs_int32 creator
28         afs_int32 ngroups
29         afs_int32 nusers
30         afs_int32 count
31
32     int ubik_PR_NameToID(ubik_client *, afs_int32, namelist *, idlist *)
33     int ubik_PR_IDToName(ubik_client *, afs_int32, idlist *, namelist *)
34     int ubik_PR_INewEntry(ubik_client *, afs_int32, char *, afs_int32, afs_int32)
35     int ubik_PR_NewEntry(ubik_client *, afs_int32, char *, afs_int32, afs_int32, afs_int32 *)
36     int ubik_PR_Delete(ubik_client *, afs_int32, afs_int32)
37     int ubik_PR_AddToGroup(ubik_client *, afs_int32, afs_int32, afs_int32)
38     int ubik_PR_RemoveFromGroup(ubik_client *, afs_int32, afs_int32, afs_int32)
39     int ubik_PR_ListElements(ubik_client *, afs_int32, afs_int32, prlist *, afs_int32 *)
40     int ubik_PR_ListOwned(ubik_client *, afs_int32, afs_int32, prlist *, afs_int32 *)
41     int ubik_PR_ListEntry(ubik_client *, afs_int32, afs_int32, prcheckentry *)
42     int ubik_PR_ChangeEntry(ubik_client *, afs_int32, afs_int32, char *, afs_int32, afs_int32)
43     int ubik_PR_IsAMemberOf(ubik_client *, afs_int32, afs_int32, afs_int32, afs_int32 *)
44     int ubik_PR_ListMax(ubik_client *, afs_int32, afs_int32 *, afs_int32 *)
45     int ubik_PR_SetMax(ubik_client *, afs_int32, afs_int32, afs_int32)
46
47 cdef import from "afs/pterror.h":
48     enum:
49         PRNOENT
50         PRTOOMANY
51
52     void initialize_PT_error_table()
53
54 cdef class PTEntry:
55     cdef public afs_int32 flags
56     cdef public afs_int32 id
57     cdef public afs_int32 owner
58     cdef public afs_int32 creator
59     cdef public afs_int32 ngroups
60     cdef public afs_int32 nusers
61     cdef public afs_int32 count
62
63 cdef int _ptentry_from_c(PTEntry p_entry, prcheckentry c_entry) except -1:
64     if p_entry is None:
65         raise TypeError
66         return -1
67
68     p_entry.flags = c_entry.flags
69     p_entry.id = c_entry.id
70     p_entry.owner = c_entry.owner
71     p_entry.creator = c_entry.creator
72     p_entry.ngroups = c_entry.ngroups
73     p_entry.nusers = c_entry.nusers
74     p_entry.count = c_entry.count
75     return 0
76
77 cdef int _ptentry_to_c(prcheckentry * c_entry, PTEntry p_entry) except -1:
78     if p_entry is None:
79         raise TypeError
80         return -1
81
82     c_entry.flags = p_entry.flags
83     c_entry.id = p_entry.id
84     c_entry.owner = p_entry.owner
85     c_entry.creator = p_entry.creator
86     c_entry.ngroups = p_entry.ngroups
87     c_entry.nusers = p_entry.nusers
88     c_entry.count = p_entry.count
89     return 0
90
91 cdef class PTS:
92     """
93     A PTS object is essentially a handle to talk to the server in a
94     given cell.
95
96     cell defaults to None. If no argument is passed for cell, PTS
97     connects to the home cell.
98
99     sec is the security level, an integer from 0 to 3:
100       - 0: unauthenticated connection
101       - 1: try authenticated, then fall back to unauthenticated
102       - 2: fail if an authenticated connection can't be established
103       - 3: same as 2, plus encrypt all traffic to the protection
104         server
105     """
106     cdef ubik_client * client
107
108     def __cinit__(self, cell=None, sec=1):
109         cdef afs_int32 code
110         cdef afsconf_dir *cdir
111         cdef afsconf_cell info
112         cdef char * c_cell
113         cdef ktc_principal prin
114         cdef ktc_token token
115         cdef rx_securityClass *sc
116         cdef rx_connection *serverconns[MAXSERVERS]
117         cdef int i
118
119         initialize_PT_error_table()
120
121         if cell is None:
122             c_cell = NULL
123         else:
124             c_cell = cell
125
126         self.client = NULL
127
128         code = rx_Init(0)
129         if code != 0:
130             raise Exception(code, "Error initializing Rx")
131
132         cdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH)
133         if cdir is NULL:
134             raise OSError(errno,
135                           "Error opening configuration directory (%s): %s" % \
136                               (AFSDIR_CLIENT_ETC_DIRPATH, strerror(errno)))
137         code = afsconf_GetCellInfo(cdir, c_cell, "afsprot", &info)
138         if code != 0:
139             raise Exception(code, "GetCellInfo: %s" % afs_error_message(code))
140
141         if sec > 0:
142             strncpy(prin.cell, info.name, sizeof(prin.cell))
143             prin.instance[0] = 0
144             strncpy(prin.name, "afs", sizeof(prin.name))
145
146             code = ktc_GetToken(&prin, &token, sizeof(token), NULL);
147             if code != 0:
148                 if sec >= 2:
149                     # No really - we wanted authentication
150                     raise Exception(code, "Failed to get token for service AFS: %s" % afs_error_message(code))
151                 sec = 0
152             else:
153                 if sec == 3:
154                     level = rxkad_crypt
155                 else:
156                     level = rxkad_clear
157                 sc = rxkad_NewClientSecurityObject(level, &token.sessionKey,
158                                                    token.kvno, token.ticketLen,
159                                                    token.ticket)
160
161         if sec == 0:
162             sc = rxnull_NewClientSecurityObject()
163         else:
164             sec = 2
165
166         memset(serverconns, 0, sizeof(serverconns))
167         for 0 <= i < info.numServers:
168             serverconns[i] = rx_NewConnection(info.hostAddr[i].sin_addr.s_addr,
169                                               info.hostAddr[i].sin_port,
170                                               PRSRV,
171                                               sc,
172                                               sec)
173
174         code = ubik_ClientInit(serverconns, &self.client)
175         if code != 0:
176             raise Exception("Failed to initialize ubik connection to Protection server: %s" % afs_error_message(code))
177
178         code = rxs_Release(sc)
179
180     def __dealloc__(self):
181         ubik_ClientDestroy(self.client)
182         rx_Finalize()
183
184     def NameToId(self, name):
185         """
186         Converts a user or group to an AFS ID.
187         """
188         cdef namelist lnames
189         cdef idlist lids
190         cdef afs_int32 code, id
191         name = name.lower()
192
193         lids.idlist_len = 0
194         lids.idlist_val = NULL
195         lnames.namelist_len = 1
196         lnames.namelist_val = <prname *>malloc(PR_MAXNAMELEN)
197         strncpy(lnames.namelist_val[0], name, PR_MAXNAMELEN)
198         code = ubik_PR_NameToID(self.client, 0, &lnames, &lids)
199         if lids.idlist_val is not NULL:
200             id = lids.idlist_val[0]
201             free(lids.idlist_val)
202         if id == ANONYMOUSID:
203             code = PRNOENT
204         if code != 0:
205             raise Exception("Failed to lookup PTS name: %s" % afs_error_message(code))
206         return id
207
208     def IdToName(self, id):
209         """
210         Convert an AFS ID to the name of a user or group.
211         """
212         cdef namelist lnames
213         cdef idlist lids
214         cdef afs_int32 code
215         cdef char name[PR_MAXNAMELEN]
216
217         lids.idlist_len = 1
218         lids.idlist_val = <afs_int32 *>malloc(sizeof(afs_int32))
219         lids.idlist_val[0] = id
220         lnames.namelist_len = 0
221         lnames.namelist_val = NULL
222         code = ubik_PR_IDToName(self.client, 0, &lids, &lnames)
223         if lnames.namelist_val is not NULL:
224             strncpy(name, lnames.namelist_val[0], sizeof(name))
225             free(lnames.namelist_val)
226         if lids.idlist_val is not NULL:
227             free(lids.idlist_val)
228         if name == str(id):
229             code = PRNOENT
230         if code != 0:
231             raise Exception("Failed to lookup PTS ID: %s" % afs_error_message(code))
232         return name
233
234     def CreateUser(self, name, id=None):
235         """
236         Create a new user in the protection database. If an ID is
237         provided, that one will be used.
238         """
239         cdef afs_int32 code
240         cdef afs_int32 cid
241         name = name[:PR_MAXNAMELEN].lower()
242
243         if id is not None:
244             cid = id
245
246         if id is not None:
247             code = ubik_PR_INewEntry(self.client, 0, name, cid, 0)
248         else:
249             code = ubik_PR_NewEntry(self.client, 0, name, 0, 0, &cid)
250
251         if code != 0:
252             raise Exception("Failed to create user: %s" % afs_error_message(code))
253         return cid
254
255     def CreateGroup(self, name, owner, id=None):
256         """
257         Create a new group in the protection database. If an ID is
258         provided, that one will be used.
259         """
260         cdef afs_int32 code, cid
261
262         name = name[:PR_MAXNAMELEN].lower()
263         oid = self.NameToId(owner)
264
265         if id is not None:
266             cid = id
267             code = ubik_PR_INewEntry(self.client, 0, name, cid, oid)
268         else:
269             code = ubik_PR_NewEntry(self.client, 0, name, PRGRP, oid, &cid)
270
271         if code != 0:
272             raise Exception("Failed to create group: %s" % afs_error_message(code))
273         return cid
274
275     def Delete(self, id):
276         """
277         Delete the protection database entry with the provided ID.
278         """
279         cdef afs_int32 code
280
281         code = ubik_PR_Delete(self.client, 0, id)
282         if code != 0:
283             raise Exception("Failed to delete user: %s" % afs_error_message(code))
284
285     def AddToGroup(self, uid, gid):
286         """
287         Add the user with the given ID to the group with the given ID.
288         """
289         cdef afs_int32 code
290
291         code = ubik_PR_AddToGroup(self.client, 0, uid, gid)
292         if code != 0:
293             raise Exception("Failed to add user to group: %s" % afs_error_message(code))
294
295     def RemoveFromGroup(self, uid, gid):
296         """
297         Remove the user with the given ID from the group with the given ID.
298         """
299         cdef afs_int32 code
300
301         code = ubik_PR_RemoveFromGroup(self.client, 0, uid, gid)
302         if code != 0:
303             raise Exception("Failed to remove user from group: %s" % afs_error_message(code))
304
305     def ListMembers(self, id):
306         """
307         Get the membership of an entity.
308
309         If id is a group ID, this returns the users that are in that
310         group.
311
312         If id is a user ID, this returns the list of groups that user
313         is on.
314
315         This returns a list of PTS IDs.
316         """
317         cdef afs_int32 code, over
318         cdef prlist alist
319         cdef int i
320         cdef object members = []
321
322         alist.prlist_len = 0
323         alist.prlist_val = NULL
324
325         code = ubik_PR_ListElements(self.client, 0, id, &alist, &over)
326
327         if alist.prlist_val is not NULL:
328             for i in range(alist.prlist_len):
329                 members.append(alist.prlist_val[i])
330             free(alist.prlist_val)
331
332         if over:
333             code = PRTOOMANY
334         if code != 0:
335             raise Exception("Failed to get group membership: %s" % afs_error_message(code))
336
337         return members
338
339     def ListOwned(self, oid):
340         """
341         Get all groups owned by an entity.
342         """
343         cdef afs_int32 code, over
344         cdef prlist alist
345         cdef int i
346         cdef object owned = []
347
348         alist.prlist_len = 0
349         alist.prlist_val = NULL
350
351         code = ubik_PR_ListOwned(self.client, 0, oid, &alist, &over)
352
353         if alist.prlist_val is not NULL:
354             for i in range(alist.prlist_len):
355                 owned.append(alist.prlist_val[i])
356             free(alist.prlist_val)
357
358         if over:
359             code = PRTOOMANY
360         if code != 0:
361             raise Exception("Failed to get owned entities: %s" % afs_error_message(code))
362
363         return owned
364
365     def ListEntry(self, id):
366         """
367         Load a PTEntry instance with information about the provided
368         ID.
369         """
370         cdef afs_int32 code
371         cdef prcheckentry centry
372         cdef object entry = PTEntry()
373
374         code = ubik_PR_ListEntry(self.client, 0, id, &centry)
375         if code != 0:
376             raise Exception("Error getting entity info: %s" % afs_error_message(code))
377
378         _ptentry_from_c(entry, centry)
379         return entry
380
381     def ChangeEntry(self, id, newname=None, newid=None, newoid=None):
382         """
383         Change the name, ID, and/or owner of a PTS entity.
384
385         For any of newname, newid, and newoid which aren't specified
386         or ar None, the value isn't changed.
387         """
388         cdef afs_int32 code
389         cdef afs_int32 c_newid = 0, c_newoid = 0
390         cdef char * c_newname
391
392         if newname is None:
393             newname = self.IdToName(id)
394         c_newname = newname
395         if newid is not None:
396             c_newid = newid
397         if newoid is not None:
398             c_newoid = newoid
399
400         code = ubik_PR_ChangeEntry(self.client, 0, id, c_newname, c_newoid, c_newid)
401         if code != 0:
402             raise Exception("Error changing entity info: %s" % afs_error_message(code))
403
404     def IsAMemberOf(self, uid, gid):
405         """
406         Return True if the given uid is a member of the given gid.
407         """
408         cdef afs_int32 code
409         cdef afs_int32 flag
410
411         code = ubik_PR_IsAMemberOf(self.client, 0, uid, gid, &flag)
412         if code != 0:
413             raise Exception("Error testing membership: %s" % afs_error_message(code))
414
415         return bool(flag)
416
417     def ListMax(self):
418         """
419         Return a tuple of the maximum user ID and the maximum group
420         ID currently assigned.
421         """
422         cdef afs_int32 code, uid, gid
423
424         code = ubik_PR_ListMax(self.client, 0, &uid, &gid)
425         if code != 0:
426             raise Exception("Error looking up max uid/gid: %s" % afs_error_message(code))
427
428         return (uid, gid)
429
430     def SetMaxUserId(self, id):
431         """
432         Set the maximum currently assigned user ID (the next
433         automatically assigned UID will be id + 1)
434         """
435         cdef afs_int32 code
436
437         code = ubik_PR_SetMax(self.client, 0, id, 0)
438         if code != 0:
439             raise Exception("Error setting max uid: %s" % afs_error_message(code))
440
441     def SetMaxGroupId(self, id):
442         """
443         Set the maximum currently assigned user ID (the next
444         automatically assigned UID will be id + 1)
445         """
446         cdef afs_int32 code
447
448         code = ubik_PR_SetMax(self.client, 0, id, PRGRP)
449         if code != 0:
450             raise Exception("Error setting max gid: %s" % afs_error_message(code))