mirror of
https://github.com/evennia/evennia.git
synced 2026-03-28 18:47:16 +01:00
Refactor Permissions documentation
This commit is contained in:
parent
fb9a274c20
commit
6b66b0f367
1 changed files with 148 additions and 65 deletions
|
|
@ -3,12 +3,119 @@
|
|||
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](./Tags) - one specifically dedicated
|
||||
to access checking. They are thus often tightly coupled to [Locks](./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. Permissions are especially
|
||||
handled by the `perm()` and `pperm()` [lock functions](./Locks).
|
||||
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](./Locks).
|
||||
|
||||
Let's say we have a `red_key` object. We also have red chests that we want to unlock with this key.
|
||||
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
|
||||
|
||||
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](./Accounts) Tommy instead of any [Character](./Objects) 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](./Locks#Quelling)).
|
||||
|
||||
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*
|
||||
[Object](./Objects) (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](Locks) 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
|
||||
|
||||
|
|
@ -32,87 +139,63 @@ class TreasureChest(Object):
|
|||
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.")
|
||||
# ...
|
||||
|
||||
```
|
||||
|
||||
All new accounts are given a default set of permissions defined by
|
||||
`settings.PERMISSION_ACCOUNT_DEFAULT`.
|
||||
There are several variations to the default `perm` lockfunc:
|
||||
|
||||
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:
|
||||
- `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.
|
||||
|
||||
Developer # like superuser but affected by locks
|
||||
Admin # can administrate accounts
|
||||
Builder # can edit the world
|
||||
Helper # can edit help files
|
||||
Player # can chat and send tells (default level)
|
||||
|
||||
(Also the plural form works, so you could use `Developers` etc too).
|
||||
### Some examples
|
||||
|
||||
> There is also a `Guest` level below `Player` that is only active if `settings.GUEST_ENABLED` is
|
||||
set. This is never part of `settings.PERMISSION_HIERARCHY`.
|
||||
|
||||
The main use of this is that if you use the lock function `perm()` mentioned above, a lock check for
|
||||
a particular permission in the hierarchy will *also* grant access to those with *higher* hierarchy
|
||||
access. So if you have the permission "Admin" you will also pass a lock defined as `perm(Builder)`
|
||||
or any of those levels below "Admin".
|
||||
|
||||
When doing an access check from an [Object](./Objects) or Character, the `perm()` lock function 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.
|
||||
|
||||
Here is how you use `perm` to give an account more permissions:
|
||||
|
||||
perm/account Tommy = Builders
|
||||
perm/account/del Tommy = Builders # remove it again
|
||||
|
||||
Note the use of the `/account` switch. It means you assign the permission to the
|
||||
[Accounts](./Accounts) Tommy instead of any [Character](./Objects) that also happens to be named
|
||||
"Tommy".
|
||||
|
||||
Putting permissions on the *Account* 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* - as mentioned above, 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](./Locks#Quelling)).
|
||||
|
||||
Below is an example of an object without any connected account
|
||||
Adding permissions and checking with locks
|
||||
|
||||
```python
|
||||
obj1.permissions = ["Builders", "cool_guy"]
|
||||
obj2.locks.add("enter:perm_above(Accounts) and perm(cool_guy)")
|
||||
|
||||
obj2.access(obj1, "enter") # this returns True!
|
||||
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!
|
||||
```
|
||||
|
||||
And one example of a puppet with a connected account:
|
||||
An example of a puppet with a connected account:
|
||||
|
||||
```python
|
||||
account.permissions.add("Accounts")
|
||||
puppet.permissions.add("Builders", "cool_guy")
|
||||
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!
|
||||
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 so no checks are even run. This allows for the
|
||||
superuser to always have access to everything in an emergency. But it also hides 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 so your locks get tested correctly.
|
||||
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.
|
||||
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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue