diff --git a/evennia/locks/tests.py b/evennia/locks/tests.py index c078b597ea..ebd21b3122 100644 --- a/evennia/locks/tests.py +++ b/evennia/locks/tests.py @@ -208,3 +208,39 @@ class TestLockfuncs(EvenniaTest): self.assertEqual(True, lockfuncs.serversetting(None, None, "TESTVAL", "[1, 2, 3]")) self.assertEqual(False, lockfuncs.serversetting(None, None, "TESTVAL", "[1, 2, 4]")) self.assertEqual(False, lockfuncs.serversetting(None, None, "TESTVAL", "123")) + + +class TestPermissionCheck(EvenniaTest): + """ + Test the PermissionHandler.check method + + """ + def test_check__success(self): + """Test combinations that should pass the check""" + self.assertEqual( + [perm for perm in self.char1.account.permissions.all()], + ['developer', 'player'] + + ) + self.assertTrue(self.char1.permissions.check("Builder")) + self.assertTrue(self.char1.permissions.check("Builder", "Player")) + self.assertTrue(self.char1.permissions.check("Builder", "dummy")) + self.assertTrue(self.char1.permissions.check("Developer", "dummy", "foobar")) + self.assertTrue(self.char1.permissions.check("Builder", "Player", require_all=True)) + + def test_check__fail(self): + """Test combinations that should fail the check""" + self.assertFalse(self.char1.permissions.check("dummy")) + self.assertFalse(self.char1.permissions.check("Builder", "dummy", require_all=True)) + self.assertFalse(self.char1.permissions.check("Developer", "foobar", require_all=True)) + + self.char1.account.permissions.remove('developer') + self.char1.account.permissions.add("Builder") + + self.assertEqual( + [perm for perm in self.char1.account.permissions.all()], + ['builder', 'player'] + ) + self.assertFalse(self.char1.permissions.check("Developer")) + self.assertFalse(self.char1.permissions.check("Developer", "Player", require_all=True)) + self.assertFalse(self.char1.permissions.check("Player", "dummy", require_all=True)) diff --git a/evennia/typeclasses/tags.py b/evennia/typeclasses/tags.py index f49ad3a14f..56f42db4cd 100644 --- a/evennia/typeclasses/tags.py +++ b/evennia/typeclasses/tags.py @@ -14,6 +14,7 @@ from collections import defaultdict from django.conf import settings from django.db import models from evennia.utils.utils import to_str, make_iter +from evennia.locks.lockfuncs import perm as perm_lockfunc _TYPECLASS_AGGRESSIVE_CACHE = settings.TYPECLASS_AGGRESSIVE_CACHE @@ -536,3 +537,47 @@ class PermissionHandler(TagHandler): """ _tagtype = "permission" + + def check(self, *permissions, require_all=False): + """ + Straight-up check the provided permission against this handler. The check will pass if + + - any/all given permission exists on the handler (depending on if `require_all` is set). + - If handler sits on puppeted object and this is a hierarachical perm, the puppeting + Account's permission will also be included in the check, prioritizing the Account's perm + (this avoids escalation exploits by puppeting a too-high prio character) + - a permission is also considered to exist on the handler, if it is *lower* than + a permission on the handler and this is a 'hierarchical' permission given + in `settings.PERMISSION_HIERARCHY`. Example: If the 'Developer' hierarchical + perm perm is set on the handler, and we check for the 'Builder' perm, the + check will pass. + + Args: + *permissions (str): Any number of permissions to check. By default, + the permission is passed if any of these (or higher, if a + hierarchical permission defined in settings.PERMISSION_HIERARCHY) + exists in the handler. Permissions are not case-sensitive. + require_all (bool): If set, *all* provided permissions much pass + the check for the entire check to pass. By default only one + needs to pass. + + Returns: + bool: If the provided permission(s) pass the check on this handler. + + Example: + :: + can_enter = obj.permissions.check("Blacksmith", "Builder") + + Notes: + This works the same way as the `perms` lockfunc and could be + replicated with a lock check against the lockstring + + "locktype: perm(perm1) OR perm(perm2) OR ..." + + (using AND for the `require_all` condition). + + """ + if require_all: + return all(perm_lockfunc(self.obj, None, perm) for perm in permissions) + else: + return any(perm_lockfunc(self.obj, None, perm) for perm in permissions)