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.at_account_creation()
permissions = settings.PERMISSION_ACCOUNT_DEFAULT
permissions = [settings.PERMISSION_ACCOUNT_DEFAULT]
if hasattr(self, "_createdict"):
# this will only be set if the utils.create_account
# 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 ast import literal_eval
from django.conf import settings
from evennia.utils import utils
_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):
@ -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
gtmode = kwargs.pop("_greater_than", False)
try:
permission = args[0].lower()
perms_object = [p.lower() for p in accessing_obj.permissions.all()]
perms_object = accessing_obj.permissions.all()
except (AttributeError, IndexError):
return False
if utils.inherits_from(accessing_obj, "evennia.objects.objects.DefaultObject") and accessing_obj.account:
account = accessing_obj.account
# we strip eventual plural forms, so Builders == Builder
perms_account = [p.lower().rstrip("s") for p in account.permissions.all()]
gtmode = kwargs.pop("_greater_than", False)
is_quell = False
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")
if permission in _PERMISSION_HIERARCHY:
# check hierarchy without allowing escalation obj->account
hpos_target = _PERMISSION_HIERARCHY.index(permission)
hpos_account = [hpos for hpos, hperm in enumerate(_PERMISSION_HIERARCHY) if hperm in perms_account]
# Check hirarchy matches; handle both singular/plural forms in hierarchy
hpos_target = None
if permission in _PERMISSION_HIERARCHY:
hpos_target = _PERMISSION_HIERARCHY.index(permission)
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
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_account, hpos_object)
else:
return hpos_target <= min(hpos_account, hpos_object)
elif gtmode:
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
if account and is_quell:
# quell mode: use smallest perm from account and object
if gtmode:
return hpos_target < min(hpos_account, hpos_object)
else:
return hpos_target <= min(hpos_account, hpos_object)
elif account:
# use account perm
if gtmode:
return hpos_target < hpos_account
else:
return hpos_target <= hpos_account
elif not is_quell and permission in perms_account:
# if we get here, check account perms first, otherwise
# continue as normal
else:
# use object perm
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
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
@ -229,7 +261,6 @@ def pperm(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):
"""
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
tagkey = args[0] if args 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):
@ -494,7 +525,7 @@ def objtag(accessing_obj, accessed_obj, *args, **kwargs):
Only true if accessed_obj has the specified tag and optional
category.
"""
return accessed_obj.tags.get(*args)
return bool(accessed_obj.tags.get(*args))
def inside(accessing_obj, accessed_obj, *args, **kwargs):
@ -592,7 +623,9 @@ def serversetting(accessing_obj, accessed_obj, *args, **kwargs):
serversetting(IRC_ENABLED)
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]:
return False
@ -602,12 +635,12 @@ def serversetting(accessing_obj, accessed_obj, *args, **kwargs):
else:
setting, val = args[0], args[1]
# convert
if val == 'True':
val = True
elif val == 'False':
val = False
elif val.isdigit():
val = int(val)
try:
val = literal_eval(val)
except Exception:
# we swallow errors here, lockfuncs has noone to report to
return False
if setting in settings._wrapped.__dict__:
return settings._wrapped.__dict__[setting] == val
return False

View file

@ -11,10 +11,11 @@ from evennia.utils.test_resources import EvenniaTest
try:
# 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:
from django.test import TestCase
from django.test import TestCase, override_settings
from evennia import settings_default
from evennia.locks import lockfuncs
# ------------------------------------------------------------
@ -25,7 +26,8 @@ from evennia.locks import lockfuncs
class TestLockCheck(EvenniaTest):
def testrun(self):
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.assertEquals(True, self.obj1.locks.check(self.obj2, 'owner'))
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(True, self.obj1.locks.check(self.obj2, 'not_exist', default=True))
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.assertEquals(True, lockfuncs.true(self.obj2, self.obj1))
self.assertEquals(False, lockfuncs.false(self.obj2, self.obj1))
self.assertEquals(True, lockfuncs.perm(self.obj2, self.obj1, 'Admin'))
self.assertEquals(True, lockfuncs.perm_above(self.obj2, self.obj1, 'Builder'))
self.assertEquals(False, lockfuncs.perm(self.obj2, None, 'Developer'))
self.assertEquals(False, lockfuncs.perm(self.obj2, None, 'Developers'))
self.assertEquals(True, lockfuncs.perm(self.obj2, None, 'Admin'))
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
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.assertEquals(True, lockfuncs.attr(self.obj2, self.obj1, 'testattr', '45'))
self.assertEquals(False, lockfuncs.attr_gt(self.obj2, self.obj1, 'testattr', '45'))
self.assertEquals(True, lockfuncs.attr_ge(self.obj2, self.obj1, 'testattr', '45'))
self.assertEquals(False, lockfuncs.attr_lt(self.obj2, self.obj1, 'testattr', '45'))
self.assertEquals(True, lockfuncs.attr_le(self.obj2, self.obj1, 'testattr', '45'))
self.assertEquals(False, lockfuncs.attr_ne(self.obj2, self.obj1, 'testattr', '45'))
self.assertEquals(True, lockfuncs.attr(self.obj2, None, 'testattr', '45'))
self.assertEquals(False, lockfuncs.attr_gt(self.obj2, None, 'testattr', '45'))
self.assertEquals(True, lockfuncs.attr_ge(self.obj2, None, 'testattr', '45'))
self.assertEquals(False, lockfuncs.attr_lt(self.obj2, None, 'testattr', '45'))
self.assertEquals(True, lockfuncs.attr_le(self.obj2, None, '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'))