Further cleanup of source; making class methods _private for clarity in the API.

This commit is contained in:
Griatch 2012-03-31 15:09:22 +02:00
parent fc156b5a54
commit c8df141e89
18 changed files with 607 additions and 588 deletions

View file

@ -3,13 +3,13 @@ This module provides a set of permission lock functions for use
with Evennia's permissions system.
To call these locks, make sure this module is included in the
settings tuple PERMISSION_FUNC_MODULES then define a lock on the form
'<access_type>:func(args)' and add it to the object's lockhandler.
Run the access() method of the handler to execute the lock check.
settings tuple PERMISSION_FUNC_MODULES then define a lock on the form
'<access_type>:func(args)' and add it to the object's lockhandler.
Run the access() method of the handler to execute the lock check.
Note that accessing_obj and accessed_obj can be any object type
with a lock variable/field, so be careful to not expect
a certain object type.
a certain object type.
Appendix: MUX locks
@ -22,7 +22,7 @@ available like the MUX ones. So many of these are not available in
basic Evennia, but could all be implemented easily if needed for the
individual game.
MUX Name: Affects: Effect:
MUX Name: Affects: Effect:
-------------------------------------------------------------------------------
DefaultLock: Exits: controls who may traverse the exit to
its destination.
@ -34,43 +34,43 @@ DefaultLock: Exits: controls who may traverse the exit to
Players/Things: controls who may GET the object.
Evennia: "get:<lockfunc()"
EnterLock: Players/Things: controls who may ENTER the object
Evennia:
Evennia:
GetFromLock: All but Exits: controls who may gets things from a given
location.
Evennia:
Evennia:
GiveLock: Players/Things: controls who may give the object.
Evennia:
Evennia:
LeaveLock: Players/Things: controls who may LEAVE the object.
Evennia:
Evennia:
LinkLock: All but Exits: controls who may link to the location if the
location is LINK_OK (for linking exits or
setting drop-tos) or ABODE (for setting
homes)
Evennia:
Evennia:
MailLock: Players: controls who may @mail the player.
Evennia:
Evennia:
OpenLock: All but Exits: controls who may open an exit.
Evennia:
Evennia:
PageLock: Players: controls who may page the player.
Evennia: "send:<lockfunc()>"
ParentLock: All: controls who may make @parent links to the
object.
Evennia: Typeclasses and "puppet:<lockstring()>"
ReceiveLock: Players/Things: controls who may give things to the object.
Evennia:
Evennia:
SpeechLock: All but Exits: controls who may speak in that location
Evennia:
Evennia:
TeloutLock: All but Exits: controls who may teleport out of the
location.
Evennia:
Evennia:
TportLock: Rooms/Things: controls who may teleport there
Evennia:
Evennia:
UseLock: All but Exits: controls who may USE the object, GIVE the
object money and have the PAY attributes
run, have their messages heard and possibly
acted on by LISTEN and AxHEAR, and invoke
$-commands stored on the object.
Evennia: Commands and Cmdsets.
Evennia: Commands and Cmdsets.
DropLock: All but rooms: controls who may drop that object.
Evennia:
VisibleLock: All: Controls object visibility when the object
@ -85,7 +85,7 @@ from django.conf import settings
from src.utils import search
from src.utils import utils
PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
_PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
def _to_player(accessing_obj):
"Helper function. Makes sure an accessing object is a player object"
@ -95,85 +95,85 @@ def _to_player(accessing_obj):
return accessing_obj
# lock functions
# lock functions
def true(*args, **kwargs):
"Always returns True."
return True
def all(*args, **kwargs):
return True
return True
def false(*args, **kwargs):
"Always returns False"
return False
return False
def none(*args, **kwargs):
return False
return False
def perm(accessing_obj, accessed_obj, *args, **kwargs):
"""
The basic permission-checker. Ignores case.
The basic permission-checker. Ignores case.
Usage:
Usage:
perm(<permission>)
where <permission> is the permission accessing_obj must
have in order to pass the lock. If the given permission
is part of PERMISSION_HIERARCHY, permission is also granted
to all ranks higher up in the hierarchy.
have in order to pass the lock. If the given permission
is part of _PERMISSION_HIERARCHY, permission is also granted
to all ranks higher up in the hierarchy.
"""
try:
perm = args[0].lower()
permissions = [p.lower() for p in accessing_obj.permissions]
except AttributeError, IndexError:
return False
return False
if perm in permissions:
# simplest case - we have a direct match
return True
if perm in PERMISSION_HIERARCHY:
return True
if perm in _PERMISSION_HIERARCHY:
# check if we have a higher hierarchy position
ppos = PERMISSION_HIERARCHY.index(perm)
return any(1 for hpos, hperm in enumerate(PERMISSION_HIERARCHY)
ppos = _PERMISSION_HIERARCHY.index(perm)
return any(1 for hpos, hperm in enumerate(_PERMISSION_HIERARCHY)
if hperm in permissions and hpos > ppos)
return False
return False
def perm_above(accessing_obj, accessed_obj, *args, **kwargs):
"""
Only allow objects with a permission *higher* in the permission
hierarchy than the one given. If there is no such higher rank,
hierarchy than the one given. If there is no such higher rank,
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:
perm = args[0].lower()
except IndexError:
return False
return False
if perm in PERMISSION_HIERARCHY:
ppos = PERMISSION_HIERARCHY.index(perm)
return any(1 for hpos, hperm in enumerate(PERMISSION_HIERARCHY)
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
return False
def pperm(accessing_obj, accessed_obj, *args, **kwargs):
"""
The basic permission-checker for Player objects. Ignores case.
The basic permission-checker for Player objects. Ignores case.
Usage:
Usage:
pperm(<permission>)
where <permission> is the permission accessing_obj must
have in order to pass the lock. If the given permission
is part of PERMISSION_HIERARCHY, permission is also granted
to all ranks higher up in the hierarchy.
have in order to pass the lock. If the given permission
is part of _PERMISSION_HIERARCHY, permission is also granted
to all ranks higher up in the hierarchy.
"""
return perm(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
def pperm_above(accessing_obj, accessed_obj, *args, **kwargs):
"""
Only allow Player objects with a permission *higher* in the permission
hierarchy than the one given. If there is no such higher rank,
hierarchy than the one given. If there is no such higher rank,
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.
"""
return perm_above(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
@ -181,7 +181,7 @@ def dbref(accessing_obj, accessed_obj, *args, **kwargs):
"""
Usage:
dbref(3)
This lock type checks if the checking object
has a particular dbref. Note that this only
works for checking objects that are stored
@ -195,7 +195,7 @@ def dbref(accessing_obj, accessed_obj, *args, **kwargs):
return False
if hasattr(accessing_obj, 'id'):
return dbref == accessing_obj.id
return False
return False
def pdbref(accessing_obj, accessed_obj, *args, **kwargs):
"""
@ -212,7 +212,7 @@ def pid(accessing_obj, accessed_obj, *args, **kwargs):
return dbref(_to_player(accessing_obj), accessed_obj, *args, **kwargs)
# this is more efficient than multiple if ... elif statments
# this is more efficient than multiple if ... elif statments
CF_MAPPING = {'eq': lambda val1, val2: val1 == val2 or int(val1) == int(val2),
'gt': lambda val1, val2: int(val1) > int(val2),
'lt': lambda val1, val2: int(val1) < int(val2),
@ -232,7 +232,7 @@ def attr(accessing_obj, accessed_obj, *args, **kwargs):
how the value should be compared with one on accessing_obj (so
compare=gt means the accessing_obj must have a value greater than
the one given).
Searches attributes *and* properties stored on the checking
object. The first form works like a flag - if the
attribute/property exists on the object, the value is checked for
@ -240,17 +240,17 @@ def attr(accessing_obj, accessed_obj, *args, **kwargs):
attribute/property matches. Note that all retrieved values will be
converted to strings before doing the comparison.
"""
# deal with arguments
# deal with arguments
if not args:
return False
attrname = args[0].strip()
value = None
value = None
if len(args) > 1:
value = args[1].strip()
compare = 'eq'
if kwargs:
compare = kwargs.get('compare', 'eq')
def valcompare(val1, val2, typ='eq'):
"compare based on type"
try:
@ -258,20 +258,20 @@ def attr(accessing_obj, accessed_obj, *args, **kwargs):
except Exception, e:
#print e
# this might happen if we try to compare two things that cannot be compared
return False
return False
# first, look for normal properties on the object trying to gain access
# first, look for normal properties on the object trying to gain access
if hasattr(accessing_obj, attrname):
if value:
return valcompare(str(getattr(accessing_obj, attrname)), value, compare)
return valcompare(str(getattr(accessing_obj, attrname)), value, compare)
return bool(getattr(accessing_obj, attrname)) # will return Fail on False value etc
# check attributes, if they exist
# check attributes, if they exist
if (hasattr(accessing_obj, 'has_attribute') and accessing_obj.has_attribute(attrname)):
if value:
return (hasattr(accessing_obj, 'get_attribute')
return (hasattr(accessing_obj, 'get_attribute')
and valcompare(accessing_obj.get_attribute(attrname), value, compare))
return bool(accessing_obj.get_attribute(attrname)) # fails on False/None values
return False
return False
def objattr(accessing_obj, accessed_obj, *args, **kwargs):
"""
@ -280,7 +280,7 @@ def objattr(accessing_obj, accessed_obj, *args, **kwargs):
objattr(attrname, value)
objattr(attrname, value, compare=type)
Works like attr, except it looks for an attribute on
Works like attr, except it looks for an attribute on
accessing_obj.obj, if such an entity exists. Suitable
for commands.
@ -295,8 +295,8 @@ def locattr(accessing_obj, accessed_obj, *args, **kwargs):
locattr(attrname, value)
locattr(attrname, value, compare=type)
Works like attr, except it looks for an attribute on
accessing_obj.location, if such an entity exists.
Works like attr, except it looks for an attribute on
accessing_obj.location, if such an entity exists.
"""
if hasattr(accessing_obj, "location"):
@ -305,14 +305,14 @@ def locattr(accessing_obj, accessed_obj, *args, **kwargs):
def attr_eq(accessing_obj, accessed_obj, *args, **kwargs):
"""
Usage:
Usage:
attr_gt(attrname, 54)
"""
return attr(accessing_obj, accessed_obj, *args, **kwargs)
def attr_gt(accessing_obj, accessed_obj, *args, **kwargs):
"""
Usage:
Usage:
attr_gt(attrname, 54)
Only true if access_obj's attribute > the value given.
@ -320,7 +320,7 @@ def attr_gt(accessing_obj, accessed_obj, *args, **kwargs):
return attr(accessing_obj, accessed_obj, *args, **{'compare':'gt'})
def attr_ge(accessing_obj, accessed_obj, *args, **kwargs):
"""
Usage:
Usage:
attr_gt(attrname, 54)
Only true if access_obj's attribute >= the value given.
@ -328,7 +328,7 @@ def attr_ge(accessing_obj, accessed_obj, *args, **kwargs):
return attr(accessing_obj, accessed_obj, *args, **{'compare':'ge'})
def attr_lt(accessing_obj, accessed_obj, *args, **kwargs):
"""
Usage:
Usage:
attr_gt(attrname, 54)
Only true if access_obj's attribute < the value given.
@ -336,7 +336,7 @@ def attr_lt(accessing_obj, accessed_obj, *args, **kwargs):
return attr(accessing_obj, accessed_obj, *args, **{'compare':'lt'})
def attr_le(accessing_obj, accessed_obj, *args, **kwargs):
"""
Usage:
Usage:
attr_gt(attrname, 54)
Only true if access_obj's attribute <= the value given.
@ -344,7 +344,7 @@ def attr_le(accessing_obj, accessed_obj, *args, **kwargs):
return attr(accessing_obj, accessed_obj, *args, **{'compare':'le'})
def attr_ne(accessing_obj, accessed_obj, *args, **kwargs):
"""
Usage:
Usage:
attr_gt(attrname, 54)
Only true if access_obj's attribute != the value given.
@ -353,7 +353,7 @@ def attr_ne(accessing_obj, accessed_obj, *args, **kwargs):
def holds(accessing_obj, accessed_obj, *args, **kwargs):
"""
Usage:
Usage:
holds() # checks if accessed_obj or accessed_obj.obj is held by accessing_obj
holds(key/dbref) # checks if accessing_obj holds an object with given key/dbref
@ -364,50 +364,50 @@ def holds(accessing_obj, accessed_obj, *args, **kwargs):
try:
# commands and scripts don't have contents, so we are usually looking
# for the contents of their .obj property instead (i.e. the object the
# command/script is attached to).
# command/script is attached to).
contents = accessing_obj.contents
except AttributeError:
try:
try:
contents = accessing_obj.obj.contents
except AttributeError:
return False
return False
def check_holds(objid):
# helper function. Compares both dbrefs and keys/aliases.
objid = str(objid)
dbref = utils.dbref(objid)
dbref = utils.dbref(objid)
if dbref and any((True for obj in contents if obj.id == dbref)):
return True
return True
objid = objid.lower()
return any((True for obj in contents
return any((True for obj in contents
if obj.key.lower() == objid or objid in [al.lower() for al in obj.aliases]))
if args and args[0]:
return check_holds(args[0])
else:
else:
try:
if check_holds(accessed_obj.id):
#print "holds: accessed_obj.id - True"
return True
except Exception:
pass
pass
#print "holds: accessed_obj.obj.id -", hasattr(accessed_obj, "obj") and check_holds(accessed_obj.obj.id)
return hasattr(accessed_obj, "obj") and check_holds(accessed_obj.obj.id)
def superuser(*args, **kwargs):
"""
Only accepts an accesing_obj that is superuser (e.g. user #1)
Since a superuser would not ever reach this check (superusers
bypass the lock entirely), any user who gets this far cannot be a
superuser, hence we just return False. :)
"""
return False
return False
def serversetting(accessing_obj, accessed_obj, *args, **kwargs):
"""
Only returns true if the Evennia settings exists, alternatively has a certain value.
Usage:
serversetting(IRC_ENABLED)
serversetting(BASE_SCRIPT_PATH, [game.gamesrc.scripts])
@ -418,16 +418,16 @@ def serversetting(accessing_obj, accessed_obj, *args, **kwargs):
return False
if len(args) < 2:
setting = args[0]
val = "True"
val = "True"
else:
setting, val = args[0], args[1]
# convert
if val == 'True':
val = True
elif val == 'False':
val = False
val = False
elif val.isdigit():
val = int(val)
if setting in settings._wrapped.__dict__:
return settings._wrapped.__dict__[setting] == val
return False
return False

View file

@ -5,15 +5,15 @@ A lock defines access to a particular subsystem or property of
Evennia. For example, the "owner" property can be impmemented as a
lock. Or the disability to lift an object or to ban users.
A lock consists of three parts:
A lock consists of three parts:
- access_type - this defines what kind of access this lock regulates. This
just a string.
just a string.
- function call - this is one or many calls to functions that will determine
if the lock is passed or not.
- lock function(s). These are regular python functions with a special
- lock function(s). These are regular python functions with a special
set of allowed arguments. They should always return a boolean depending
on if they allow access or not.
on if they allow access or not.
# Lock function
@ -26,26 +26,26 @@ take four arguments looking like this:
The accessing object is the object wanting to gain access.
The accessed object is the object this lock resides on
args and kwargs will hold optional arguments and/or keyword arguments
args and kwargs will hold optional arguments and/or keyword arguments
to the function as a list and a dictionary respectively.
Example:
perm(accessing_obj, accessed_obj, *args, **kwargs):
"Checking if the object has a particular, desired permission"
if args:
desired_perm = args[0]
desired_perm = args[0]
return desired_perm in accessing_obj.permissions
return False
return False
Lock functions should most often be pretty general and ideally possible to
re-use and combine in various ways to build clever locks.
re-use and combine in various ways to build clever locks.
# Lock definition ("Lock string")
A lock definition is a string with a special syntax. It is added to
each object's lockhandler, making that lock available from then on.
A lock definition is a string with a special syntax. It is added to
each object's lockhandler, making that lock available from then on.
The lock definition looks like this:
@ -54,15 +54,15 @@ The lock definition looks like this:
That is, the access_type, a colon followed by calls to lock functions
combined with AND or OR. NOT negates the result of the following call.
Example:
We want to limit who may edit a particular object (let's call this access_type
Example:
We want to limit who may edit a particular object (let's call this access_type
for 'edit', it depends on what the command is looking for). We want this to
only work for those with the Permission 'Builders'. So we use our lock
function above and define it like this:
'edit:perm(Builders)'
Here, the lock-function perm() will be called with the string
'Builders' (accessing_obj and accessed_obj are added automatically,
you only need to add the args/kwargs, if any).
@ -73,17 +73,17 @@ could use AND:
'edit:perm(Builders) AND perm(GoodGuy)'
To allow EITHER Builders and GoodGuys, we replace AND with OR. perm() is just one example,
the lock function can do anything and compare any properties of the calling object to
the lock function can do anything and compare any properties of the calling object to
decide if the lock is passed or not.
'lift:attrib(very_strong) AND NOT attrib(bad_back)'
To make these work, add the string to the lockhandler of the object you want
to apply the lock to:
to apply the lock to:
obj.lockhandler.add('edit:perm(Builders)')
From then on, a command that wants to check for 'edit' access on this
From then on, a command that wants to check for 'edit' access on this
object would do something like this:
if not target_obj.lockhandler.has_perm(caller, 'edit'):
@ -93,8 +93,8 @@ All objects also has a shortcut called 'access' that is recommended to use inste
if not target_obj.access(caller, 'edit'):
caller.msg("Sorry, you cannot edit that.")
# Permissions
# Permissions
Permissions are just text strings stored in a comma-separated list on
typeclassed objects. The default perm() lock function uses them,
@ -106,8 +106,8 @@ to any other identifier you can use.
import re, inspect
from django.conf import settings
from src.utils import logger, utils
from src.utils import logger, utils
__all__ = ("LockHandler", )
#
# Exception class
#
@ -120,17 +120,17 @@ class LockException(Exception):
# Cached lock functions
#
LOCKFUNCS = {}
def cache_lockfuncs():
_LOCKFUNCS = {}
def _cache_lockfuncs():
"Updates the cache."
global LOCKFUNCS
LOCKFUNCS = {}
global _LOCKFUNCS
_LOCKFUNCS = {}
for modulepath in settings.LOCK_FUNC_MODULES:
modulepath = utils.pypath_to_realpath(modulepath)
mod = utils.mod_import(modulepath)
if mod:
if mod:
for tup in (tup for tup in inspect.getmembers(mod) if callable(tup[1])):
LOCKFUNCS[tup[0]] = tup[1]
_LOCKFUNCS[tup[0]] = tup[1]
else:
logger.log_errmsg("Couldn't load %s from PERMISSION_FUNC_MODULES." % modulepath)
@ -138,21 +138,21 @@ def cache_lockfuncs():
# pre-compiled regular expressions
#
RE_FUNCS = re.compile(r"\w+\([^)]*\)")
RE_SEPS = re.compile(r"(?<=[ )])AND(?=\s)|(?<=[ )])OR(?=\s)|(?<=[ )])NOT(?=\s)")
RE_OK = re.compile(r"%s|and|or|not")
_RE_FUNCS = re.compile(r"\w+\([^)]*\)")
_RE_SEPS = re.compile(r"(?<=[ )])AND(?=\s)|(?<=[ )])OR(?=\s)|(?<=[ )])NOT(?=\s)")
_RE_OK = re.compile(r"%s|and|or|not")
#
#
# Lock handler
# Lock handler
#
#
class LockHandler(object):
"""
This handler should be attached to all objects implementing
permission checks, under the property 'lockhandler'.
This handler should be attached to all objects implementing
permission checks, under the property 'lockhandler'.
"""
def __init__(self, obj):
@ -160,13 +160,13 @@ class LockHandler(object):
Loads and pre-caches all relevant locks and their
functions.
"""
if not LOCKFUNCS:
cache_lockfuncs()
if not _LOCKFUNCS:
_cache_lockfuncs()
self.obj = obj
self.locks = {}
self.log_obj = None
self.locks = {}
self.log_obj = None
self.no_errors = True
self.reset_flag = False
self.reset_flag = False
self._cache_locks(self.obj.lock_storage)
@ -177,7 +177,7 @@ class LockHandler(object):
def _log_error(self, message):
"Try to log errors back to object"
if self.log_obj and hasattr(self.log_obj, 'msg'):
self.log_obj.msg(message)
self.log_obj.msg(message)
elif hasattr(self.obj, 'msg'):
self.obj.msg(message)
else:
@ -192,12 +192,12 @@ class LockHandler(object):
"""
locks = {}
if not storage_lockstring:
return locks
return locks
nlocks = storage_lockstring.count(';') + 1
duplicates = 0
elist = [] # errors
elist = [] # errors
wlist = [] # warnings
for raw_lockstring in storage_lockstring.split(';'):
for raw_lockstring in storage_lockstring.split(';'):
lock_funcs = []
try:
access_type, rhs = (part.strip() for part in raw_lockstring.split(':', 1))
@ -206,42 +206,42 @@ class LockHandler(object):
return locks
# parse the lock functions and separators
funclist = RE_FUNCS.findall(rhs)
funclist = _RE_FUNCS.findall(rhs)
evalstring = rhs.replace('AND','and').replace('OR','or').replace('NOT','not')
nfuncs = len(funclist)
for funcstring in funclist:
for funcstring in funclist:
funcname, rest = (part.strip().strip(')') for part in funcstring.split('(', 1))
func = LOCKFUNCS.get(funcname, None)
func = _LOCKFUNCS.get(funcname, None)
if not callable(func):
elist.append("Lock: function '%s' is not available." % funcstring)
continue
continue
args = list(arg.strip() for arg in rest.split(',') if not '=' in arg)
kwargs = dict([arg.split('=', 1) for arg in rest.split(',') if '=' in arg])
kwargs = dict([arg.split('=', 1) for arg in rest.split(',') if '=' in arg])
lock_funcs.append((func, args, kwargs))
evalstring = evalstring.replace(funcstring, '%s')
if len(lock_funcs) < nfuncs:
evalstring = evalstring.replace(funcstring, '%s')
if len(lock_funcs) < nfuncs:
continue
try:
# purge the eval string of any superfluos items, then test it
evalstring = " ".join(RE_OK.findall(evalstring))
evalstring = " ".join(_RE_OK.findall(evalstring))
eval(evalstring % tuple(True for func in funclist))
except Exception:
elist.append("Lock: definition '%s' has syntax errors." % raw_lockstring)
continue
if access_type in locks:
if access_type in locks:
duplicates += 1
wlist.append("Lock: access type '%s' changed from '%s' to '%s' " % \
(access_type, locks[access_type][2], raw_lockstring))
locks[access_type] = (evalstring, tuple(lock_funcs), raw_lockstring)
locks[access_type] = (evalstring, tuple(lock_funcs), raw_lockstring)
if wlist and self.log_obj:
# a warning text was set, it's not an error, so only report if log_obj is available.
self._log_error("\n".join(wlist))
if elist:
# an error text was set, raise exception.
raise LockException("\n".join(elist))
# an error text was set, raise exception.
raise LockException("\n".join(elist))
self.no_errors = False
# return the gathered locks in an easily executable form
return locks
# return the gathered locks in an easily executable form
return locks
def _cache_locks(self, storage_lockstring):
"""Store data"""
@ -253,29 +253,29 @@ class LockHandler(object):
def add(self, lockstring, log_obj=None):
"""
Add a new lockstring on the form '<access_type>:<functions>'. Multiple
access types should be separated by semicolon (;).
Add a new lockstring on the form '<access_type>:<functions>'. Multiple
access types should be separated by semicolon (;).
If log_obj is given, it will be fed error information.
"""
if log_obj:
self.log_obj = log_obj
self.no_errors = True
# sanity checks
for lockdef in lockstring.split(';'):
self.no_errors = True
# sanity checks
for lockdef in lockstring.split(';'):
if not ':' in lockstring:
self._log_error("Lock: '%s' contains no colon (:)." % lockdef)
return False
return False
access_type, rhs = [part.strip() for part in lockdef.split(':', 1)]
if not access_type:
self._log_error("Lock: '%s' has no access_type (left-side of colon is empty)." % lockdef)
return False
return False
if rhs.count('(') != rhs.count(')'):
self._log_error("Lock: '%s' has mismatched parentheses." % lockdef)
return False
if not RE_FUNCS.findall(rhs):
return False
if not _RE_FUNCS.findall(rhs):
self._log_error("Lock: '%s' has no valid lock functions." % lockdef)
return False
return False
# get the lock string
storage_lockstring = self.obj.lock_storage
if storage_lockstring:
@ -285,8 +285,8 @@ class LockHandler(object):
# cache the locks will get rid of eventual doublets
self._cache_locks(storage_lockstring)
self._save_locks()
self.log_obj = None
return self.no_errors
self.log_obj = None
return self.no_errors
def get(self, access_type):
"get the lockstring of a particular type"
@ -297,8 +297,8 @@ class LockHandler(object):
if access_type in self.locks:
del self.locks[access_type]
self._save_locks()
return True
return False
return True
return False
def clear(self):
"Remove all locks"
@ -307,10 +307,10 @@ class LockHandler(object):
def reset(self):
"""
Set the reset flag, so the the lock will be re-cached at next checking.
This is usually set by @reload.
This is usually set by @reload.
"""
self.reset_flag = True
def check(self, accessing_obj, access_type, default=False, no_superuser_bypass=False):
"""
Checks a lock of the correct type by passing execution
@ -322,8 +322,8 @@ class LockHandler(object):
no_superuser_bypass - don't use this unless you really, really need to,
it makes supersusers susceptible to the lock check.
A lock is executed in the follwoing way:
A lock is executed in the follwoing way:
Parsing the lockstring, we (during cache) extract the valid
lock functions and store their function objects in the right
order along with their args/kwargs. These are now executed in
@ -340,11 +340,11 @@ class LockHandler(object):
"""
if self.reset_flag:
# rebuild cache
# rebuild cache
self._cache_locks(self.obj.lock_storage)
self.reset_flag = False
self.reset_flag = False
if (not no_superuser_bypass
if (not no_superuser_bypass
and ((hasattr(accessing_obj, 'is_superuser') and accessing_obj.is_superuser)
or (hasattr(accessing_obj, 'player') and hasattr(accessing_obj.player, 'is_superuser') and accessing_obj.player.is_superuser)
or (hasattr(accessing_obj, 'get_player') and (accessing_obj.get_player()==None or accessing_obj.get_player().is_superuser)))):
@ -353,11 +353,11 @@ class LockHandler(object):
return True
if access_type in self.locks:
# we have a lock, test it.
evalstring, func_tup, raw_string = self.locks[access_type]
# we have a lock, test it.
evalstring, func_tup, raw_string = self.locks[access_type]
# execute all lock funcs in the correct order, producing a tuple of True/False results.
true_false = tuple(bool(tup[0](accessing_obj, self.obj, *tup[1], **tup[2])) for tup in func_tup)
# the True/False tuple goes into evalstring, which combines them
# the True/False tuple goes into evalstring, which combines them
# with AND/OR/NOT in order to get the final result.
return eval(evalstring % true_false)
else:
@ -367,22 +367,22 @@ class LockHandler(object):
"""
Do a direct check against a lockstring ('atype:func()..'), without any
intermediary storage on the accessed object (this can be left
to None if the lock functions called don't access it). atype can also be
to None if the lock functions called don't access it). atype can also be
put to a dummy value since no lock selection is made.
"""
if ((hasattr(accessing_obj, 'is_superuser') and accessing_obj.is_superuser)
or (hasattr(accessing_obj, 'player') and hasattr(accessing_obj.player, 'is_superuser') and accessing_obj.player.is_superuser)
or (hasattr(accessing_obj, 'get_player') and (accessing_obj.get_player()==None or accessing_obj.get_player().is_superuser))):
return True
return True
locks = self. _parse_lockstring(lockstring)
for access_type in locks:
evalstring, func_tup, raw_string = locks[access_type]
true_false = tuple(tup[0](accessing_obj, self.obj, *tup[1], **tup[2]) for tup in func_tup)
return eval(evalstring % true_false)
def test():
# testing
def _test():
# testing
class TestObj(object):
pass
@ -390,9 +390,9 @@ def test():
import pdb
obj1 = TestObj()
obj2 = TestObj()
#obj1.lock_storage = "owner:dbref(#4);edit:dbref(#5) or perm(Wizards);examine:perm(Builders);delete:perm(Wizards);get:all()"
#obj1.lock_storage = "cmd:all();admin:id(1);listen:all();send:all()"
#obj1.lock_storage = "cmd:all();admin:id(1);listen:all();send:all()"
obj1.lock_storage = "listen:perm(Immortals)"
pdb.set_trace()

View file

@ -2,7 +2,7 @@
"""
This is part of Evennia's unittest framework, for testing
the stability and integrrity of the codebase during updates.
the stability and integrrity of the codebase during updates.
This module tests the lock functionality of Evennia.
@ -49,7 +49,7 @@ class TestLockfuncs(LockTest):
def testrun(self):
self.obj2.permissions = ['Wizards']
self.assertEquals(True, lockfuncs.true(self.obj2, self.obj1))
self.assertEquals(False, lockfuncs.false(self.obj2, self.obj1))
self.assertEquals(False, lockfuncs.false(self.obj2, self.obj1))
self.assertEquals(True, lockfuncs.perm(self.obj2, self.obj1, 'Wizards'))
self.assertEquals(True, lockfuncs.perm_above(self.obj2, self.obj1, 'Builders'))
dbref = self.obj2.dbref