Changed perm lockfunc to primarily use the Player-level permission and added the @quell command for managing permission downgrading.
This commit is contained in:
parent
78e7346962
commit
3ac44946c3
5 changed files with 148 additions and 32 deletions
|
|
@ -21,19 +21,32 @@ from src.commands.default.muxcommand import MuxCommand
|
||||||
PERMISSION_HIERARCHY = settings.PERMISSION_HIERARCHY
|
PERMISSION_HIERARCHY = settings.PERMISSION_HIERARCHY
|
||||||
PERMISSION_HIERARCHY_LOWER = [perm.lower() for perm in PERMISSION_HIERARCHY]
|
PERMISSION_HIERARCHY_LOWER = [perm.lower() for perm in PERMISSION_HIERARCHY]
|
||||||
|
|
||||||
class CmdQuell(MuxCommand):
|
class CmdQuell(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
Quelling permissions
|
Quelling permissions
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
quell <command> [=permission level]
|
quell
|
||||||
|
unquell
|
||||||
|
quell/permlevel <command>
|
||||||
|
|
||||||
|
Normally the permission level of the Player is used when puppeting a
|
||||||
|
Character/Object to determine access. Giving this command without
|
||||||
|
arguments will instead switch the lock system to make use of the
|
||||||
|
puppeted Object's permissions instead. Note that this only works DOWNWARDS -
|
||||||
|
a Player cannot use a higher-permission Character to escalate their Player
|
||||||
|
permissions for example. Use the unquell command to revert this state.
|
||||||
|
|
||||||
|
Note that the superuser character is unaffected by full quelling. Use a separate
|
||||||
|
admin account for testing.
|
||||||
|
|
||||||
|
When given an argument, the argument is considered a command to execute with
|
||||||
|
a different (lower) permission level than they currently have. This is useful
|
||||||
|
for quick testing. If no permlevel switch is given, the command will be
|
||||||
|
executed using the lowest permission level available in settings.PERMISSION_HIERARCHY.
|
||||||
|
|
||||||
|
Quelling singular commands will work also for the superuser.
|
||||||
|
|
||||||
This is an admin command that allows to execute another command as
|
|
||||||
another (lower) permission level than what you currently
|
|
||||||
have. This is useful for testing. Also superuser flag will be
|
|
||||||
deactivated by this command. If no permission level is given,
|
|
||||||
the command will be executed as the lowest level available in
|
|
||||||
settings.PERMISSION_HIERARCHY.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "quell"
|
key = "quell"
|
||||||
|
|
@ -43,12 +56,30 @@ class CmdQuell(MuxCommand):
|
||||||
def func(self):
|
def func(self):
|
||||||
"Perform the command"
|
"Perform the command"
|
||||||
|
|
||||||
|
player = self.caller
|
||||||
|
|
||||||
if not self.args:
|
if not self.args:
|
||||||
self.caller.msg("Usage: quell <command> [=permission level]")
|
# try to fully quell player permissions
|
||||||
return
|
if self.cmdstring == 'unquell':
|
||||||
|
if player.get_attribute('_quell'):
|
||||||
|
self.msg("You are not currently quelling you Player permissions.")
|
||||||
|
else:
|
||||||
|
player.del_attribute('_quell')
|
||||||
|
self.msg("You are now using your Player permissions normally.")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
if player.is_superuser:
|
||||||
|
self.msg("Superusers cannot be quelled.")
|
||||||
|
return
|
||||||
|
if player.get_attribute('_quell'):
|
||||||
|
self.msg("You are already quelling your Player permissions.")
|
||||||
|
return
|
||||||
|
player.set_attribute('_quell', True)
|
||||||
|
self.msg("You quell your Player permissions.")
|
||||||
|
return
|
||||||
|
|
||||||
cmd = self.lhs
|
cmd = self.lhs
|
||||||
perm = self.rhs
|
perm = self.switches and self.switches[0] or None
|
||||||
|
|
||||||
if not PERMISSION_HIERARCHY:
|
if not PERMISSION_HIERARCHY:
|
||||||
self.caller.msg("settings.PERMISSION_HIERARCHY is not defined. Add a hierarchy to use this command.")
|
self.caller.msg("settings.PERMISSION_HIERARCHY is not defined. Add a hierarchy to use this command.")
|
||||||
|
|
|
||||||
|
|
@ -1116,6 +1116,7 @@ class CmdSetAttribute(ObjManipCommand):
|
||||||
@set <obj>/<attr> = <value>
|
@set <obj>/<attr> = <value>
|
||||||
@set <obj>/<attr> =
|
@set <obj>/<attr> =
|
||||||
@set <obj>/<attr>
|
@set <obj>/<attr>
|
||||||
|
@set *<player>/attr = <value>
|
||||||
|
|
||||||
Sets attributes on objects. The second form clears
|
Sets attributes on objects. The second form clears
|
||||||
a previously set attribute while the last form
|
a previously set attribute while the last form
|
||||||
|
|
@ -1214,7 +1215,10 @@ class CmdSetAttribute(ObjManipCommand):
|
||||||
objname = self.lhs_objattr[0]['name']
|
objname = self.lhs_objattr[0]['name']
|
||||||
attrs = self.lhs_objattr[0]['attrs']
|
attrs = self.lhs_objattr[0]['attrs']
|
||||||
|
|
||||||
obj = caller.search(objname)
|
if objname.startswith('*'):
|
||||||
|
obj = caller.search_player(objname.lstrip('*'))
|
||||||
|
else:
|
||||||
|
obj = caller.search(objname)
|
||||||
if not obj:
|
if not obj:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -1682,7 +1686,7 @@ class CmdExamine(ObjManipCommand):
|
||||||
|
|
||||||
self.player_mode = "player" in self.switches or obj_name.startswith('*')
|
self.player_mode = "player" in self.switches or obj_name.startswith('*')
|
||||||
if self.player_mode:
|
if self.player_mode:
|
||||||
obj = self.search_player(obj_name)
|
obj = caller.search_player(obj_name.lstrip('*'))
|
||||||
else:
|
else:
|
||||||
obj = caller.search(obj_name)
|
obj = caller.search(obj_name)
|
||||||
if not obj:
|
if not obj:
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ class PlayerCmdSet(CmdSet):
|
||||||
self.add(player.CmdQuit())
|
self.add(player.CmdQuit())
|
||||||
self.add(player.CmdPassword())
|
self.add(player.CmdPassword())
|
||||||
self.add(player.CmdColorTest())
|
self.add(player.CmdColorTest())
|
||||||
|
self.add(player.CmdQuell())
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
self.add(building.CmdExamine())
|
self.add(building.CmdExamine())
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ __all__ = ("CmdOOCLook", "CmdIC", "CmdOOC", "CmdPassword", "CmdQuit",
|
||||||
MAX_NR_CHARACTERS = MULTISESSION_MODE < 2 and 1 or MAX_NR_CHARACTERS
|
MAX_NR_CHARACTERS = MULTISESSION_MODE < 2 and 1 or MAX_NR_CHARACTERS
|
||||||
BASE_PLAYER_TYPECLASS = settings.BASE_PLAYER_TYPECLASS
|
BASE_PLAYER_TYPECLASS = settings.BASE_PLAYER_TYPECLASS
|
||||||
|
|
||||||
|
PERMISSION_HIERARCHY = settings.PERMISSION_HIERARCHY
|
||||||
|
PERMISSION_HIERARCHY_LOWER = [perm.lower() for perm in PERMISSION_HIERARCHY]
|
||||||
|
|
||||||
class CmdOOCLook(MuxPlayerCommand):
|
class CmdOOCLook(MuxPlayerCommand):
|
||||||
"""
|
"""
|
||||||
ooc look
|
ooc look
|
||||||
|
|
@ -601,3 +604,51 @@ class CmdColorTest(MuxPlayerCommand):
|
||||||
# malformed input
|
# malformed input
|
||||||
self.msg("Usage: @color ansi|xterm256")
|
self.msg("Usage: @color ansi|xterm256")
|
||||||
|
|
||||||
|
class CmdQuell(MuxPlayerCommand):
|
||||||
|
"""
|
||||||
|
Quelling permissions
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
quell
|
||||||
|
unquell
|
||||||
|
|
||||||
|
Normally the permission level of the Player is used when puppeting a
|
||||||
|
Character/Object to determine access. This command will switch the lock
|
||||||
|
system to make use of the puppeted Object's permissions instead. This is
|
||||||
|
useful mainly for testing.
|
||||||
|
Hierarchical permission quelling only work downwards, thus a Player cannot
|
||||||
|
use a higher-permission Character to escalate their permission level.
|
||||||
|
Use the unquell command to revert back to normal operation.
|
||||||
|
|
||||||
|
Note that the superuser character cannot be quelled. Use a separate
|
||||||
|
admin account for testing.
|
||||||
|
"""
|
||||||
|
|
||||||
|
key = "@quell"
|
||||||
|
aliases =["@unquell"]
|
||||||
|
locks = "cmd:all()"
|
||||||
|
help_category = "General"
|
||||||
|
|
||||||
|
def func(self):
|
||||||
|
"Perform the command"
|
||||||
|
player = self.caller
|
||||||
|
permstr = " (%s)" % (", ".join(player.permissions))
|
||||||
|
if player.is_superuser:
|
||||||
|
self.msg("Superusers cannot be quelled.")
|
||||||
|
return
|
||||||
|
if self.cmdstring == '@unquell':
|
||||||
|
if not player.get_attribute('_quell'):
|
||||||
|
self.msg("Already using normal Player permissions%s." % permstr)
|
||||||
|
else:
|
||||||
|
player.del_attribute('_quell')
|
||||||
|
self.msg("Player permissions restored%s." % permstr)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
if player.get_attribute('_quell'):
|
||||||
|
self.msg("Already quelling Player permissions")
|
||||||
|
return
|
||||||
|
player.set_attribute('_quell', True)
|
||||||
|
self.msg("Quelling Player permissions%s." % permstr)
|
||||||
|
return
|
||||||
|
|
||||||
|
return
|
||||||
|
|
|
||||||
|
|
@ -128,24 +128,61 @@ def perm(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
perm(<permission>)
|
perm(<permission>)
|
||||||
|
|
||||||
where <permission> is the permission accessing_obj must
|
where <permission> is the permission accessing_obj must
|
||||||
have in order to pass the lock. If the given permission
|
have in order to pass the lock.
|
||||||
is part of _PERMISSION_HIERARCHY, permission is also granted
|
|
||||||
to all ranks higher up in the hierarchy.
|
If the given permission is part of settings.PERMISSION_HIERARCHY,
|
||||||
|
permission is also granted to all ranks higher up in the hierarchy.
|
||||||
|
|
||||||
|
If accessing_object is an Object controlled by a Player, the
|
||||||
|
permissions of the Player is used unless the PlayerAttribute _quell
|
||||||
|
is set to True on the Object. In this case however, the
|
||||||
|
LOWEST hieararcy-permission of the Player/Object-pair will be used
|
||||||
|
(this is order to avoid Players potentially escalating their own permissions
|
||||||
|
by use of a higher-level Object)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
# this allows the perm_above lockfunc to make use of this function too
|
||||||
|
gtmode = kwargs.pop("_greater_than", False)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
perm = args[0].lower()
|
perm = args[0].lower()
|
||||||
permissions = [p.lower() for p in accessing_obj.permissions]
|
perms_object = [p.lower() for p in accessing_obj.permissions]
|
||||||
except (AttributeError, IndexError):
|
except (AttributeError, IndexError):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if perm in permissions:
|
if utils.inherits_from(accessing_obj, "src.objects.objects.Object") and accessing_obj.player:
|
||||||
# simplest case - we have a direct match
|
player = accessing_obj.player
|
||||||
|
perms_player = [p.lower() for p in player.permissions]
|
||||||
|
is_quell = player.get_attribute("_quell")
|
||||||
|
|
||||||
|
if perm in _PERMISSION_HIERARCHY:
|
||||||
|
# check hierarchy without allowing escalation obj->player
|
||||||
|
hpos_target = _PERMISSION_HIERARCHY.index(perm)
|
||||||
|
hpos_player = [hpos for hpos, hperm in enumerate(_PERMISSION_HIERARCHY) if hperm in perms_player]
|
||||||
|
hpos_player = hpos_player and hpos_player[-1] or -1
|
||||||
|
if is_quell:
|
||||||
|
hpos_object = [hpos for hpos, hperm in enumerate(_PERMISSION_HIERARCHY) if hperm in perms_object]
|
||||||
|
hpos_object = hpos_object and hpos_object[-1] or -1
|
||||||
|
if gtmode:
|
||||||
|
return hpos_target < min(hpos_player, hpos_object)
|
||||||
|
else:
|
||||||
|
return hpos_target <= min(hpos_player, hpos_object)
|
||||||
|
elif gtmode:
|
||||||
|
return gtmode and hpos_target < hpos_player
|
||||||
|
else:
|
||||||
|
return hpos_target <= hpos_player
|
||||||
|
elif not is_quell and perm in perms_player:
|
||||||
|
# if we get here, check player perms first, otherwise continue as normal
|
||||||
|
return True
|
||||||
|
|
||||||
|
if perm in perms_object:
|
||||||
|
# simplest case - we have direct match
|
||||||
return True
|
return True
|
||||||
if perm in _PERMISSION_HIERARCHY:
|
if perm in _PERMISSION_HIERARCHY:
|
||||||
# check if we have a higher hierarchy position
|
# check if we have a higher hierarchy position
|
||||||
ppos = _PERMISSION_HIERARCHY.index(perm)
|
hpos_target = _PERMISSION_HIERARCHY.index(perm)
|
||||||
return any(1 for hpos, hperm in enumerate(_PERMISSION_HIERARCHY)
|
return any(1 for hpos, hperm in enumerate(_PERMISSION_HIERARCHY)
|
||||||
if hperm in permissions and hpos > ppos)
|
if hperm in perms_object and hpos_target < hpos)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def perm_above(accessing_obj, accessed_obj, *args, **kwargs):
|
def perm_above(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
|
|
@ -155,20 +192,12 @@ def perm_above(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
it's assumed we refer to superuser. If no hierarchy is defined,
|
it's assumed we refer to superuser. If no hierarchy is defined,
|
||||||
this function has no meaning and returns False.
|
this function has no meaning and returns False.
|
||||||
"""
|
"""
|
||||||
try:
|
kwargs["_greater_than"] = True
|
||||||
perm = args[0].lower()
|
return perm(accessing_obj,accessed_obj, *args, **kwargs)
|
||||||
except (AttributeError, IndexError):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if perm in _PERMISSION_HIERARCHY:
|
|
||||||
ppos = _PERMISSION_HIERARCHY.index(perm)
|
|
||||||
return any(1 for hpos, hperm in enumerate(_PERMISSION_HIERARCHY)
|
|
||||||
if hperm in [p.lower() for p in accessing_obj.permissions] and hpos > ppos)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def pperm(accessing_obj, accessed_obj, *args, **kwargs):
|
def pperm(accessing_obj, accessed_obj, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
The basic permission-checker for Player objects. Ignores case.
|
The basic permission-checker only for Player objects. Ignores case.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
pperm(<permission>)
|
pperm(<permission>)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue