3 # py-rsbac - RSBAC Python bindings
4 # Copyright (C) 2006 Frederic Jolliton <pyrsbac@tuxee.net>
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.
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.
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
20 __all__ = [ 'Group' , 'groups' ]
23 if not isinstance( o , basestring ) :
28 raise ImportError , 'duplicate name %r in module %r' % ( name , __name__ )
29 __all__.append( name )
32 from ctypes import byref, pointer
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
39 g_aclSyscallArg = headers.rsbac_acl_syscall_arg_t()
40 g_aclSyscallArgRef = byref( g_aclSyscallArg )
42 g_aclSyscallByNameArg = headers.rsbac_acl_syscall_n_arg_t()
43 g_aclSyscallByNameArgRef = byref( g_aclSyscallByNameArg )
46 g_targetId = headers.rsbac_target_id_t()
49 def grant( subject , object , requests ) :
50 """Grant 'requests' to 'subject' for the given 'object'.
52 subject -- RC Role, User or ACL Group
54 requests -- RequestVector
57 rights , ttl = object.acl[ subject ]
58 rights |= AclRequestVector( requests )
59 object.acl[ subject ] = rights , unchangedTtl
62 def revoke( subject , object , requests ) :
63 """Revoke 'requests' to '' for the given 'type'.
65 subject -- RC Role, User or ACL Group
67 requests -- RequestVector
70 rights , ttl = object.acl[ subject ]
71 rights &= ~AclRequestVector( requests )
72 object.acl[ subject ] = rights , unchangedTtl
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
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
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
101 raise RuntimeError , 'unexpected ACL subject of type %r' % ( object.__class__ , )
103 def _from_acl_subject( type , id ) :
104 if type == headers.ACLS_USER :
106 elif type == headers.ACLS_ROLE :
108 elif type == headers.ACLS_GROUP :
111 raise RuntimeError , 'unexpected subject type %r' % ( type , )
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 ,
123 int( rights ) , ttlToInt( ttl ) ) ) )
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 ,
131 int( rights ) , ttlToInt( ttl ) ) ) )
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 ,
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 ,
157 def removeAclById( target ) :
158 raiseIfError( lib.rsbac_acl( transaction._t ,
159 headers.ACLC_remove_acl ,
160 _acl_preset( target.type , target._id._obj ,
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 ,
170 # remove_from_acl_entry
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 ,
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 ,
195 def aclRemoveUser( user ) :
196 """Remove user from all groups and all ACLs.
198 user -- UID as integer
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 ,
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 ,
220 return AclRequestVector( rights.value )
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 ,
231 return AclRequestVector( rights.value )
237 def _unpackAclTlist( entries , ttls ) :
238 return [ ( _from_acl_subject( entry.subj_type , entry.subj_id ) ,
239 AclRequestVector( entry.rights ) ,
241 for entry , ttl in zip( entries , ttls ) ]
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 ,
251 return _unpackAclTlist( *arrs )
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 ,
261 return _unpackAclTlist( *arrs )
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 ,
272 return AclRequestVector( rights.value )
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 ,
279 return AclRequestVector( rights.value )
286 def setAclEntry( target , subject , rights , ttl = unlimitedTtl ) :
287 """Set an ACL entry for a target-subject couple.
290 subject -- either an instance of objects.User, acl.Group or rc.Role.
291 rights -- AclRequestVector
296 return setAclEntryByName( target , subject , rights , ttl )
298 return setAclEntryById( target , subject , rights , ttl )
301 def removeAclEntry( target , subject ) :
302 """Remove an ACL entry.
305 subject -- either an instance of objects.User, acl.Group or rc.Role.
309 return removeAclEntryByName( target , subject )
311 return removeAclEntryById( target , subject )
314 def removeAcl( target ) :
315 """Remove all ACL entries on a target.
321 return removeAclByName( target )
323 return removeAclById( target )
326 def setAclMask( target , mask ) :
327 """Set ACL mask on a target.
330 mask -- AclRequestVector
334 return setAclMaskByName( target , mask )
336 return setAclMaskById( target , mask )
339 def getAclRights( target , subject , effective = False ) :
340 """Get ACL rights for a target-subject couple.
343 subject -- either an instance of objects.User, acl.Group or rc.Role.
344 effective -- True to retrieve effective rights, False otherwise.
346 Returns an AclRequestVector.
350 return getAclRightsByName( target , subject , effective )
352 return getAclRightsById( target , subject , effective )
355 def getAclMask( target ) :
356 """Get ACL mask for a target.
360 Returns an AclRequestVector.
364 return getAclMaskByName( target , mask )
366 return getAclMaskById( target , mask )
369 def getAclTargetList( target ) :
370 """Get all ACL entries for a target.
374 Returns a list of 3-tuple (Object, AclRequestVector, ttl).
378 return getAclTargetListByName( target )
380 return getAclTargetListById( target )
383 class AclBase( object ) :
384 __slots__ = ( 'target' , )
385 def __init__( self , target ) :
386 object.__init__( self )
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()
399 return [ e[ 0 ] for e in self._list() ]
401 return [ ( e[ 1 ] , e[ 2 ] ) for e in self._list() ]
403 return [ ( e[ 0 ] , ( e[ 1 ] , e[ 2 ] ) ) for e in self._list() ]
405 class AclById( AclBase ) :
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() :
415 return getAclRightsById( self.target , subject ) , False
416 def __setitem__( self , subject , value ) :
417 if not isinstance( value , tuple ) :
418 value = value , unlimitedTtl
420 setAclEntryById( self.target , subject , rights , ttl )
421 def __delitem__( self , subject ) :
422 removeAclEntryById( self.target , subject )
423 # def update( self ) : pass
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 )
430 removeAclById( self.target )
432 class AclByName( AclBase ) :
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() :
444 return getAclRightsByName( self.target , subject ) , False
445 def __setitem__( self , subject , value ) :
446 if not isinstance( value , tuple ) :
447 value = value , unlimitedTtl
449 setAclEntryByName( self.target , subject , rights , ttl )
450 def __delitem__( self , subject ) :
451 removeAclEntryByName( self.target , subject )
453 removeAclByName( self.target )
455 class EffectiveAclById( AclBase ) :
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
464 class EffectiveAclByName( AclBase ) :
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
473 #--[ Group ]------------------------------------------------------------------
476 def addGroup( name , type = headers.ACLG_PRIVATE , id = None ) :
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)
483 Returns the ID of the new group.
488 id_ = headers.rsbac_acl_group_id_t()
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 ,
497 return int( id_.value )
500 def addGlobalGroup( name , id = None ) :
501 """Add an global ACL group.
503 name -- group name as string
504 id -- a positive integer or None (automatic ID)
506 Returns the ID of the new group.
509 return addGroup( name , headers.ACLG_GLOBAL , id )
512 def addPrivateGroup( name , id = None ) :
513 """Add an private ACL group.
515 name -- group name as string
516 id -- a positive integer or None (automatic ID)
518 Returns the ID of the new group.
521 return addGroup( name , headers.ACLG_PRIVATE , id )
524 def changeGroup( id , owner , type , name ) :
525 """Change ACL group attributes.
527 Change the owner, type and name of an existing ACL group.
529 id -- ACL group as integer
530 owner -- user as integer
531 type -- either headers.ACLG_PRIVATE or headers.ACLG_GLOBAL
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 ,
545 def removeGroup( id ) :
546 """Remove an ACL group.
548 id -- ACL group as integer
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 ,
557 def _unpackEntry( entry ) :
558 return int( entry.id ) , int( entry.owner ) , int( entry.type ) , entry.name
561 def getGroupEntry( id ) :
562 """Get group entry attributes.
564 id -- ACL group as integer
566 Returns a tuple with 4 attributes: id, owner, type and name.
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 ,
576 return _unpackEntry( entry )
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 ,
587 # FIXME: Other groups may be viewed/changed while not listed by
588 # _listGroups (the groups belonging to other users.)
590 def getGroupList( includeGlobal = False ) :
591 """Get the list of ACL groups.
593 includeGlobal -- boolean. If True, include global groups.
595 Returns list of group ID.
598 arr = fetch( headers.rsbac_acl_group_entry_t ,
599 lambda n , a : _listGroups( includeGlobal , a , n ) ,
601 return map( _unpackEntry , arr )
604 def addGroupMember( group , user , ttl = unlimitedTtl ) :
605 """Add a member to an ACL group.
607 group -- ACL group as integer
608 user -- user as integer
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 ,
621 def removeGroupMember( group , user ) :
622 """Remove a member from an ACL group.
624 group -- ACL group as integer
625 user -- user as integer
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 ,
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 ,
646 def getUserGroups( uid ) :
647 """Get the ACL groups to which a user belong.
649 uid -- user as integer
651 Returns a list of pair (id,ttl).
654 arrs = fetch( ( headers.rsbac_acl_group_id_t ,
655 headers.rsbac_time_t ) ,
656 lambda n , a , b : _getUserGroups( uid , a , b , n ) ,
658 return [ ( int( a ) , int( b ) ) for a , b in zip( *arrs ) ]
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 ,
671 def getGroupMembers( id ) :
672 """Get the members of the given ACL group.
676 Returns a list of pair (uid,ttl).
679 arrs = fetch( ( headers.rsbac_uid_t ,
680 headers.rsbac_time_t ) ,
681 lambda n , a , b : _getGroupMembers( id , a , b , n ) ,
683 return [ ( int( a ) , intToTtl( b ) ) for a , b in zip( *arrs ) ]
686 class AclGroupMembersTtlDictProxy( object ) :
687 __slots__ = ( '__group' , )
688 def __init__( self , 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 ) :
697 def __setitem__( self , user , value ) :
698 addGroupMember( self.__group , user , value )
699 def __delitem__( self , 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 ) :
707 for user , ttl in self.items() :
709 r.append( str( user ) )
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 )
721 map( self.discard , self )
722 def update( self , other ) :
723 for user , ttl in other.items() :
728 return getGroupMembers( self.__group )
730 # FIXME: Add a note about settings (not settable individually
732 class Group( object ) :
733 __slots__ = ( '_id' , 'members' )
734 def __init__( self , id ) :
736 self.members = AclGroupMembersTtlDictProxy( id )
737 def __repr__( self ) :
739 entry = getGroupEntry( self._id )
740 assert entry[ 2 ] in ( headers.ACLG_GLOBAL , headers.ACLG_PRIVATE )
743 ( 'PRIVATE' , 'GLOBAL' )[ entry[ 2 ] == headers.ACLG_GLOBAL ] )
745 if e[ 0 ] != headers.RSBAC_ENOTFOUND :
748 return '<ACL Group [%d] %s>' % ( self._id , desc )
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 )
759 # private (not really a boolean, but let's say it is)
761 def getPrivate( self ) :
762 entry = getGroupEntry( self._id )
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 )
769 value = headers.ACLG_PRIVATE
771 value = headers.ACLG_GLOBAL
772 changeGroup( self._id , entry[ 1 ] , value , entry[ 3 ] )
773 private = property( getPrivate , setPrivate )
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 )
785 class GroupDict( object ) :
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 ) :
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() ) + '}'
800 return map( Group , self )
802 return [ ( group , Group( group ) ) for group in self ]
806 from rsbac.rc import Role
807 from rsbac.objects import User, Process
810 # indent-tabs-mode: nil