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