evennia.locks package

This sub-package defines the lock (access) mechanism of Evennia. All lock strings are processed through the lockhandler in this package. It also contains the default lock functions used in lock definitions.

Submodules

evennia.locks.lockfuncs module

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.

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.

Appendix: MUX locks

Below is a list nicked from the MUX help file on the locks available in standard MUX. Most of these are not relevant to core Evennia since locks in Evennia are considerably more flexible and can be implemented on an individual command/typeclass basis rather than as globally 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: ———————————————————————- DefaultLock: Exits: controls who may traverse the exit to

its destination.

Evennia: “traverse:<lockfunc()>”

Rooms: controls whether the account sees the

SUCC or FAIL message for the room following the room description when looking at the room.

Evennia: Custom typeclass

Accounts/Things: controls who may GET the object.

Evennia: “get:<lockfunc()”

EnterLock: Accounts/Things: controls who may ENTER the object

Evennia:

GetFromLock: All but Exits: controls who may gets things from a
given location.

Evennia:

GiveLock: Accounts/Things: controls who may give the object.

Evennia:

LeaveLock: Accounts/Things: controls who may LEAVE the object.

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:

MailLock: Accounts: controls who may @mail the account.

Evennia:

OpenLock: All but Exits: controls who may open an exit.

Evennia:

PageLock: Accounts: controls who may page the account.

Evennia: “send:<lockfunc()>”

ParentLock: All: controls who may make @parent links to
the object.

Evennia: Typeclasses and

“puppet:<lockstring()>”

ReceiveLock: Accounts/Things: controls who may give things to the
object.

Evennia:

SpeechLock: All but Exits: controls who may speak in that location

Evennia:

TeloutLock: All but Exits: controls who may teleport out of the
location.

Evennia:

TportLock: Rooms/Things: controls who may teleport there

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.

DropLock: All but rooms: controls who may drop that object.

Evennia:

VisibleLock: All: Controls object visibility when the

object is not dark and the looker passes the lock. In DARK locations, the object must also be set LIGHT and the viewer must pass the VisibleLock.

Evennia: Room typeclass with

Dark/light script

```

evennia.locks.lockfuncs._to_account(accessing_obj)[source]

Helper function. Makes sure an accessing object is an account object

evennia.locks.lockfuncs.all(*args, **kwargs)[source]
evennia.locks.lockfuncs.attr(accessing_obj, accessed_obj, *args, **kwargs)[source]
Usage:

attr(attrname) attr(attrname, value) attr(attrname, value, compare=type)

where compare’s type is one of (eq,gt,lt,ge,le,ne) and signifies 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 accessing_obj. if accessing_obj has a property “obj”, then this is used as accessing_obj (this makes this usable for Commands too)

The first form works like a flag - if the attribute/property exists on the object, the value is checked for True/False. The second form also requires that the value of the attribute/property matches. Note that all retrieved values will be converted to strings before doing the comparison.

evennia.locks.lockfuncs.attr_eq(accessing_obj, accessed_obj, *args, **kwargs)[source]
Usage:

attr_gt(attrname, 54)

evennia.locks.lockfuncs.attr_ge(accessing_obj, accessed_obj, *args, **kwargs)[source]
Usage:

attr_gt(attrname, 54)

Only true if access_obj’s attribute >= the value given.

evennia.locks.lockfuncs.attr_gt(accessing_obj, accessed_obj, *args, **kwargs)[source]
Usage:

attr_gt(attrname, 54)

Only true if access_obj’s attribute > the value given.

evennia.locks.lockfuncs.attr_le(accessing_obj, accessed_obj, *args, **kwargs)[source]
Usage:

attr_gt(attrname, 54)

Only true if access_obj’s attribute <= the value given.

evennia.locks.lockfuncs.attr_lt(accessing_obj, accessed_obj, *args, **kwargs)[source]
Usage:

attr_gt(attrname, 54)

Only true if access_obj’s attribute < the value given.

evennia.locks.lockfuncs.attr_ne(accessing_obj, accessed_obj, *args, **kwargs)[source]
Usage:

attr_gt(attrname, 54)

Only true if access_obj’s attribute != the value given.

evennia.locks.lockfuncs.dbref(accessing_obj, accessed_obj, *args, **kwargs)[source]
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 in the database (e.g. not for commands)

evennia.locks.lockfuncs.false(*args, **kwargs)[source]

Always returns False

evennia.locks.lockfuncs.has_account(accessing_obj, accessed_obj, *args, **kwargs)[source]

Only returns true if accessing_obj has_account is true, that is, this is an account-controlled object. It fails on actual accounts!

This is a useful lock for traverse-locking Exits to restrain NPC mobiles from moving outside their areas.

evennia.locks.lockfuncs.holds(accessing_obj, accessed_obj, *args, **kwargs)[source]
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

holds(attrname, value) checks if accessing_obj holds an

object with the given attrname and value

This is passed if accessed_obj is carried by accessing_obj (that is, accessed_obj.location == accessing_obj), or if accessing_obj itself holds an object matching the given key.

evennia.locks.lockfuncs.id(accessing_obj, accessed_obj, *args, **kwargs)[source]

Alias to dbref

evennia.locks.lockfuncs.inside(accessing_obj, accessed_obj, *args, **kwargs)[source]
Usage:

inside()

True if accessing_obj is ‘inside’ accessing_obj. Note that this only checks one level down. So if if the lock is on a room, you will pass but not your inventory (since their location is you, not the locked object). If you want also nested objects to pass the lock, use the insiderecursive lockfunc.

evennia.locks.lockfuncs.inside_rec(accessing_obj, accessed_obj, *args, **kwargs)[source]
Usage:

inside_rec()

True if accessing_obj is inside the accessed obj, at up to 10 levels of recursion (so if this lock is on a room, then an object inside a box in your inventory will also pass the lock).

evennia.locks.lockfuncs.locattr(accessing_obj, accessed_obj, *args, **kwargs)[source]
Usage:

locattr(attrname) 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.

if accessing_obj has a property “.obj” (such as is the case for a Command), then accessing_obj.obj.location is used instead.

evennia.locks.lockfuncs.none(*args, **kwargs)[source]
evennia.locks.lockfuncs.objattr(accessing_obj, accessed_obj, *args, **kwargs)[source]
Usage:

objattr(attrname) objattr(attrname, value) objattr(attrname, value, compare=type)

Works like attr, except it looks for an attribute on accessed_obj instead.

evennia.locks.lockfuncs.objlocattr(accessing_obj, accessed_obj, *args, **kwargs)[source]
Usage:

locattr(attrname) locattr(attrname, value) locattr(attrname, value, compare=type)

Works like attr, except it looks for an attribute on accessed_obj.location, if such an entity exists.

if accessed_obj has a property “.obj” (such as is the case for a Command), then accessing_obj.obj.location is used instead.

evennia.locks.lockfuncs.objtag(accessing_obj, accessed_obj, *args, **kwargs)[source]
Usage:

objtag(tagkey) objtag(tagkey, category)

Only true if accessed_obj has the specified tag and optional category.

evennia.locks.lockfuncs.pdbref(accessing_obj, accessed_obj, *args, **kwargs)[source]

Same as dbref, but making sure accessing_obj is an account.

evennia.locks.lockfuncs.perm(accessing_obj, accessed_obj, *args, **kwargs)[source]

The basic permission-checker. Ignores case.

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 settings.PERMISSION_HIERARCHY, permission is also granted to all ranks higher up in the hierarchy.

If accessing_object is an Object controlled by an Account, the permissions of the Account is used unless the Attribute _quell is set to True on the Object. In this case however, the LOWEST hieararcy-permission of the Account/Object-pair will be used (this is order to avoid Accounts potentially escalating their own permissions by use of a higher-level Object)

evennia.locks.lockfuncs.perm_above(accessing_obj, accessed_obj, *args, **kwargs)[source]

Only allow objects with a permission higher in the permission 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.

evennia.locks.lockfuncs.pid(accessing_obj, accessed_obj, *args, **kwargs)[source]

Alias to dbref, for Accounts

evennia.locks.lockfuncs.pperm(accessing_obj, accessed_obj, *args, **kwargs)[source]

The basic permission-checker only for Account objects. Ignores case.

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.

evennia.locks.lockfuncs.pperm_above(accessing_obj, accessed_obj, *args, **kwargs)[source]

Only allow Account objects with a permission higher in the permission 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.

evennia.locks.lockfuncs.self(accessing_obj, accessed_obj, *args, **kwargs)[source]

Check if accessing_obj is the same as accessed_obj

Usage:

self()

This can be used to lock specifically only to the same object that the lock is defined on.

evennia.locks.lockfuncs.serversetting(accessing_obj, accessed_obj, *args, **kwargs)[source]

Only returns true if the Evennia settings exists, alternatively has a certain value.

Usage:

serversetting(IRC_ENABLED) serversetting(BASE_SCRIPT_PATH, [‘types’])

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.

evennia.locks.lockfuncs.superuser(*args, **kwargs)[source]

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. :)

evennia.locks.lockfuncs.tag(accessing_obj, accessed_obj, *args, **kwargs)[source]
Usage:

tag(tagkey) tag(tagkey, category)

Only true if accessing_obj has the specified tag and optional category. If accessing_obj has the “.obj” property (such as is the case for a command), then accessing_obj.obj is used instead.

evennia.locks.lockfuncs.true(*args, **kwargs)[source]

Always returns True.

evennia.locks.lockhandler module

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:

funcname(accessing_obj, accessed_obj, *args, **kwargs):

[…]

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_locks(storage_lockstring)[source]

Store data

_eval_access_type(accessing_obj, locks, access_type)[source]

Helper method for evaluating the access type using eval().

Parameters
  • accessing_obj (object) – Object seeking access.

  • locks (dict) – The pre-parsed representation of all access-types.

  • access_type (str) – An access-type key to evaluate.

_log_error(message)[source]

Try to log errors back to object

_parse_lockstring(storage_lockstring)[source]

Helper function. This is normally only called when the lockstring is cached and does preliminary checking. locks are stored as a string

atype:[NOT] lock()[[ AND|OR [NOT] lock()[…]];atype…

Parameters

storage_locksring (str) – The lockstring to parse.

_save_locks()[source]

Store locks to obj

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

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.

Return type

success (bool)

all()[source]

Return all lockstrings

Returns

All separate lockstrings

Return type

lockstrings (list)

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.

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.

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

If check is passed or not.

Return type

access (bool)

clear()[source]

Remove all locks in the handler.

delete(access_type)

Remove a particular lock from the handler

Parameters

access_type (str) – The type of lock to remove.

Returns

If the access_type was not found

in the lock, this returns False.

Return type

success (bool)

get(access_type=None)[source]

Get the full lockstring or the lockstring of a particular access type.

Parameters

access_type (str, optional) –

Returns

The matched lockstring, or the full

lockstring if no access_type was given.

Return type

lockstring (str)

remove(access_type)[source]

Remove a particular lock from the handler

Parameters

access_type (str) – The type of lock to remove.

Returns

If the access_type was not found

in the lock, this returns False.

Return type

success (bool)

replace(lockstring)[source]

Replaces the lockstring entirely.

Parameters

lockstring (str) – The new lock definition.

Returns

False if an error occurred.

Return type

success (bool)

Raises

LockException – If a critical error occurred. If so, the old string is recovered.

reset()[source]

Set the reset flag, so the the lock will be re-cached at next checking. This is usually called by @reload.

validate(lockstring)[source]

Validate lockstring syntactically, without saving it.

Parameters

lockstring (str) – Lockstring to validate.

Returns

If validation passed or not.

Return type

valid (bool)

exception evennia.locks.lockhandler.LockException[source]

Bases: Exception

Raised during an error in a lock.

evennia.locks.tests module

This is part of Evennia’s unittest framework, for testing the stability and integrity of the codebase during updates.

This module tests the lock functionality of Evennia.

class evennia.locks.tests.TestLockCheck(methodName='runTest')[source]

Bases: evennia.utils.test_resources.EvenniaTest

testrun()[source]
class evennia.locks.tests.TestLockfuncs(methodName='runTest')[source]

Bases: evennia.utils.test_resources.EvenniaTest

setUp()[source]

Sets up testing environment

test_account_perm()[source]
test_account_perm_above()[source]
test_attr()[source]
test_booleans()[source]
test_dbref()[source]
test_has_account()[source]
test_inside_holds()[source]
test_locattr()[source]
test_object_above_perm()[source]
test_object_perm()[source]
test_pperm()[source]
test_puppet_perm()[source]
test_quell_above_perm()[source]
test_quell_perm()[source]
test_serversetting()[source]
test_tag()[source]
test_traverse_taglock()[source]
test_traverse_taglock_fail()[source]