evennia.locks.lockhandler¶
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:
access_type - this defines what kind of access this lock regulates. This 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 set of allowed arguments. They should always return a boolean depending on if they allow access or not.
A lock function is defined by existing in one of the modules listed by settings.LOCK_FUNC_MODULES. It should also always 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 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] return desired_perm in accessing_obj.permissions.all()
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.
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.
The lock definition looks like this:
‘access_type:[NOT] func1(args)[ AND|OR][NOT] func2() …’
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
for ‘edit’, it depends on what the command is looking for). We want this to only work for those with the Permission ‘Builder’. So we use our lock function above and define it like this:
‘edit:perm(Builder)’
Here, the lock-function perm() will be called with the string ‘Builder’ (accessing_obj and accessed_obj are added automatically, you only need to add the args/kwargs, if any).
If we wanted to make sure the accessing object was BOTH a Builder and a GoodGuy, we could use AND:
‘edit:perm(Builder) AND perm(GoodGuy)’
To allow EITHER Builder 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 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:
obj.lockhandler.add(‘edit:perm(Builder)’)
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’):
caller.msg(“Sorry, you cannot edit that.”)
All objects also has a shortcut called ‘access’ that is recommended to use instead:
- if not target_obj.access(caller, ‘edit’):
caller.msg(“Sorry, you cannot edit that.”)
Permissions
Permissions are just text strings stored in a comma-separated list on typeclassed objects. The default perm() lock function uses them, taking into account settings.PERMISSION_HIERARCHY. Also, the restricted @perm command sets them, but otherwise they are identical to any other identifier you can use.
-
class
evennia.locks.lockhandler.
LockHandler
(obj)[source]¶ Bases:
object
This handler should be attached to all objects implementing permission checks, under the property ‘lockhandler’.
-
__init__
(obj)[source]¶ Loads and pre-caches all relevant locks and their functions.
- Parameters
obj (object) – The object on which the lockhandler is
defined. –
-
cache_lock_bypass
(obj)[source]¶ We cache superuser bypass checks here for efficiency. This needs to be re-run when an account is assigned to a character. We need to grant access to superusers. We need to check both directly on the object (accounts), through obj.account and using the get_account() method (this sits on serversessions, in some rare cases where a check is done before the login process has yet been fully finalized)
- Parameters
obj (object) – This is checked for the is_superuser property.
-
add
(lockstring, validate_only=False)[source]¶ Add a new lockstring to handler.
- Parameters
lockstring (str or list) – A string on the form “<access_type>:<functions>”. Multiple access types should be separated by semicolon (;). Alternatively, a list with lockstrings.
validate_only (bool, optional) – If True, validate the lockstring but don’t actually store it.
- Returns
success (bool) –
- The outcome of the addition, False on
error. If validate_only is True, this will be a tuple (bool, error), for pass/fail and a string error.
-
validate
(lockstring)[source]¶ Validate lockstring syntactically, without saving it.
- Parameters
lockstring (str) – Lockstring to validate.
- Returns
valid (bool) – If validation passed or not.
-
replace
(lockstring)[source]¶ Replaces the lockstring entirely.
- Parameters
lockstring (str) – The new lock definition.
- Returns
success (bool) – False if an error occurred.
- Raises
LockException – If a critical error occurred. If so, the old string is recovered.
-
get
(access_type=None)[source]¶ Get the full lockstring or the lockstring of a particular access type.
- Parameters
access_type (str, optional) –
- Returns
lockstring (str) –
- The matched lockstring, or the full
lockstring if no access_type was given.
-
remove
(access_type)[source]¶ Remove a particular lock from the handler
- Parameters
access_type (str) – The type of lock to remove.
- Returns
success (bool) –
- If the access_type was not found
in the lock, this returns False.
-
delete
(access_type)¶ Remove a particular lock from the handler
- Parameters
access_type (str) – The type of lock to remove.
- Returns
success (bool) –
- If the access_type was not found
in the lock, this returns False.
-
reset
()[source]¶ Set the reset flag, so the the lock will be re-cached at next checking. This is usually called by @reload.
-
append
(access_type, lockstring, op='or')[source]¶ Append a lock definition to access_type if it doesn’t already exist.
- Parameters
access_type (str) – Access type.
lockstring (str) – A valid lockstring, without the operator to link it to an eventual existing lockstring.
op (str) – An operator ‘and’, ‘or’, ‘and not’, ‘or not’ used for appending the lockstring to an existing access-type.
Note
The most common use of this method is for use in commands where the user can specify their own lockstrings. This method allows the system to auto-add things like Admin-override access.
-
check
(accessing_obj, access_type, default=False, no_superuser_bypass=False)[source]¶ Checks a lock of the correct type by passing execution off to the lock function(s).
- Parameters
accessing_obj (object) – The object seeking access.
access_type (str) – The type of access wanted.
default (bool, optional) – If no suitable lock type is found, default to this result.
no_superuser_bypass (bool) – Don’t use this unless you really, really need to, it makes supersusers susceptible to the lock check.
Notes
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 sequence, creating a list of True/False values. This is put into the evalstring, which is a string of AND/OR/NOT entries separated by placeholders where each function result should go. We just put those results in and evaluate the string to get a final, combined True/False value for the lockstring.
The important bit with this solution is that the full lockstring is never blindly evaluated, and thus there (should be) no way to sneak in malign code in it. Only “safe” lock functions (as defined by your settings) are executed.
-
check_lockstring
(accessing_obj, lockstring, no_superuser_bypass=False, default=False, access_type=None)[source]¶ Do a direct check against a lockstring (‘atype:func()..’), without any intermediary storage on the accessed object.
- Parameters
accessing_obj (object or None) – The object seeking access. Importantly, this can be left unset if the lock functions don’t access it, no updating or storage of locks are made against this object in this method.
lockstring (str) – Lock string to check, on the form “access_type:lock_definition” where the access_type part can potentially be set to a dummy value to just check a lock condition.
no_superuser_bypass (bool, optional) – Force superusers to heed lock.
default (bool, optional) – Fallback result to use if access_type is set but no such access_type is found in the given lockstring.
access_type (str, bool) – If set, only this access_type will be looked up among the locks defined by lockstring.
- Returns
access (bool) – If check is passed or not.
-