2021-05-23 17:00:02 +02:00
|
|
|
# Permissions
|
|
|
|
|
|
|
|
|
|
A *permission* is simply a text string stored in the handler `permissions` on `Objects`
|
2021-10-21 21:04:14 +02:00
|
|
|
and `Accounts`. Think of it as a specialized sort of [Tag](./Tags.md) - one specifically dedicated
|
|
|
|
|
to access checking. They are thus often tightly coupled to [Locks](./Locks.md).
|
2021-10-09 17:21:37 +02:00
|
|
|
Permission strings are not case-sensitive, so "Builder" is the same as "builder"
|
|
|
|
|
etc.
|
2021-05-23 17:00:02 +02:00
|
|
|
|
|
|
|
|
Permissions are used as a convenient way to structure access levels and
|
2021-10-09 17:21:37 +02:00
|
|
|
hierarchies. It is set by the `perm` command and checked by the
|
|
|
|
|
`PermissionHandler.check` method as well as by the specially the `perm()` and
|
2021-10-21 21:04:14 +02:00
|
|
|
`pperm()` [lock functions](./Locks.md).
|
2021-05-23 17:00:02 +02:00
|
|
|
|
2021-10-09 17:21:37 +02:00
|
|
|
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
|
2022-04-09 15:39:39 +02:00
|
|
|
j
|
2021-10-09 17:21:37 +02:00
|
|
|
perm/account Tommy = Builders
|
|
|
|
|
perm/account/del Tommy = Builders
|
|
|
|
|
|
|
|
|
|
Note the use of the `/account` switch. It means you assign the permission to the
|
2021-10-21 21:04:14 +02:00
|
|
|
[Accounts](./Accounts.md) Tommy instead of any [Character](./Objects.md) that also
|
2021-10-09 17:21:37 +02:00
|
|
|
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,
|
2021-10-21 21:04:14 +02:00
|
|
|
not on their Characters (but see also [quelling](#quelling)).
|
2021-10-09 17:21:37 +02:00
|
|
|
|
|
|
|
|
In code, you add/remove Permissions via the `PermissionHandler`, which sits on all
|
|
|
|
|
typeclassed entities as the property `.permissions`:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
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 below `Player` that is only active if `settings.GUEST_ENABLED` is
|
|
|
|
|
set. The Guest is is never part of `settings.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*
|
2021-10-21 21:04:14 +02:00
|
|
|
[Object](./Objects.md) (like a Character), the check will always first use the
|
2021-10-09 17:21:37 +02:00
|
|
|
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.
|
|
|
|
|
|
2021-10-21 21:04:14 +02:00
|
|
|
```{warning}
|
2021-10-09 17:21:37 +02:00
|
|
|
|
|
|
|
|
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
|
2021-10-21 21:04:14 +02:00
|
|
|
strings](./Locks.md) offers a mini-language for describing how something is accessed.
|
2021-10-09 17:21:37 +02:00
|
|
|
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.
|
2021-05-23 17:00:02 +02:00
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# 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
|
2021-10-09 17:21:37 +02:00
|
|
|
else:
|
|
|
|
|
who.msg("The key fits! The chest opens.")
|
|
|
|
|
# ...
|
2021-05-23 17:00:02 +02:00
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
2021-10-09 17:21:37 +02:00
|
|
|
There are several variations to the default `perm` lockfunc:
|
2021-05-23 17:00:02 +02:00
|
|
|
|
2021-10-09 17:21:37 +02:00
|
|
|
- `perm_above` - requires a hierarchical permission *higher* than the one
|
|
|
|
|
provided. Example: `"edit: perm_above(Player)"`
|
|
|
|
|
- `pperm` - looks *only* for permissions on `Accounts`, never at any puppeted
|
|
|
|
|
objects (regardless of hierarchical perm or not).
|
|
|
|
|
- `pperm_above` - like `perm_above`, but for Accounts only.
|
2021-05-23 17:00:02 +02:00
|
|
|
|
2021-10-09 17:21:37 +02:00
|
|
|
### Some examples
|
2021-05-23 17:00:02 +02:00
|
|
|
|
2021-10-09 17:21:37 +02:00
|
|
|
Adding permissions and checking with locks
|
2021-05-23 17:00:02 +02:00
|
|
|
|
|
|
|
|
```python
|
2021-10-09 17:21:37 +02:00
|
|
|
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!
|
2021-05-23 17:00:02 +02:00
|
|
|
```
|
|
|
|
|
|
2021-10-09 17:21:37 +02:00
|
|
|
An example of a puppet with a connected account:
|
2021-05-23 17:00:02 +02:00
|
|
|
|
|
|
|
|
```python
|
2021-10-09 17:21:37 +02:00
|
|
|
account.permissions.add("Player")
|
|
|
|
|
puppet.permissions.add("Builders")
|
|
|
|
|
puppet.permissions.add("cool_guy")
|
2021-05-23 17:00:02 +02:00
|
|
|
obj2.locks.add("enter:perm_above(Accounts) and perm(cool_guy)")
|
|
|
|
|
|
2021-10-09 17:21:37 +02:00
|
|
|
obj2.access(puppet, "enter") # this returns False, since puppet permission
|
|
|
|
|
# is lower than Account's perm, and perm takes
|
|
|
|
|
# precedence.
|
2021-05-23 17:00:02 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Superusers
|
|
|
|
|
|
2021-10-09 17:21:37 +02:00
|
|
|
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.
|
2021-05-23 17:00:02 +02:00
|
|
|
|
|
|
|
|
## Quelling
|
|
|
|
|
|
2021-10-09 17:21:37 +02:00
|
|
|
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.
|