From 368c137a61608ac6ce9a8417e8ddf6b13dc84ff5 Mon Sep 17 00:00:00 2001 From: Michael Ziegler Date: Sat, 19 Dec 2009 14:20:46 +0100 Subject: [PATCH] properly implement ACL handling --- pyweb/mumble/MumbleCtlDbus.py | 43 +++++++++++- pyweb/mumble/MumbleCtlIce.py | 119 +++++++++------------------------- pyweb/mumble/mctl.py | 2 +- pyweb/mumble/mmobjects.py | 90 +++++++++++++++++-------- pyweb/mumble/models.py | 29 +++------ 5 files changed, 143 insertions(+), 140 deletions(-) diff --git a/pyweb/mumble/MumbleCtlDbus.py b/pyweb/mumble/MumbleCtlDbus.py index a8e2165..b16eeb0 100644 --- a/pyweb/mumble/MumbleCtlDbus.py +++ b/pyweb/mumble/MumbleCtlDbus.py @@ -135,10 +135,47 @@ class MumbleCtlDbus_118(MumbleCtlBase): return MumbleCtlDbus_118.convertDbusTypeToNative(self._getDbusServerObject(srvid).getRegisteredPlayers( filter ) ) def getACL(self, srvid, channelid): - return MumbleCtlDbus_118.convertDbusTypeToNative(self._getDbusServerObject(srvid).getACL(channelid)) + raw_acls, raw_groups, raw_inherit = self._getDbusServerObject(srvid).getACL(channelid) + + acls = [ ObjectInfo( + applyHere = bool(rule[0]), + applySubs = bool(rule[1]), + inherited = bool(rule[2]), + userid = int(rule[3]), + group = str(rule[4]), + allow = int(rule[5]), + deny = int(rule[6]), + ) + for rule in raw_acls + ]; + + groups = [ ObjectInfo( + name = str(group[0]), + inherited = bool(group[1]), + inherit = bool(group[2]), + inheritable = bool(group[3]), + add = [ int(usrid) for usrid in group[4] ], + remove = [ int(usrid) for usrid in group[5] ], + members = [ int(usrid) for usrid in group[6] ], + ) + for group in raw_groups + ]; + + return acls, groups, bool(raw_inherit); - def setACL(self, srvid, acl): - self._getDbusServerObject(srvid).setACL(*acl.pack()) + def setACL(self, srvid, channelid, acls, groups, inherit): + # Pack acl ObjectInfo into a tuple and send that over dbus + dbus_acls = [ + ( rule.applyHere, rule.applySubs, rule.inherited, rule.userid, rule.group, rule.allow, rule.deny ) + for rule in acls + ]; + + dbus_groups = [ + ( group.name, group.inherited, group.inherit, group.inheritable, group.add, group.remove, group.members ) + for group in groups + ]; + + return self._getDbusServerObject(srvid).setACL( channelid, dbus_acls, dbus_groups, inherit ); def getBootedServers(self): return MumbleCtlDbus_118.convertDbusTypeToNative(self.meta.getBootedServers()) diff --git a/pyweb/mumble/MumbleCtlIce.py b/pyweb/mumble/MumbleCtlIce.py index 32317cd..16897d7 100644 --- a/pyweb/mumble/MumbleCtlIce.py +++ b/pyweb/mumble/MumbleCtlIce.py @@ -195,54 +195,41 @@ class MumbleCtlIce_118(MumbleCtlBase): @protectDjangoErrPage def getACL(self, srvid, channelid): - import Murmur - acls = self._getIceServerObject(srvid).getACL(channelid) - ret = [] - for x in acls: - if isinstance(x, list): - tmp = [] - for y in x: - if y.__class__ is Murmur.ACL: - tmp.append([y.applyHere, y.applySubs, y.inherited, y.playerid, self.setUnicodeFlag(y.group), y.allow, y.deny]) - elif y.__class__ is Murmur.Group: - tmp.append([self.setUnicodeFlag(y.name), y.inherited, y.inherit, y.inheritable, y.add, y.remove, y.members]) - - ret.append(tmp) - else: - ret.append(x) + # need to convert acls to say "userid" instead of "playerid". meh. + raw_acls, raw_groups, raw_inherit = self._getIceServerObject(srvid).getACL(channelid) - return ret + acls = [ ObjectInfo( + applyHere = rule.applyHere, + applySubs = rule.applySubs, + inherited = rule.inherited, + userid = rule.playerid, + group = rule.group, + allow = rule.allow, + deny = rule.deny, + ) + for rule in raw_acls + ]; + + return acls, raw_groups, raw_inherit; @protectDjangoErrPage - def setACL(self, srvid, acl): + def setACL(self, srvid, channelid, acls, groups, inherit): import Murmur - newacls = []; - newgroups = []; + ice_acls = []; - for curr_acl in acl.acls: - new_acl = Murmur.ACL(); - new_acl.applyHere = curr_acl['applyHere']; - new_acl.applySubs = curr_acl['applySubs']; - new_acl.inherited = curr_acl['inherited']; - new_acl.playerid = curr_acl['playerid']; - new_acl.group = curr_acl['group'].encode( "UTF-8" ); - new_acl.allow = curr_acl['allow']; - new_acl.deny = curr_acl['deny']; - newacls.append( new_acl ); + for rule in acls: + ice_rule = Murmur.ACL(); + ice_rule.applyHere = rule.applyHere; + ice_rule.applySubs = rule.applySubs; + ice_rule.inherited = rule.inherited; + ice_rule.playerid = rule.userid; + ice_rule.group = rule.group; + ice_rule.allow = rule.allow; + ice_rule.deny = rule.deny; + ice_acls.append(ice_rule); - for curr_group in acl.groups: - new_group = Murmur.Group() - new_group.name = curr_group['name'].encode( "UTF-8" ); - new_group.inherited = curr_group['inherited']; - new_group.inherit = curr_group['inherit']; - new_group.inheritable = curr_group['inheritable']; - new_group.add = curr_group['add']; - new_group.remove = curr_group['remove']; - new_group.members = curr_group['members']; - newgroups.append( new_group ); - - self._getIceServerObject(srvid).setACL( acl.channelId, newacls, newgroups, acl.inherit ); + return self._getIceServerObject(srvid).setACL( channelid, ice_acls, groups, inherit ); @protectDjangoErrPage def getTexture(self, srvid, mumbleid): @@ -359,53 +346,9 @@ class MumbleCtlIce_120(MumbleCtlIce_118): @protectDjangoErrPage def getACL(self, srvid, channelid): - import Murmur - acls = self._getIceServerObject(srvid).getACL(channelid) - ret = [] - for x in acls: - if isinstance(x, list): - tmp = [] - for y in x: - if y.__class__ is Murmur.ACL: - tmp.append([y.applyHere, y.applySubs, y.inherited, y.userid, self.setUnicodeFlag(y.group), y.allow, y.deny]) - elif y.__class__ is Murmur.Group: - tmp.append([self.setUnicodeFlag(y.name), y.inherited, y.inherit, y.inheritable, y.add, y.remove, y.members]) - - ret.append(tmp) - else: - ret.append(x) - - return ret + return self._getIceServerObject(srvid).getACL(channelid) @protectDjangoErrPage - def setACL(self, srvid, acl): - import Murmur - - newacls = []; - newgroups = []; - - for curr_acl in acl.acls: - new_acl = Murmur.ACL(); - new_acl.applyHere = curr_acl['applyHere']; - new_acl.applySubs = curr_acl['applySubs']; - new_acl.inherited = curr_acl['inherited']; - new_acl.userid = curr_acl['playerid']; - new_acl.group = curr_acl['group'].encode( "UTF-8" ); - new_acl.allow = curr_acl['allow']; - new_acl.deny = curr_acl['deny']; - newacls.append( new_acl ); - - for curr_group in acl.groups: - new_group = Murmur.Group() - new_group.name = curr_group['name'].encode( "UTF-8" ); - new_group.inherited = curr_group['inherited']; - new_group.inherit = curr_group['inherit']; - new_group.inheritable = curr_group['inheritable']; - new_group.add = curr_group['add']; - new_group.remove = curr_group['remove']; - new_group.members = curr_group['members']; - newgroups.append( new_group ); - - self._getIceServerObject(srvid).setACL( acl.channelId, newacls, newgroups, acl.inherit ); - + def setACL(self, srvid, channelid, acls, groups, inherit): + return self._getIceServerObject(srvid).setACL( channelid, acls, groups, inherit ); diff --git a/pyweb/mumble/mctl.py b/pyweb/mumble/mctl.py index 88f468d..1745cd3 100644 --- a/pyweb/mumble/mctl.py +++ b/pyweb/mumble/mctl.py @@ -82,7 +82,7 @@ class MumbleCtlBase (object): def getACL(self, srvid, channelid): raise NotImplementedError( "mctl::getACL" ); - def setACL(self, srvid, acl): + def setACL(self, srvid, channelid, acl, groups, inherit): raise NotImplementedError( "mctl::setACL" ); def getTexture(self, srvid, mumbleid): diff --git a/pyweb/mumble/mmobjects.py b/pyweb/mumble/mmobjects.py index a70b502..85c5878 100644 --- a/pyweb/mumble/mmobjects.py +++ b/pyweb/mumble/mmobjects.py @@ -43,6 +43,8 @@ class mmChannel( object ): self.parent = parentChan; if self.parent is not None: self.parent.subchans.append( self ); + + self._acl = None; # Lookup unknown attributes in self.channelObj to automatically include Murmur's fields def __getattr__( self, key ): @@ -58,6 +60,16 @@ class mmChannel( object ): return self.parent.parentChannels() + [self.parent.name]; + def getACL( self ): + """ Retrieve the ACL for this channel. """ + if not self._acl: + self._acl = mmACL( self, self.server.ctl.getACL( self.server.srvid, self.chanid ) ); + + return self._acl; + + acl = property( getACL, doc=getACL.__doc__ ); + + is_server = False; is_channel = True; is_player = False; @@ -213,34 +225,56 @@ class mmPlayer( object ): class mmACL: """Represents an ACL for a certain channel.""" - def __init__( self, channelId, aclObj ): - aclsrc, groupsrc, inherit = aclObj; - - self.channelId = channelId; - - self.acls = []; - for line in aclsrc: - acl = {}; - acl['applyHere'], acl['applySubs'], acl['inherited'], acl['playerid'], acl['group'], acl['allow'], acl['deny'] = line; - self.acls.append( acl ); - - self.groups = []; - for line in groupsrc: - group = {}; - group['name'], group['inherited'], group['inherit'], group['inheritable'], group['add'], group['remove'], group['members'] = line; - self.groups.append( group ); - if group['name'] == "admin": - self.admingroup = group; - - self.inherit = inherit; - - def pack( self ): - """ Pack the information in this ACL up in a way that it can be passed to DBus. """ - return ( - self.channelId, - [( acl['applyHere'], acl['applySubs'], acl['inherited'], acl['playerid'], acl['group'], acl['allow'], acl['deny'] ) for acl in self.acls ], - [( group['name'], group['inherited'], group['inherit'], group['inheritable'], group['add'], group['remove'], group['members'] ) for group in self.groups ], - self.inherit + def __init__( self, channel, aclObj ): + self.channel = channel; + self.acls, self.groups, self.inherit = aclObj; + + self.groups_dict = {}; + + for group in self.groups: + self.groups_dict[ group.name ] = group; + + def groupHasMember( self, name, userid ): + """ Checks if the given userid is a member of the given group in this channel. """ + if name not in self.groups_dict: + raise ReferenceError( "No such group '%s'" % name ); + + return userid in self.groups_dict[name].add or userid in self.groups_dict[name].members; + + def groupAddMember( self, name, userid ): + """ Make sure this userid is a member of the group in this channel (and subs). """ + if name not in self.groups_dict: + raise ReferenceError( "No such group '%s'" % name ); + + group = self.groups_dict[name]; + + # if neither inherited nor to be added, add + if userid not in group.members and userid not in group.add: + group.add.append( userid ); + + # if to be removed, unremove + if userid in group.remove: + group.remove.remove( userid ); + + def groupRemoveMember( self, name, userid ): + """ Make sure this userid is NOT a member of the group in this channel (and subs). """ + if name not in self.groups_dict: + raise ReferenceError( "No such group '%s'" % name ); + + group = self.groups_dict[name]; + + # if added here, unadd + if userid in group.add: + group.add.remove( userid ); + # if member and not in remove, add to remove + elif userid in group.members and userid not in group.remove: + group.remove.append( userid ); + + def save( self ): + return self.channel.server.ctl.setACL( + self.channel.server.srvid, + self.channel.chanid, + self.acls, self.groups, self.inherit ); diff --git a/pyweb/mumble/models.py b/pyweb/mumble/models.py index c9d1cdf..600f45c 100644 --- a/pyweb/mumble/models.py +++ b/pyweb/mumble/models.py @@ -446,39 +446,28 @@ class MumbleUser( models.Model ): # Don't save the users' passwords, we don't need them anyway self.password = ''; - self.setAdmin( self.isAdmin ); + self.aclAdmin = self.isAdmin; # Now allow django to save the record set return models.Model.save( self ); # Admin handlers - def getAdmin( self ): """Get ACL of root Channel, get the admin group and see if this user is in it.""" - acl = mmACL( 0, self.server.ctl.getACL(self.server.srvid, 0) ); - - if not hasattr( acl, "admingroup" ): - raise ReferenceError( _( "The admin group was not found in the ACL's groups list!" ) ); - return self.mumbleid in acl.admingroup['add']; + return self.server.rootchan.acl.groupHasMember( "admin", self.mumbleid ); def setAdmin( self, value ): """Set or revoke this user's membership in the admin group on the root channel.""" - ctl = self.server.ctl; - acl = mmACL( 0, ctl.getACL(self.server.srvid, 0) ); - - if not hasattr( acl, "admingroup" ): - raise ReferenceError( _( "The admin group was not found in the ACL's groups list!" ) ); - - if value != ( self.mumbleid in acl.admingroup['add'] ): - if value: - acl.admingroup['add'].append( self.mumbleid ); - else: - acl.admingroup['add'].remove( self.mumbleid ); - - ctl.setACL(self.server.srvid, acl); + if value: + self.server.rootchan.acl.groupAddMember( "admin", self.mumbleid ); + else: + self.server.rootchan.acl.groupRemoveMember( "admin", self.mumbleid ); + self.server.rootchan.acl.save(); return value; + aclAdmin = property( getAdmin, setAdmin, doc="Wrapper around getAdmin/setAdmin (not a database field like isAdmin)" ); + # Registration fetching def getRegistration( self ): """Retrieve a user's registration from Murmur as a dict."""