More explicit warning about versions mismatch.
[py-rsbac] / rsbac / acl.py
1 # -*- coding:utf-8 -*-
2
3 # py-rsbac - RSBAC Python bindings
4 # Copyright (C) 2006  Frederic Jolliton <pyrsbac@tuxee.net>
5
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20 __all__ = [ 'Group' , 'groups' ]
21
22 def export( o ) :
23     if not isinstance( o , basestring ) :
24         name = o.__name__
25     else :
26         name , o = o , None
27     if name in __all__ :
28         raise ImportError , 'duplicate name %r in module %r' % ( name , __name__ )
29     __all__.append( name )
30     return o
31
32 from ctypes import byref, pointer
33
34 from rsbac import headers, lib, transaction
35 from rsbac._data import AclRequestVector
36 from rsbac.errors import raiseIfError, Error
37 from rsbac._utils import fetch, intToTtl, ttlToInt, unlimitedTtl, unchangedTtl
38
39 g_aclSyscallArg = headers.rsbac_acl_syscall_arg_t()
40 g_aclSyscallArgRef = byref( g_aclSyscallArg )
41
42 g_aclSyscallByNameArg = headers.rsbac_acl_syscall_n_arg_t()
43 g_aclSyscallByNameArgRef = byref( g_aclSyscallByNameArg )
44
45 # for acl_remove_user
46 g_targetId = headers.rsbac_target_id_t()
47
48 @export
49 def grant( subject , object , requests ) :
50     """Grant 'requests' to 'subject' for the given 'object'.
51
52     subject -- RC Role, User or ACL Group
53     object -- Object
54     requests -- RequestVector
55
56     """
57     rights , ttl = object.acl[ subject ]
58     rights |= AclRequestVector( requests )
59     object.acl[ subject ] = rights , unchangedTtl
60
61 @export
62 def revoke( subject , object , requests ) :
63     """Revoke 'requests' to '' for the given 'type'.
64
65     subject -- RC Role, User or ACL Group
66     object -- Object
67     requests -- RequestVector
68
69     """
70     rights , ttl = object.acl[ subject ]
71     rights &= ~AclRequestVector( requests )
72     object.acl[ subject ] = rights , unchangedTtl
73
74 def _acl_preset( target , tid , subjectType , subjectId , rights , ttl ) :
75     g_aclSyscallArg.target = target
76     g_aclSyscallArg.tid = tid
77     g_aclSyscallArg.subj_type = subjectType
78     g_aclSyscallArg.subj_id = subjectId
79     g_aclSyscallArg.rights = rights
80     g_aclSyscallArg.ttl = ttl
81     return g_aclSyscallArgRef
82
83 def _acl_preset_n( target , name , subjectType , subjectId , rights , ttl ) :
84     g_aclSyscallByNameArg.target = target
85     g_aclSyscallByNameArg.name = name
86     g_aclSyscallByNameArg.subj_type = subjectType
87     g_aclSyscallByNameArg.subj_id = subjectId
88     g_aclSyscallByNameArg.rights = rights
89     g_aclSyscallByNameArg.ttl = ttl
90     return g_aclSyscallByNameArgRef
91
92 def _to_acl_subject( object ) :
93     from rsbac.objects import User
94     if isinstance( object , Role ) :
95         return headers.ACLS_ROLE , object._role
96     elif isinstance( object , User ) :
97         return headers.ACLS_USER , object.uid
98     elif isinstance( object , Group ) :
99         return headers.ACLS_GROUP , object._id
100     else :
101         raise RuntimeError , 'unexpected ACL subject of type %r' % ( object.__class__ , )
102
103 def _from_acl_subject( type , id ) :
104     if type == headers.ACLS_USER :
105         return User( id )
106     elif type == headers.ACLS_ROLE :
107         return Role( id )
108     elif type == headers.ACLS_GROUP :
109         return Group( id )
110     else :
111         raise RuntimeError , 'unexpected subject type %r' % ( type , )
112
113 #
114 # set_acl_entry
115 #
116
117 def setAclEntryById( target , subject , rights , ttl = unlimitedTtl ) :
118     st , si = _to_acl_subject( subject )
119     raiseIfError( lib.rsbac_acl( transaction._t ,
120                                  headers.ACLC_set_acl_entry ,
121                                  _acl_preset( target.type , target._id._obj ,
122                                               st , si ,
123                                               int( rights ) , ttlToInt( ttl ) ) ) )
124
125 def setAclEntryByName( target , subject , rights , ttl = unlimitedTtl ) :
126     st , si = _to_acl_subject( subject )
127     raiseIfError( lib.rsbac_acl_n( transaction._t ,
128                                    headers.ACLC_set_acl_entry ,
129                                    _acl_preset_n( target.type , target._id ,
130                                                   st , si ,
131                                                   int( rights ) , ttlToInt( ttl ) ) ) )
132
133 #
134 # remove_acl_entry
135 #
136
137 def removeAclEntryById( target , subject ) :
138     st , si = _to_acl_subject( subject )
139     raiseIfError( lib.rsbac_acl( transaction._t ,
140                                  headers.ACLC_remove_acl_entry ,
141                                  _acl_preset( target.type , target._id._obj ,
142                                               st , si ,
143                                               0 , 0 ) ) )
144
145 def removeAclEntryByName( target , subject ) :
146     st , si = _to_acl_subject( subject )
147     raiseIfError( lib.rsbac_acl_n( transaction._t ,
148                                    headers.ACLC_remove_acl_entry ,
149                                    _acl_preset_n( target.type , target._id ,
150                                                   st , si ,
151                                                   0 , 0 ) ) )
152
153 #
154 # remove_acl
155 #
156
157 def removeAclById( target ) :
158     raiseIfError( lib.rsbac_acl( transaction._t ,
159                                  headers.ACLC_remove_acl ,
160                                  _acl_preset( target.type , target._id._obj ,
161                                               0 , 0 , 0 , 0 ) ) )
162
163 def removeAclByName( target ) :
164     raiseIfError( lib.rsbac_acl_n( transaction._t ,
165                                    headers.ACLC_remove_acl ,
166                                    _acl_preset_n( target.type , target._id ,
167                                                   0 , 0 , 0 , 0 ) ) )
168
169 # add_to_acl_entry
170 # remove_from_acl_entry
171
172 #
173 # set_mask
174 #
175
176 def setAclMaskById( target , mask ) :
177     raiseIfError( lib.rsbac_acl( transaction._t ,
178                                  headers.ACLC_remove_acl ,
179                                  _acl_preset( target.type , target._id._obj ,
180                                               0 , 0 ,
181                                               mask , 0 ) ) )
182
183 def setAclMaskByName( target , mask ) :
184     raiseIfError( lib.rsbac_acl_n( transaction._t ,
185                                    headers.ACLC_remove_acl ,
186                                    _acl_preset_n( target.type , target._id ,
187                                                   0 , 0 ,
188                                                   mask , 0 ) ) )
189
190 #
191 # remove_user
192 #
193
194 @export
195 def aclRemoveUser( user ) :
196     """Remove user from all groups and all ACLs.
197
198     user -- UID as integer
199
200     """
201     g_targetId.user = user
202     raiseIfError( lib.rsbac_acl( transaction._t ,
203                                  headers.ACLC_remove_user ,
204                                  _acl_preset( headers.T_USER , g_targetId ,
205                                               0 , 0 , 0 , 0 ) ) )
206
207 #
208 # get_acl_rights
209 #
210
211 def getAclRightsById( target , subject , effective = False ) :
212     st , si = _to_acl_subject( subject )
213     rights = headers.rsbac_acl_rights_vector_t()
214     raiseIfError( lib.rsbac_acl_get_rights( transaction._t ,
215                                             _acl_preset( target.type , target._id._obj ,
216                                                          st , si ,
217                                                          0 , 0 ) ,
218                                             byref( rights ) ,
219                                             effective ) )
220     return AclRequestVector( rights.value )
221
222 def getAclRightsByName( target , subject , effective = False ) :
223     st , si = _to_acl_subject( subject )
224     rights = headers.rsbac_acl_rights_vector_t()
225     raiseIfError( lib.rsbac_acl_get_rights_n( transaction._t ,
226                                               _acl_preset_n( target.type , target._id ,
227                                                              st , si ,
228                                                              0 , 0 ) ,
229                                               byref( rights ) ,
230                                               effective ) )
231     return AclRequestVector( rights.value )
232
233 #
234 # acl_get_tlist
235 #
236
237 def _unpackAclTlist( entries , ttls ) :
238     return [ ( _from_acl_subject( entry.subj_type , entry.subj_id ) ,
239                AclRequestVector( entry.rights ) ,
240                intToTtl( ttl ) )
241              for entry , ttl in zip( entries , ttls ) ]
242
243 def getAclTargetListById( target ) :
244     arrs = fetch( ( headers.rsbac_acl_entry_t ,
245                     headers.rsbac_time_t ) ,
246                   lambda n , a , b : lib.rsbac_acl_get_tlist( transaction._t ,
247                                                               target.type ,
248                                                               target._id._obj ,
249                                                               a , b , n ) ,
250                   32 )
251     return _unpackAclTlist( *arrs )
252
253 def getAclTargetListByName( target ) :
254     arrs = fetch( ( headers.rsbac_acl_entry_t ,
255                     headers.rsbac_time_t ) ,
256                   lambda n , a , b : lib.rsbac_acl_get_tlist_n( transaction._t ,
257                                                                 target.type ,
258                                                                 target._id ,
259                                                                 a , b , n ) ,
260                   32 )
261     return _unpackAclTlist( *arrs )
262
263 #
264 # acl_get_mask
265 #
266
267 def getAclMaskById( target ) :
268     rights = headers.rsbac_acl_rights_vector_t()
269     raiseIfError( lib.rsbac_acl_get_mask( transaction._t ,
270                                           target.type , target._id._obj ,
271                                           byref( rights ) ) )
272     return AclRequestVector( rights.value )
273
274 def getAclMaskByName( target ) :
275     rights = headers.rsbac_acl_rights_vector_t()
276     raiseIfError( lib.rsbac_acl_get_mask_n( transaction._t ,
277                                             target.type , target._id ,
278                                             byref( rights ) ) )
279     return AclRequestVector( rights.value )
280
281 #
282 # dispatchers
283 #
284
285 @export
286 def setAclEntry( target , subject , rights , ttl = unlimitedTtl ) :
287     """Set an ACL entry for a target-subject couple.
288
289     target -- Object
290     subject -- either an instance of objects.User, acl.Group or rc.Role.
291     rights -- AclRequestVector
292     ttl -- TTL
293
294     """
295     if target._byName :
296         return setAclEntryByName( target , subject , rights , ttl )
297     else :
298         return setAclEntryById( target , subject , rights , ttl )
299
300 @export
301 def removeAclEntry( target , subject ) :
302     """Remove an ACL entry.
303
304     target -- Object
305     subject -- either an instance of objects.User, acl.Group or rc.Role.
306
307     """
308     if target._byName :
309         return removeAclEntryByName( target , subject )
310     else :
311         return removeAclEntryById( target , subject )
312
313 @export
314 def removeAcl( target ) :
315     """Remove all ACL entries on a target.
316
317     target -- Object
318
319     """
320     if target._byName :
321         return removeAclByName( target )
322     else :
323         return removeAclById( target )
324
325 @export
326 def setAclMask( target , mask ) :
327     """Set ACL mask on a target.
328
329     target -- Object
330     mask -- AclRequestVector
331
332     """
333     if target._byName :
334         return setAclMaskByName( target , mask )
335     else :
336         return setAclMaskById( target , mask )
337
338 @export
339 def getAclRights( target , subject , effective = False ) :
340     """Get ACL rights for a target-subject couple.
341
342     target -- Object
343     subject -- either an instance of objects.User, acl.Group or rc.Role.
344     effective -- True to retrieve effective rights, False otherwise.
345
346     Returns an AclRequestVector.
347
348     """
349     if target._byName :
350         return getAclRightsByName( target , subject , effective )
351     else :
352         return getAclRightsById( target , subject , effective )
353
354 @export
355 def getAclMask( target ) :
356     """Get ACL mask for a target.
357
358     target -- Object
359
360     Returns an AclRequestVector.
361
362     """
363     if target._byName :
364         return getAclMaskByName( target , mask )
365     else :
366         return getAclMaskById( target , mask )
367
368 @export
369 def getAclTargetList( target ) :
370     """Get all ACL entries for a target.
371
372     target -- Object
373
374     Returns a list of 3-tuple (Object, AclRequestVector, ttl).
375
376     """
377     if target._byName :
378         return getAclTargetListByName( target )
379     else :
380         return getAclTargetListById( target )
381
382 # dict interface
383 class AclBase( object ) :
384     __slots__ = ( 'target' , )
385     def __init__( self , target ) :
386         object.__init__( self )
387         self.target = target
388     def __contains__( self , subject ) :
389         return self.has_key( subject )
390     def __len__( self ) :
391         return len( self._list() )
392     def __iter__( self ) :
393         return iter( self.keys() )
394     def __repr__( self ) :
395         return '<ACL for %s: %d entries>' % ( repr( self.target ).strip( '<>' ) , len( self ) )
396     def has_key( self , subject ) :
397         return subject in self.keys()
398     def keys( self ) :
399         return [ e[ 0 ] for e in self._list() ]
400     def values( self ) :
401         return [ ( e[ 1 ] , e[ 2 ] ) for e in self._list() ]
402     def items( self ) :
403         return [ ( e[ 0 ] , ( e[ 1 ] , e[ 2 ] ) ) for e in self._list() ]
404
405 class AclById( AclBase ) :
406     __slots__ = ()
407     def _list( self ) :
408         return getAclTargetListById( self.target )
409     def __getitem__( self , subject ) :
410         # FIXME: temporary solution. Waiting for an O(1) way to get
411         # (right,TTL) couple.
412         for k , v in self.items() :
413             if k == subject :
414                 return v
415         return getAclRightsById( self.target , subject ) , False
416     def __setitem__( self , subject , value ) :
417         if not isinstance( value , tuple ) :
418             value = value , unlimitedTtl
419         rights , ttl = value
420         setAclEntryById( self.target , subject , rights , ttl )
421     def __delitem__( self , subject ) :
422         removeAclEntryById( self.target , subject )
423     # def update( self ) : pass
424     def clear( self ) :
425         # FIXME: temporary solution. Should sys_rsbac_remove_acl
426         # handle T_PROCESS instead?
427         if isinstance( self.target , Process ) :
428             map( self.__delitem__ , self )
429         else :
430             removeAclById( self.target )
431
432 class AclByName( AclBase ) :
433     __slots__ = ()
434     def _list( self ) :
435         return getAclTargetListByName( self.target )
436     def __iter__( self ) :
437         return ( n[ 0 ] for n in self.__list() )
438     def __getitem__( self , subject ) :
439         # FIXME: temporary solution. Waiting for an O(1) way to get
440         # (right,TTL) couple.
441         for k , v in self.items() :
442             if k == subject :
443                 return v
444         return getAclRightsByName( self.target , subject ) , False
445     def __setitem__( self , subject , value ) :
446         if not isinstance( value , tuple ) :
447             value = value , unlimitedTtl
448         rights , ttl = value
449         setAclEntryByName( self.target , subject , rights , ttl )
450     def __delitem__( self , subject ) :
451         removeAclEntryByName( self.target , subject )
452     def clear( self ) :
453         removeAclByName( self.target )
454
455 class EffectiveAclById( AclBase ) :
456     __slots__ = ()
457     def _list( self ) :
458         return getAclTargetListById( self.target )
459     def __iter__( self ) :
460         return ( n[ 0 ] for n in self.__list() )
461     def __getitem__( self , subject ) :
462         return getAclRightsById( self.target , subject , True ) , False
463
464 class EffectiveAclByName( AclBase ) :
465     __slots__ = ()
466     def _list( self ) :
467         return getAclTargetListByName( self.target )
468     def __iter__( self ) :
469         return ( n[ 0 ] for n in self.__list() )
470     def __getitem__( self , subject ) :
471         return getAclRightsByName( self.target , subject , True ) , False
472
473 #--[ Group ]------------------------------------------------------------------
474
475 @export
476 def addGroup( name , type = headers.ACLG_PRIVATE , id = None ) :
477     """Add an ACL group.
478
479     type -- type of group (either ACLG_GLOBAL or ACLG_PRIVATE)
480     name -- group name as string
481     id -- a positive integer or None (automatic ID)
482
483     Returns the ID of the new group.
484
485     """
486     if id is None :
487         id = 0
488     id_ = headers.rsbac_acl_group_id_t()
489     id_.value = id
490     arg = headers.rsbac_acl_group_syscall_arg_t()
491     arg.add_group.type = type
492     arg.add_group.name = name
493     arg.add_group.group_id_p = pointer( id_ )
494     raiseIfError( lib.rsbac_acl_group( transaction._t ,
495                                        headers.ACLGS_add_group ,
496                                        byref( arg ) ) )
497     return int( id_.value )
498
499 @export
500 def addGlobalGroup( name , id = None ) :
501     """Add an global ACL group.
502
503     name -- group name as string
504     id -- a positive integer or None (automatic ID)
505
506     Returns the ID of the new group.
507
508     """
509     return addGroup( name , headers.ACLG_GLOBAL , id )
510
511 @export
512 def addPrivateGroup( name , id = None ) :
513     """Add an private ACL group.
514
515     name -- group name as string
516     id -- a positive integer or None (automatic ID)
517
518     Returns the ID of the new group.
519
520     """
521     return addGroup( name , headers.ACLG_PRIVATE , id )
522
523 @export
524 def changeGroup( id , owner , type , name ) :
525     """Change ACL group attributes.
526
527     Change the owner, type and name of an existing ACL group.
528
529     id -- ACL group as integer
530     owner -- user as integer
531     type -- either headers.ACLG_PRIVATE or headers.ACLG_GLOBAL
532     name -- group name
533
534     """
535     arg = headers.rsbac_acl_group_syscall_arg_t()
536     arg.change_group.id = id
537     arg.change_group.owner = owner
538     arg.change_group.type = type
539     arg.change_group.name = name
540     raiseIfError( lib.rsbac_acl_group( transaction._t ,
541                                        headers.ACLGS_change_group ,
542                                        byref( arg ) ) )
543
544 @export
545 def removeGroup( id ) :
546     """Remove an ACL group.
547
548     id -- ACL group as integer
549
550     """
551     arg = headers.rsbac_acl_group_syscall_arg_t()
552     arg.remove_group.id = id
553     raiseIfError( lib.rsbac_acl_group( transaction._t ,
554                                        headers.ACLGS_remove_group ,
555                                        byref( arg ) ) )
556
557 def _unpackEntry( entry ) :
558     return int( entry.id ) , int( entry.owner ) , int( entry.type ) , entry.name
559
560 @export
561 def getGroupEntry( id ) :
562     """Get group entry attributes.
563
564     id -- ACL group as integer
565
566     Returns a tuple with 4 attributes: id, owner, type and name.
567
568     """
569     entry = headers.rsbac_acl_group_entry_t()
570     arg = headers.rsbac_acl_group_syscall_arg_t()
571     arg.get_group_entry.id = id
572     arg.get_group_entry.entry_p = pointer( entry )
573     raiseIfError( lib.rsbac_acl_group( transaction._t ,
574                                        headers.ACLGS_get_group_entry ,
575                                        byref( arg ) ) )
576     return _unpackEntry( entry )
577
578 def _listGroups( includeGlobal , arr , n ) :
579     arg = headers.rsbac_acl_group_syscall_arg_t()
580     arg.list_groups.include_global = includeGlobal
581     arg.list_groups.group_entry_array = arr
582     arg.list_groups.maxnum = n
583     return raiseIfError( lib.rsbac_acl_group( transaction._t ,
584                                               headers.ACLGS_list_groups ,
585                                               byref( arg ) ) )
586
587 # FIXME: Other groups may be viewed/changed while not listed by
588 # _listGroups (the groups belonging to other users.)
589 @export
590 def getGroupList( includeGlobal = False ) :
591     """Get the list of ACL groups.
592
593     includeGlobal -- boolean. If True, include global groups.
594
595     Returns list of group ID.
596
597     """
598     arr = fetch( headers.rsbac_acl_group_entry_t ,
599                  lambda n , a : _listGroups( includeGlobal , a , n ) ,
600                  32 )
601     return map( _unpackEntry , arr )
602
603 @export
604 def addGroupMember( group , user , ttl = unlimitedTtl ) :
605     """Add a member to an ACL group.
606
607     group -- ACL group as integer
608     user -- user as integer
609     ttl -- TTL
610
611     """
612     arg = headers.rsbac_acl_group_syscall_arg_t()
613     arg.add_member.group = int( group )
614     arg.add_member.user = int( user )
615     arg.add_member.ttl = ttlToInt( ttl )
616     return raiseIfError( lib.rsbac_acl_group( transaction._t ,
617                                               headers.ACLGS_add_member ,
618                                               byref( arg ) ) )
619
620 @export
621 def removeGroupMember( group , user ) :
622     """Remove a member from an ACL group.
623
624     group -- ACL group as integer
625     user -- user as integer
626
627     """
628     arg = headers.rsbac_acl_group_syscall_arg_t()
629     arg.remove_member.group = int( group )
630     arg.remove_member.user = int( user )
631     return raiseIfError( lib.rsbac_acl_group( transaction._t ,
632                                               headers.ACLGS_remove_member ,
633                                               byref( arg ) ) )
634
635 def _getUserGroups( user , groups , ttls , n ) :
636     arg = headers.rsbac_acl_group_syscall_arg_t()
637     arg.get_user_groups.user = user
638     arg.get_user_groups.group_array = groups
639     arg.get_user_groups.ttl_array = ttls
640     arg.get_user_groups.maxnum = n
641     return raiseIfError( lib.rsbac_acl_group( transaction._t ,
642                                               headers.ACLGS_get_user_groups ,
643                                               byref( arg ) ) )
644
645 @export
646 def getUserGroups( uid ) :
647     """Get the ACL groups to which a user belong.
648
649     uid -- user as integer
650
651     Returns a list of pair (id,ttl).
652
653     """
654     arrs = fetch( ( headers.rsbac_acl_group_id_t ,
655                     headers.rsbac_time_t ) ,
656                   lambda n , a , b : _getUserGroups( uid , a , b , n ) ,
657                   32 )
658     return [ ( int( a ) , int( b ) ) for a , b in zip( *arrs ) ]
659
660 def _getGroupMembers( group , users , ttls , n ) :
661     arg = headers.rsbac_acl_group_syscall_arg_t()
662     arg.get_group_members.group = group
663     arg.get_group_members.user_array = users
664     arg.get_group_members.ttl_array = ttls
665     arg.get_group_members.maxnum = n
666     return raiseIfError( lib.rsbac_acl_group( transaction._t ,
667                                               headers.ACLGS_get_group_members ,
668                                               byref( arg ) ) )
669
670 @export
671 def getGroupMembers( id ) :
672     """Get the members of the given ACL group.
673
674     id -- ACL group
675
676     Returns a list of pair (uid,ttl).
677
678     """
679     arrs = fetch( ( headers.rsbac_uid_t ,
680                     headers.rsbac_time_t ) ,
681                   lambda n , a , b : _getGroupMembers( id , a , b , n ) ,
682                   32 )
683     return [ ( int( a ) , intToTtl( b ) ) for a , b in zip( *arrs ) ]
684
685 # dict/set interface
686 class AclGroupMembersTtlDictProxy( object ) :
687     __slots__ = ( '__group' , )
688     def __init__( self , id ) :
689         self.__group = id
690     def __contains__( self , user ) :
691         return self.has_key( user )
692     def __getitem__( self , user ) :
693         for uid , ttl in getGroupMembers( self.__group ) :
694             if uid == user :
695                 return ttl
696         return False
697     def __setitem__( self , user , value ) :
698         addGroupMember( self.__group , user , value )
699     def __delitem__( self , user ) :
700         self.discard( user )
701     def __len__( self ) :
702         return len( getGroupMembers( self.__group ) )
703     def __iter__( self ) :
704         return ( n[ 0 ] for n in getGroupMembers( self.__group ) )
705     def __repr__( self ) :
706         r = []
707         for user , ttl in self.items() :
708             if ttl is True :
709                 r.append( str( user ) )
710             else :
711                 r.append( '%s(%ds)' % ( user , ttl ) )
712         return '<Members for ACL group %d: %s>' \
713             % ( self.__group , ', '.join( r ) or 'none' )
714     def has_key( self , user ) :
715         return user in self.keys()
716     def add( self , user , ttl = unlimitedTtl ) :
717         addGroupMember( self.__group , user , ttl )
718     def discard( self , user ) :
719         removeGroupMember( self.__group , user )
720     def clear( self ) :
721         map( self.discard , self )
722     def update( self , other ) :
723         for user , ttl in other.items() :
724             self[ user ] = ttl
725     def keys( self ) :
726         return list( self )
727     def items( self ) :
728         return getGroupMembers( self.__group )
729
730 # FIXME: Add a note about settings (not settable individually
731 # normally)
732 class Group( object ) :
733     __slots__ = ( '_id' , 'members' )
734     def __init__( self , id ) :
735         self._id = id
736         self.members = AclGroupMembersTtlDictProxy( id )
737     def __repr__( self ) :
738         try :
739             entry = getGroupEntry( self._id )
740             assert entry[ 2 ] in ( headers.ACLG_GLOBAL , headers.ACLG_PRIVATE )
741             desc = '%r %s' % \
742                 ( entry[ 3 ] ,
743                   ( 'PRIVATE' , 'GLOBAL' )[ entry[ 2 ] == headers.ACLG_GLOBAL ] )
744         except Error , e :
745             if e[ 0 ] != headers.RSBAC_ENOTFOUND :
746                 raise
747             desc = 'undefined'
748         return '<ACL Group [%d] %s>' % ( self._id , desc )
749     #
750     # owner
751     #
752     def getOwner( self ) :
753         return User( getGroupEntry( self._id )[ 1 ] )
754     def setOwner( self , value ) :
755         entry = getGroupEntry( self._id )
756         changeGroup( self._id , int( value ) , entry[ 2 ] , entry[ 3 ] )
757     owner = property( getOwner , setOwner )
758     #
759     # private (not really a boolean, but let's say it is)
760     #
761     def getPrivate( self ) :
762         entry = getGroupEntry( self._id )
763         type = entry[ 2 ]
764         assert type in ( headers.ACLG_GLOBAL , headers.ACLG_PRIVATE )
765         return type == headers.ACLG_PRIVATE
766     def setPrivate( self , value ) :
767         entry = getGroupEntry( self._id )
768         if value :
769             value = headers.ACLG_PRIVATE
770         else :
771             value = headers.ACLG_GLOBAL
772         changeGroup( self._id , entry[ 1 ] , value , entry[ 3 ] )
773     private = property( getPrivate , setPrivate )
774     #
775     # name
776     #
777     def getName( self ) :
778         return getGroupEntry( self._id )[ 3 ]
779     def setName( self , value ) :
780         entry = getGroupEntry( self._id )
781         changeGroup( self._id , entry[ 1 ] , entry[ 2 ] , value )
782     name = property( getName , setName )
783
784 # dict interface
785 class GroupDict( object ) :
786     __slots__ = ()
787     def __iter__( self ) :
788         return ( item[ 0 ] for item in getGroupList( True ) )
789     def __len__( self ) :
790         return len( self.keys() )
791     def __getitem__( self , id ) :
792         return Group( id )
793     def __delitem__( self , id ) :
794         return removeGroup( id )
795     def __repr__( self ) :
796         return '{' + ', '.join( '%s: %s' % ( a , b ) for a , b in self.items() ) + '}'
797     def keys( self ) :
798         return list( self )
799     def values( self ) :
800         return map( Group , self )
801     def items( self ) :
802         return [ ( group , Group( group ) ) for group in self ]
803
804 groups = GroupDict()
805
806 from rsbac.rc import Role
807 from rsbac.objects import User, Process
808
809 # Local Variables:
810 # indent-tabs-mode: nil
811 # python-indent: 4
812 # End: