Correct bugs in lock-handling, add more unit tests

This commit is contained in:
Griatch 2018-02-03 01:17:52 +01:00
parent a74fd55c7b
commit 3b7648ec0e
3 changed files with 226 additions and 57 deletions

View file

@ -619,7 +619,7 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)):
self.basetype_setup() self.basetype_setup()
self.at_account_creation() self.at_account_creation()
permissions = settings.PERMISSION_ACCOUNT_DEFAULT permissions = [settings.PERMISSION_ACCOUNT_DEFAULT]
if hasattr(self, "_createdict"): if hasattr(self, "_createdict"):
# this will only be set if the utils.create_account # this will only be set if the utils.create_account
# function was used to create the object. # function was used to create the object.

View file

@ -89,10 +89,14 @@ DefaultLock: Exits: controls who may traverse the exit to
""" """
from __future__ import print_function from __future__ import print_function
from ast import literal_eval
from django.conf import settings from django.conf import settings
from evennia.utils import utils from evennia.utils import utils
_PERMISSION_HIERARCHY = [pe.lower() for pe in settings.PERMISSION_HIERARCHY] _PERMISSION_HIERARCHY = [pe.lower() for pe in settings.PERMISSION_HIERARCHY]
# also accept different plural forms
_PERMISSION_HIERARCHY_PLURAL = [pe + 's' if not pe.endswith('s') else pe
for pe in _PERMISSION_HIERARCHY]
def _to_account(accessing_obj): def _to_account(accessing_obj):
@ -158,49 +162,77 @@ def perm(accessing_obj, accessed_obj, *args, **kwargs):
""" """
# this allows the perm_above lockfunc to make use of this function too # this allows the perm_above lockfunc to make use of this function too
gtmode = kwargs.pop("_greater_than", False)
try: try:
permission = args[0].lower() permission = args[0].lower()
perms_object = [p.lower() for p in accessing_obj.permissions.all()] perms_object = accessing_obj.permissions.all()
except (AttributeError, IndexError): except (AttributeError, IndexError):
return False return False
if utils.inherits_from(accessing_obj, "evennia.objects.objects.DefaultObject") and accessing_obj.account: gtmode = kwargs.pop("_greater_than", False)
account = accessing_obj.account is_quell = False
# we strip eventual plural forms, so Builders == Builder
perms_account = [p.lower().rstrip("s") for p in account.permissions.all()] account = (utils.inherits_from(accessing_obj, "evennia.objects.objects.DefaultObject") and
accessing_obj.account)
# check object perms (note that accessing_obj could be an Account too)
perms_account = []
if account:
perms_account = account.permissions.all()
is_quell = account.attributes.get("_quell") is_quell = account.attributes.get("_quell")
# Check hirarchy matches; handle both singular/plural forms in hierarchy
hpos_target = None
if permission in _PERMISSION_HIERARCHY: if permission in _PERMISSION_HIERARCHY:
# check hierarchy without allowing escalation obj->account
hpos_target = _PERMISSION_HIERARCHY.index(permission) hpos_target = _PERMISSION_HIERARCHY.index(permission)
hpos_account = [hpos for hpos, hperm in enumerate(_PERMISSION_HIERARCHY) if hperm in perms_account] if permission.endswith('s') and permission[:-1] in _PERMISSION_HIERARCHY:
hpos_target = _PERMISSION_HIERARCHY.index(permission[:-1])
if hpos_target is not None:
# hieratchy match
hpos_account = -1
hpos_object = -1
if account:
# we have an account puppeting this object. We must check what perms it has
perms_account_single = [p[:-1] if p.endswith('s') else p for p in perms_account]
hpos_account = [hpos for hpos, hperm in enumerate(_PERMISSION_HIERARCHY)
if hperm in perms_account_single]
hpos_account = hpos_account and hpos_account[-1] or -1 hpos_account = hpos_account and hpos_account[-1] or -1
if is_quell:
hpos_object = [hpos for hpos, hperm in enumerate(_PERMISSION_HIERARCHY) if hperm in perms_object] if not account or is_quell:
# only get the object-level perms if there is no account or quelling
perms_object_single = [p[:-1] if p.endswith('s') else p for p in perms_object]
hpos_object = [hpos for hpos, hperm in enumerate(_PERMISSION_HIERARCHY)
if hperm in perms_object_single]
hpos_object = hpos_object and hpos_object[-1] or -1 hpos_object = hpos_object and hpos_object[-1] or -1
if account and is_quell:
# quell mode: use smallest perm from account and object
if gtmode: if gtmode:
return hpos_target < min(hpos_account, hpos_object) return hpos_target < min(hpos_account, hpos_object)
else: else:
return hpos_target <= min(hpos_account, hpos_object) return hpos_target <= min(hpos_account, hpos_object)
elif gtmode: elif account:
# use account perm
if gtmode:
return hpos_target < hpos_account return hpos_target < hpos_account
else: else:
return hpos_target <= hpos_account return hpos_target <= hpos_account
elif not is_quell and permission in perms_account: else:
# if we get here, check account perms first, otherwise # use object perm
# continue as normal if gtmode:
return hpos_target < hpos_object
else:
return hpos_target <= hpos_object
else:
# no hierarchy match - check direct matches
if account:
# account exists, check it first unless quelled
if is_quell and permission in perms_object:
return True
elif permission in perms_account:
return True
elif permission in perms_object:
return True return True
if permission in perms_object:
# simplest case - we have direct match
return True
if permission in _PERMISSION_HIERARCHY:
# check if we have a higher hierarchy position
hpos_target = _PERMISSION_HIERARCHY.index(permission)
return any(1 for hpos, hperm in enumerate(_PERMISSION_HIERARCHY)
if hperm in perms_object and hpos_target < hpos)
return False return False
@ -229,7 +261,6 @@ def pperm(accessing_obj, accessed_obj, *args, **kwargs):
""" """
return perm(_to_account(accessing_obj), accessed_obj, *args, **kwargs) return perm(_to_account(accessing_obj), accessed_obj, *args, **kwargs)
def pperm_above(accessing_obj, accessed_obj, *args, **kwargs): def pperm_above(accessing_obj, accessed_obj, *args, **kwargs):
""" """
Only allow Account objects with a permission *higher* in the permission Only allow Account objects with a permission *higher* in the permission
@ -482,7 +513,7 @@ def tag(accessing_obj, accessed_obj, *args, **kwargs):
accessing_obj = accessing_obj.obj accessing_obj = accessing_obj.obj
tagkey = args[0] if args else None tagkey = args[0] if args else None
category = args[1] if len(args) > 1 else None category = args[1] if len(args) > 1 else None
return accessing_obj.tags.get(tagkey, category=category) return bool(accessing_obj.tags.get(tagkey, category=category))
def objtag(accessing_obj, accessed_obj, *args, **kwargs): def objtag(accessing_obj, accessed_obj, *args, **kwargs):
@ -494,7 +525,7 @@ def objtag(accessing_obj, accessed_obj, *args, **kwargs):
Only true if accessed_obj has the specified tag and optional Only true if accessed_obj has the specified tag and optional
category. category.
""" """
return accessed_obj.tags.get(*args) return bool(accessed_obj.tags.get(*args))
def inside(accessing_obj, accessed_obj, *args, **kwargs): def inside(accessing_obj, accessed_obj, *args, **kwargs):
@ -592,7 +623,9 @@ def serversetting(accessing_obj, accessed_obj, *args, **kwargs):
serversetting(IRC_ENABLED) serversetting(IRC_ENABLED)
serversetting(BASE_SCRIPT_PATH, ['types']) serversetting(BASE_SCRIPT_PATH, ['types'])
A given True/False or integers will be converted properly. A given True/False or integers will be converted properly. Note that
everything will enter this function as strings, so they have to be
unpacked to their real value. We only support basic properties.
""" """
if not args or not args[0]: if not args or not args[0]:
return False return False
@ -602,12 +635,12 @@ def serversetting(accessing_obj, accessed_obj, *args, **kwargs):
else: else:
setting, val = args[0], args[1] setting, val = args[0], args[1]
# convert # convert
if val == 'True': try:
val = True val = literal_eval(val)
elif val == 'False': except Exception:
val = False # we swallow errors here, lockfuncs has noone to report to
elif val.isdigit(): return False
val = int(val)
if setting in settings._wrapped.__dict__: if setting in settings._wrapped.__dict__:
return settings._wrapped.__dict__[setting] == val return settings._wrapped.__dict__[setting] == val
return False return False

View file

@ -11,10 +11,11 @@ from evennia.utils.test_resources import EvenniaTest
try: try:
# this is a special optimized Django version, only available in current Django devel # this is a special optimized Django version, only available in current Django devel
from django.utils.unittest import TestCase from django.utils.unittest import TestCase, override_settings
except ImportError: except ImportError:
from django.test import TestCase from django.test import TestCase, override_settings
from evennia import settings_default
from evennia.locks import lockfuncs from evennia.locks import lockfuncs
# ------------------------------------------------------------ # ------------------------------------------------------------
@ -25,7 +26,8 @@ from evennia.locks import lockfuncs
class TestLockCheck(EvenniaTest): class TestLockCheck(EvenniaTest):
def testrun(self): def testrun(self):
dbref = self.obj2.dbref dbref = self.obj2.dbref
self.obj1.locks.add("owner:dbref(%s);edit:dbref(%s) or perm(Admin);examine:perm(Builder) and id(%s);delete:perm(Admin);get:all()" % (dbref, dbref, dbref)) self.obj1.locks.add("owner:dbref(%s);edit:dbref(%s) or perm(Admin);examine:perm(Builder) "
"and id(%s);delete:perm(Admin);get:all()" % (dbref, dbref, dbref))
self.obj2.permissions.add('Admin') self.obj2.permissions.add('Admin')
self.assertEquals(True, self.obj1.locks.check(self.obj2, 'owner')) self.assertEquals(True, self.obj1.locks.check(self.obj2, 'owner'))
self.assertEquals(True, self.obj1.locks.check(self.obj2, 'edit')) self.assertEquals(True, self.obj1.locks.check(self.obj2, 'edit'))
@ -36,20 +38,154 @@ class TestLockCheck(EvenniaTest):
self.assertEquals(False, self.obj1.locks.check(self.obj2, 'get')) self.assertEquals(False, self.obj1.locks.check(self.obj2, 'get'))
self.assertEquals(True, self.obj1.locks.check(self.obj2, 'not_exist', default=True)) self.assertEquals(True, self.obj1.locks.check(self.obj2, 'not_exist', default=True))
class TestLockfuncs(EvenniaTest): class TestLockfuncs(EvenniaTest):
def testrun(self): def setUp(self):
super(TestLockfuncs, self).setUp()
self.account2.permissions.add('Admin')
self.char2.permissions.add('Builder')
def test_booleans(self):
self.assertEquals(True, lockfuncs.true(self.account2, self.obj1))
self.assertEquals(True, lockfuncs.all(self.account2, self.obj1))
self.assertEquals(False, lockfuncs.false(self.account2, self.obj1))
self.assertEquals(False, lockfuncs.none(self.account2, self.obj1))
self.assertEquals(True, lockfuncs.self(self.obj1, self.obj1))
self.assertEquals(True, lockfuncs.self(self.account, self.account))
self.assertEquals(False, lockfuncs.superuser(self.account, None))
@override_settings(PERMISSION_HIERARCHY=settings_default.PERMISSION_HIERARCHY)
def test_account_perm(self):
self.assertEquals(False, lockfuncs.perm(self.account2, None, 'foo'))
self.assertEquals(False, lockfuncs.perm(self.account2, None, 'Developer'))
self.assertEquals(False, lockfuncs.perm(self.account2, None, 'Developers'))
self.assertEquals(True, lockfuncs.perm(self.account2, None, 'Admin'))
self.assertEquals(True, lockfuncs.perm(self.account2, None, 'Admins'))
self.assertEquals(True, lockfuncs.perm(self.account2, None, 'Player'))
self.assertEquals(True, lockfuncs.perm(self.account2, None, 'Players'))
self.assertEquals(True, lockfuncs.perm(self.account2, None, 'Builder'))
self.assertEquals(True, lockfuncs.perm(self.account2, None, 'Builders'))
self.assertEquals(True, lockfuncs.perm_above(self.account2, None, 'Builder'))
@override_settings(PERMISSION_HIERARCHY=settings_default.PERMISSION_HIERARCHY)
def test_puppet_perm(self):
self.assertEquals(False, lockfuncs.perm(self.char2, None, 'foo'))
self.assertEquals(False, lockfuncs.perm(self.char2, None, 'Developer'))
self.assertEquals(False, lockfuncs.perm(self.char2, None, 'Develoeprs'))
self.assertEquals(True, lockfuncs.perm(self.char2, None, 'Admin'))
self.assertEquals(True, lockfuncs.perm(self.char2, None, 'Admins'))
self.assertEquals(True, lockfuncs.perm(self.char2, None, 'Player'))
self.assertEquals(True, lockfuncs.perm(self.char2, None, 'Players'))
self.assertEquals(True, lockfuncs.perm(self.char2, None, 'Builder'))
self.assertEquals(True, lockfuncs.perm(self.char2, None, 'Builders'))
@override_settings(PERMISSION_HIERARCHY=settings_default.PERMISSION_HIERARCHY)
def test_account_perm_above(self):
self.assertEquals(True, lockfuncs.perm_above(self.char2, None, 'Builder'))
self.assertEquals(True, lockfuncs.perm_above(self.char2, None, 'Builders'))
self.assertEquals(True, lockfuncs.perm_above(self.char2, None, 'Player'))
self.assertEquals(False, lockfuncs.perm_above(self.char2, None, 'Admin'))
self.assertEquals(False, lockfuncs.perm_above(self.char2, None, 'Admins'))
self.assertEquals(False, lockfuncs.perm_above(self.char2, None, 'Developers'))
@override_settings(PERMISSION_HIERARCHY=settings_default.PERMISSION_HIERARCHY)
def test_quell_perm(self):
self.account2.db._quell = True
self.assertEquals(False, lockfuncs.false(self.char2, None))
self.assertEquals(False, lockfuncs.perm(self.char2, None, 'Developer'))
self.assertEquals(False, lockfuncs.perm(self.char2, None, 'Developers'))
self.assertEquals(False, lockfuncs.perm(self.char2, None, 'Admin'))
self.assertEquals(False, lockfuncs.perm(self.char2, None, 'Admins'))
self.assertEquals(True, lockfuncs.perm(self.char2, None, 'Player'))
self.assertEquals(True, lockfuncs.perm(self.char2, None, 'Players'))
self.assertEquals(True, lockfuncs.perm(self.char2, None, 'Builder'))
self.assertEquals(True, lockfuncs.perm(self.char2, None, 'Builders'))
@override_settings(PERMISSION_HIERARCHY=settings_default.PERMISSION_HIERARCHY)
def test_quell_above_perm(self):
self.assertEquals(True, lockfuncs.perm_above(self.char2, None, 'Player'))
self.assertEquals(True, lockfuncs.perm_above(self.char2, None, 'Builder'))
@override_settings(PERMISSION_HIERARCHY=settings_default.PERMISSION_HIERARCHY)
def test_object_perm(self):
self.obj2.permissions.add('Admin') self.obj2.permissions.add('Admin')
self.assertEquals(True, lockfuncs.true(self.obj2, self.obj1)) self.assertEquals(False, lockfuncs.perm(self.obj2, None, 'Developer'))
self.assertEquals(False, lockfuncs.false(self.obj2, self.obj1)) self.assertEquals(False, lockfuncs.perm(self.obj2, None, 'Developers'))
self.assertEquals(True, lockfuncs.perm(self.obj2, self.obj1, 'Admin')) self.assertEquals(True, lockfuncs.perm(self.obj2, None, 'Admin'))
self.assertEquals(True, lockfuncs.perm_above(self.obj2, self.obj1, 'Builder')) self.assertEquals(True, lockfuncs.perm(self.obj2, None, 'Admins'))
self.assertEquals(True, lockfuncs.perm(self.obj2, None, 'Player'))
self.assertEquals(True, lockfuncs.perm(self.obj2, None, 'Players'))
self.assertEquals(True, lockfuncs.perm(self.obj2, None, 'Builder'))
self.assertEquals(True, lockfuncs.perm(self.obj2, None, 'Builders'))
@override_settings(PERMISSION_HIERARCHY=settings_default.PERMISSION_HIERARCHY)
def test_object_above_perm(self):
self.obj2.permissions.add('Admin')
self.assertEquals(False, lockfuncs.perm_above(self.obj2, None, 'Admins'))
self.assertEquals(True, lockfuncs.perm_above(self.obj2, None, 'Builder'))
self.assertEquals(True, lockfuncs.perm_above(self.obj2, None, 'Builders'))
@override_settings(PERMISSION_HIERARCHY=settings_default.PERMISSION_HIERARCHY)
def test_pperm(self):
self.obj2.permissions.add('Developer')
self.char2.permissions.add('Developer')
self.assertEquals(False, lockfuncs.pperm(self.obj2, None, 'Players'))
self.assertEquals(True, lockfuncs.pperm(self.char2, None, 'Players'))
self.assertEquals(True, lockfuncs.pperm(self.account, None, 'Admins'))
self.assertEquals(True, lockfuncs.pperm_above(self.account, None, 'Builders'))
self.assertEquals(False, lockfuncs.pperm_above(self.account2, None, 'Admins'))
self.assertEquals(True, lockfuncs.pperm_above(self.char2, None, 'Players'))
def test_dbref(self):
dbref = self.obj2.dbref dbref = self.obj2.dbref
self.assertEquals(True, lockfuncs.dbref(self.obj2, self.obj1, '%s' % dbref)) self.assertEquals(True, lockfuncs.dbref(self.obj2, None, '%s' % dbref))
self.assertEquals(False, lockfuncs.id(self.obj2, None, '%s' % (dbref + '1')))
dbref = self.account2.dbref
self.assertEquals(True, lockfuncs.pdbref(self.account2, None, '%s' % dbref))
self.assertEquals(False, lockfuncs.pid(self.account2, None, '%s' % (dbref + '1')))
def test_attr(self):
self.obj2.db.testattr = 45 self.obj2.db.testattr = 45
self.assertEquals(True, lockfuncs.attr(self.obj2, self.obj1, 'testattr', '45')) self.assertEquals(True, lockfuncs.attr(self.obj2, None, 'testattr', '45'))
self.assertEquals(False, lockfuncs.attr_gt(self.obj2, self.obj1, 'testattr', '45')) self.assertEquals(False, lockfuncs.attr_gt(self.obj2, None, 'testattr', '45'))
self.assertEquals(True, lockfuncs.attr_ge(self.obj2, self.obj1, 'testattr', '45')) self.assertEquals(True, lockfuncs.attr_ge(self.obj2, None, 'testattr', '45'))
self.assertEquals(False, lockfuncs.attr_lt(self.obj2, self.obj1, 'testattr', '45')) self.assertEquals(False, lockfuncs.attr_lt(self.obj2, None, 'testattr', '45'))
self.assertEquals(True, lockfuncs.attr_le(self.obj2, self.obj1, 'testattr', '45')) self.assertEquals(True, lockfuncs.attr_le(self.obj2, None, 'testattr', '45'))
self.assertEquals(False, lockfuncs.attr_ne(self.obj2, self.obj1, 'testattr', '45'))
self.assertEquals(True, lockfuncs.objattr(None, self.obj2, 'testattr', '45'))
self.assertEquals(True, lockfuncs.objattr(None, self.obj2, 'testattr', '45'))
self.assertEquals(False, lockfuncs.objattr(None, self.obj2, 'testattr', '45', compare='lt'))
def test_locattr(self):
self.obj2.location.db.locattr = 'test'
self.assertEquals(True, lockfuncs.locattr(self.obj2, None, 'locattr', 'test'))
self.assertEquals(False, lockfuncs.locattr(self.obj2, None, 'fail', 'testfail'))
self.assertEquals(True, lockfuncs.objlocattr(None, self.obj2, 'locattr', 'test'))
def test_tag(self):
self.obj2.tags.add("test1")
self.obj2.tags.add("test2", "category1")
self.assertEquals(True, lockfuncs.tag(self.obj2, None, 'test1'))
self.assertEquals(True, lockfuncs.tag(self.obj2, None, 'test2', 'category1'))
self.assertEquals(False, lockfuncs.tag(self.obj2, None, 'test1', 'category1'))
self.assertEquals(False, lockfuncs.tag(self.obj2, None, 'test1', 'category2'))
self.assertEquals(True, lockfuncs.objtag(None, self.obj2, 'test2', 'category1'))
self.assertEquals(False, lockfuncs.objtag(None, self.obj2, 'test2'))
def test_inside_holds(self):
self.assertEquals(True, lockfuncs.inside(self.char1, self.room1))
self.assertEquals(False, lockfuncs.inside(self.char1, self.room2))
self.assertEquals(True, lockfuncs.holds(self.room1, self.char1))
self.assertEquals(False, lockfuncs.holds(self.room2, self.char1))
def test_has_account(self):
self.assertEquals(True, lockfuncs.has_account(self.char1, None))
self.assertEquals(False, lockfuncs.has_account(self.obj1, None))
@override_settings(IRC_ENABLED=True, TESTVAL=[1, 2, 3])
def test_serversetting(self):
# import pudb
# pudb.set_trace()
self.assertEquals(True, lockfuncs.serversetting(None, None, 'IRC_ENABLED', 'True'))
self.assertEquals(True, lockfuncs.serversetting(None, None, 'TESTVAL', '[1, 2, 3]'))
self.assertEquals(False, lockfuncs.serversetting(None, None, 'TESTVAL', '[1, 2, 4]'))
self.assertEquals(False, lockfuncs.serversetting(None, None, 'TESTVAL', '123'))