Permissions¶
A permission is simply a text string stored in the handler permissions
on Objects
and Accounts
. Think of it as a specialized sort of Tag - one specifically dedicated
to access checking. They are thus often tightly coupled to Locks.
Permission strings are not case-sensitive, so “Builder” is the same as “builder”
etc.
Permissions are used as a convenient way to structure access levels and
hierarchies. It is set by the perm
command and checked by the
PermissionHandler.check
method as well as by the specially the perm()
and
pperm()
lock functions.
All new accounts are given a default set of permissions defined by
settings.PERMISSION_ACCOUNT_DEFAULT
.
Managing Permissions¶
In-game, you use the perm
command to add and remove permissions
j
perm/account Tommy = Builders
perm/account/del Tommy = Builders
Note the use of the /account
switch. It means you assign the permission to the
Accounts Tommy instead of any Character that also
happens to be named “Tommy”.
There can be reasons for putting permissions on Objects (especially NPCS), but
for granting powers to players, you should usually put the permission on the
Account
- this guarantees that they are kept, regardless
of which Character they are currently puppeting. This is especially important to
remember when assigning permissions from the hierarchy tree (see below), as an
Account’s permissions will overrule that of its character. So to be sure to
avoid confusion you should generally put hierarchy permissions on the Account,
not on their Characters (but see also quelling).
In code, you add/remove Permissions via the PermissionHandler
, which sits on all
typeclassed entities as the property .permissions
:
account.permissions.add("Builders")
account.permissions.add("cool_guy")
obj.permissions.add("Blacksmith")
obj.permissions.remove("Blacksmith")
The permission hierarchy¶
Selected permission strings can be organized in a permission hierarchy by editing the tuple
settings.PERMISSION_HIERARCHY
. Evennia’s default permission hierarchy is as follows
(in increasing order of power):
Player # can chat and send tells (default level) (lowest)
Helper # can edit help files
Builder # can edit the world
Admin # can administrate accounts
Developer # like superuser but affected by locks (highest)
(Besides being case-insensitive, hierarchical permissions also understand the
plural form, so you could use Developers
and Developer
interchangeably).
There is also a
Guest
level belowPlayer
that is only active ifsettings.GUEST_ENABLED
is set. The Guest is is never part ofsettings.PERMISSION_HIERARCHY
.
When checking a hierarchical permission (using one of the methods to follow), you will pass checks for your level and all below you. That is, even if the check explicitly checks for “Builder” level access, you will actually pass if you have one of “Builder”, “Admin” or “Developer”. By contrast, if you check for a non-hierarchical permission, like “Blacksmith” you must have exactly that permission to pass.
Checking permissions¶
It’s important to note that you check for the permission of a puppeted
Object (like a Character), the check will always first use the
permissions of any Account
connected to that Object before checking for
permissions on the Object. In the case of hierarchical permissions (Admins,
Builders etc), the Account permission will always be used (this stops an Account
from escalating their permission by puppeting a high-level Character). If the
permission looked for is not in the hierarchy, an exact match is required, first
on the Account and if not found there (or if no Account is connected), then on
the Object itself.
Checking with obj.permissions.check()¶
The simplest way to check if an entity has a permission is to check its
PermissionHandler, stored as .permissions
on all typeclassed entities.
if obj.permissions.check("Builder"):
# allow builder to do stuff
if obj.permissions.check("Blacksmith", "Warrior"):
# do stuff for blacksmiths OR warriors
if obj.permissions.check("Blacksmith", "Warrior", require_all=True):
# only for those that are both blacksmiths AND warriors
Using the .check
method is the way to go, it will take hierarchical
permissions into account, check accounts/sessions etc.
Warning
Don't confuse `.permissions.check()` with `.permissions.has()`. The .has()
method checks if a string is defined specifically on that PermissionHandler.
It will not consider permission-hierarchy, puppeting etc. `.has` can be useful
if you are manipulating permissions, but use `.check` for access checking.
Lock funcs¶
While the PermissionHandler
offers a simple way to check perms, Lock
strings offers a mini-language for describing how something is accessed.
The perm()
lock function is the main tool for using Permissions in locks.
Let’s say we have a red_key
object. We also have red chests that we want to
unlock with this key.
perm red_key = unlocks_red_chests
This gives the red_key
object the permission “unlocks_red_chests”. Next we
lock our red chests:
lock red chest = unlock:perm(unlocks_red_chests)
When trying to unlock the red chest with this key, the chest Typeclass could then take the key and do an access check:
# in some typeclass file where chest is defined
class TreasureChest(Object):
# ...
def open_chest(self, who, tried_key):
if not chest.access(who, tried_key, "unlock"):
who.msg("The key does not fit!")
return
else:
who.msg("The key fits! The chest opens.")
# ...
There are several variations to the default perm
lockfunc:
perm_above
- requires a hierarchical permission higher than the one provided. Example:"edit: perm_above(Player)"
pperm
- looks only for permissions onAccounts
, never at any puppeted objects (regardless of hierarchical perm or not).pperm_above
- likeperm_above
, but for Accounts only.
Some examples¶
Adding permissions and checking with locks
account.permissions.add("Builder")
account.permissions.add("cool_guy")
account.locks.add("enter:perm_above(Player) and perm(cool_guy)")
account.access(obj1, "enter") # this returns True!
An example of a puppet with a connected account:
account.permissions.add("Player")
puppet.permissions.add("Builders")
puppet.permissions.add("cool_guy")
obj2.locks.add("enter:perm_above(Accounts) and perm(cool_guy)")
obj2.access(puppet, "enter") # this returns False, since puppet permission
# is lower than Account's perm, and perm takes
# precedence.
Superusers¶
There is normally only one superuser account and that is the one first created
when starting Evennia (User #1). This is sometimes known as the “Owner” or “God”
user. A superuser has more than full access - it completely bypasses all
locks and will always pass the PermissionHandler.check()
check. This allows
for the superuser to always have access to everything in an emergency. But it
could also hide any eventual errors you might have made in your lock definitions. So
when trying out game systems you should either use quelling (see below) or make
a second Developer-level character that does not bypass such checks.
Quelling¶
The quell
command can be used to enforce the perm()
lockfunc to ignore
permissions on the Account and instead use the permissions on the Character
only. This can be used e.g. by staff to test out things with a lower permission
level. Return to the normal operation with unquell
. Note that quelling will
use the smallest of any hierarchical permission on the Account or Character, so
one cannot escalate one’s Account permission by quelling to a high-permission
Character. Also the superuser can quell their powers this way, making them
affectable by locks.