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