diff --git a/evennia/accounts/accounts.py b/evennia/accounts/accounts.py index 626c4a60f3..c127e71494 100644 --- a/evennia/accounts/accounts.py +++ b/evennia/accounts/accounts.py @@ -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. diff --git a/evennia/locks/lockfuncs.py b/evennia/locks/lockfuncs.py index e30263adbf..27de4a59b3 100644 --- a/evennia/locks/lockfuncs.py +++ b/evennia/locks/lockfuncs.py @@ -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 diff --git a/evennia/locks/tests.py b/evennia/locks/tests.py index 0c47456ede..6e7e1a48e6 100644 --- a/evennia/locks/tests.py +++ b/evennia/locks/tests.py @@ -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'))