Added Sphinx (reST-style) conversion of Evennia documentation to docs/. This is an auto-generated conversion directly from the Wiki, so it's not custom-written in any way (will also make it easy to update). You need Sphinx to compile the sources into fancy pages. Supporting sphinx is to make documentation easier to print and view offline. Currently no sphinx src-code viewing is activated by default, it gives too many spurious errors (the converters are in the repo though if you're interested in experimenting). So for offline autodocs, doxygen is still to recommend.
This commit is contained in:
parent
5a2b9e27a0
commit
bd0079a39d
65 changed files with 9394 additions and 143 deletions
411
docs/sphinx/source/wiki/Locks.rst
Normal file
411
docs/sphinx/source/wiki/Locks.rst
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
<wiki:toc max\_depth
|
||||
|
||||
"3" />
|
||||
======
|
||||
|
||||
Locks =
|
||||
|
||||
For most games it is a good idea to restrict what people can do. In
|
||||
Evennia such restrictions are applied and checked by something called
|
||||
*locks*. All Evennia entities (`commands <Commands.html>`_,
|
||||
`objects <Objects.html>`_, `scripts <Scripts.html>`_,
|
||||
`players <Players.html>`_, `help system <HelpSystem.html>`_,
|
||||
`messages <Communications#Msg.html>`_ and
|
||||
`channels <Communications#Channels.html>`_) are accessed through locks.
|
||||
|
||||
A lock can be thought of as an "access rule" restricting a particular
|
||||
use of an Evennia entity. Whenever another entity wants that kind of
|
||||
access the lock will analyze that entity in different ways to determine
|
||||
if access should be granted or not. Evennia implements a "lockdown"
|
||||
philosophy - all entities are inaccessible unless you explicitly define
|
||||
a lock that allows some or full access.
|
||||
|
||||
Let's take an example: An object has a lock on itself that restricts how
|
||||
people may "delete" that object. Apart from knowing that it restricts
|
||||
deletion, the lock also knows that only players with the specific ID of,
|
||||
say, '34' are allowed to delete it. So whenever a player tries to run
|
||||
@delete on the object, the @delete command makes sure to check if this
|
||||
player is really allowed to do so. It calls the lock, which in turn
|
||||
checks if the player's id is 34. Only then will it allow @delete to go
|
||||
on with its job.
|
||||
|
||||
Setting and checking a lock
|
||||
---------------------------
|
||||
|
||||
The in-game command for setting locks on objects is ``@lock``:
|
||||
|
||||
::
|
||||
|
||||
> @lock obj = <lockstring>
|
||||
|
||||
The ``<lockstring>`` is a string on a certain form that defines the
|
||||
behaviour of the lock. We will go into more detail on how
|
||||
``<lockstring>`` should look in the next section.
|
||||
|
||||
Code-wise, Evennia handles locks through what is usually called
|
||||
``locks`` on all relevant entities. This is a handler that allows you to
|
||||
add, delete and check locks.
|
||||
|
||||
::
|
||||
|
||||
myobj.locks.add(<lockstring>)
|
||||
|
||||
One can call ``locks.check()`` to perform a lock check, but to hide the
|
||||
underlying implementation all objects also have a convenience function
|
||||
called ``access``. This should preferably be used. In the example below,
|
||||
``accessing_obj`` is the object requesting the 'delete' access. This is
|
||||
how it would (and do) look from inside the ``@delete`` command:
|
||||
|
||||
::
|
||||
|
||||
if not obj.access(accessing_obj, 'delete'): accessing_obj.msg("Sorry, you may not delete that.") return
|
||||
|
||||
Defining locks
|
||||
--------------
|
||||
|
||||
Defining a lock (i.e. an access restriction) in Evennia is done by
|
||||
adding simple strings of lock definitions to the object's ``locks``
|
||||
property using ``obj.locks.add()``. Such a *lockstring* formally looks
|
||||
like this:
|
||||
|
||||
::
|
||||
|
||||
access_type:[NOT] func1([arg1,..])[[AND|OR][ NOT] func2([arg1,...])[...]]
|
||||
|
||||
where ``[..]`` marks optional parts. AND, OR and NOT are not case
|
||||
sensitive and excess spaces are ignored. ``func1, func2`` etc are
|
||||
special *lock functions* available to the lock system.
|
||||
|
||||
This form of writing may look complicated, so let's immediately give
|
||||
some much nicer examples:
|
||||
|
||||
::
|
||||
|
||||
delete:id(34) # only allow obj #34 to delete edit:all() # let everyone edit get: not attr(very_weak) or perm(Wizard) # only those who are not "very_weak" or are Wizards may pick this up
|
||||
|
||||
So, a lockstring consists of the type of restriction (the
|
||||
``access_type``), a colon (``:``) and then a list of function calls that
|
||||
determine what is needed to pass the lock. Each function returns either
|
||||
``True`` or ``False``. AND/OR and NOT works like normal Python to
|
||||
combine several function checks. If the total result is True, the lock
|
||||
is passed.
|
||||
|
||||
You can create several lock types one after the other by separating them
|
||||
with a semicolon (``;``) in the lockstring. The string below is
|
||||
identical to the first two rows of the previous example:
|
||||
|
||||
::
|
||||
|
||||
delete:id(34);edit:all()
|
||||
|
||||
Valid access\_types
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
An ``access_type``, the first part of a lockstring, defines what kind of
|
||||
capability a lock controls, such as "delete" or "edit". You may in
|
||||
principle name your ``access_type`` anything as long as it is unique for
|
||||
the particular object. Access\_types are not case-sensitive.
|
||||
|
||||
If you want to make sure the lock is used however, you should pick
|
||||
``access_type`` names that you (or the default command set) actually
|
||||
tries, as in the example of ``@delete`` above that uses the 'delete'
|
||||
``access_type``.
|
||||
|
||||
Below are the access\_types checked by the default commandset.
|
||||
|
||||
`Commands <Commands.html>`_: ``cmd`` - this defines who may call this
|
||||
command at all.
|
||||
|
||||
`Objects <Objects.html>`_:
|
||||
|
||||
- ``control`` - who is the "owner" of the object. Can set locks, delete
|
||||
it etc. Defaults to the creator of the object.
|
||||
- ``call`` - who may call object-commands on this object.
|
||||
- ``examine`` - who may examine this object's properties.
|
||||
- ``delete`` - who may delete the object.
|
||||
- ``edit`` - who may edit properties and attributes of the object.
|
||||
- ``get``- who may pick up the object and carry it around.
|
||||
- ``puppet`` - who may "become" this object and control it as their
|
||||
"character".
|
||||
|
||||
`Characters <Objects#Characters.html>`_: ``<Same as Objects>``
|
||||
|
||||
`Exits <Objects#Exits.html>`_: ``<Same as Objects>`` + ``traverse`` -
|
||||
who may pass the exit.
|
||||
|
||||
`Players <Players.html>`_:
|
||||
|
||||
- ``examine`` - who may examine the player's properties.
|
||||
- ``delete`` - who may delete the player.
|
||||
- ``edit`` - who may edit the player's attributes and properties.
|
||||
- ``msg`` - who may send messages to the player.
|
||||
- ``boot`` - who may boot the player.
|
||||
|
||||
`Attributes <Attributes.html>`_: ``<None>``
|
||||
|
||||
`Channels <Communications#Channels.html>`_:
|
||||
|
||||
- ``control`` - who is administrating the channel. This means the
|
||||
ability to delete the channel, boot listeners etc.
|
||||
- ``send`` - who may send to the channel.
|
||||
- ``listen`` - who may subscribe and listen to the channel.
|
||||
|
||||
`HelpEntry <HelpSystem.html>`_:
|
||||
|
||||
- ``examine`` - who may view this help entry (usually everyone)
|
||||
- ``edit`` - who may edit this help entry.
|
||||
|
||||
So to take an example, whenever an exit is to be traversed, a lock of
|
||||
the type *traverse* will be checked. Defining a suitable lock type for
|
||||
an exit object would thus involve a lockstring
|
||||
``traverse: <lock functions>``.
|
||||
|
||||
Lock functions
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
You are not allowed to use just any function in your lock definition;
|
||||
you are infact only allowed to use those functions defined in one of the
|
||||
modules given in ``settings.LOCK_FUNC_MODULES``. All functions in any of
|
||||
those modules will automatically be considered a valid lock function.
|
||||
The default ones are found in src/locks/lockfuncs.py.
|
||||
|
||||
A lock function must always accept at least two arguments - the
|
||||
*accessing object* (this is the object wanting to get access) and the
|
||||
*accessed object* (this is the object with the lock). Those two are fed
|
||||
automatically as the first two arguments the function when the lock is
|
||||
checked. Any arguments explicitly given in the lock definition will
|
||||
appear as extra arguments.
|
||||
|
||||
::
|
||||
|
||||
# A simple example lock function. Called with e.g. id(34)def id(accessing_obj, accessed_obj, *args, **kwargs): if args: wanted_id = args[0] return accessing_obj.id == wanted_id return False
|
||||
|
||||
(Using the ``*`` and ``**`` syntax causes Python to magically put all
|
||||
extra arguments into a list ``args``and all keyword arguments into a
|
||||
dictionary ``kwargs`` respectively. If you are unfamiliar with how
|
||||
``*args`` and ``**kwargs`` work, see the Python manuals).
|
||||
|
||||
Some useful default lockfuncs (see lockfuncs.py for a full list):
|
||||
|
||||
- ``true()/all()`` - give access to everyone
|
||||
- ``false()/none()/superuser()`` - give access to noone. Superusers
|
||||
bypass the check entirely.
|
||||
- ``perm(perm)`` - this tries to match a given ``permission`` property.
|
||||
See `below <Locks#Permissions.html>`_.
|
||||
- ``perm_above(perm)`` - requres a "higher" permission level than the
|
||||
one given.
|
||||
- ``id(num)/dbref(num)`` - checks so the accessobject has a certain
|
||||
dbref/id.
|
||||
- ``attr(attrname)`` - checks if a certain
|
||||
`Attribute <Attributes.html>`_ exists on accessingobject.
|
||||
- ``attr(attrname, value)`` - checks so an attribute exists on
|
||||
accessing*object*and has the given value.
|
||||
- ``attr_gt(attrname, value)`` - checks so accessingobject has a value
|
||||
larger (>) than the given value.
|
||||
- ``attr_ge, attr_lt, attr_le, attr_ne`` - corresponding for >
|
||||
|
||||
, <, <
|
||||
======
|
||||
|
||||
and !=.
|
||||
|
||||
- ``holds(objid)`` - checks so the accessing objects contains an object
|
||||
of given name or dbref.
|
||||
- ``pperm(perm)``, ``pid(num)/pdbref(num)`` - same as ``perm``,
|
||||
``id/dbref`` but always looks for permissions and dbrefs of
|
||||
*Players*, not on Characters.
|
||||
|
||||
Default locks
|
||||
-------------
|
||||
|
||||
Evennia sets up a few basic locks on all new objects and players (if we
|
||||
didn't, noone would have any access to anything from the start). This is
|
||||
all defined in the root `Typeclasses <Typeclass.html>`_ of the
|
||||
respective entity, in the hook method ``basetype_setup()`` (which you
|
||||
usually don't want to edit unless you want to change how basic stuff
|
||||
like rooms and exits store their internal variables). This is called
|
||||
once, before ``at_object_creation``, so just put them in the latter
|
||||
method on your child object to change the default. Also creation
|
||||
commands like ``@create`` changes the locks of objects you create - for
|
||||
example it sets the ``control`` lock\_type allow youm, its creator to
|
||||
control and delete the object.
|
||||
|
||||
Permissions
|
||||
===========
|
||||
|
||||
A *permission* is simply a list of text strings stored on the property
|
||||
``permissions`` on ``Objects`` and ``Players``. Permissions can be used
|
||||
as a convenient way to structure access levels and hierarchies. It is
|
||||
set by the ``@perm`` command.
|
||||
|
||||
::
|
||||
|
||||
@perm Tommy = Builders
|
||||
|
||||
All new players/character are given a default set of permissions defined
|
||||
by ``settings.PERMISSION_PLAYER_DEFAULT``.
|
||||
|
||||
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:
|
||||
|
||||
::
|
||||
|
||||
Immortals Wizards Builders PlayerHelpers Players # this is what all new Players start with by default
|
||||
|
||||
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
|
||||
acces. So if you have the permission "Wizards" you will also pass a lock
|
||||
defined as ``perm(Builders)`` or any of those levels below "Wizards".
|
||||
The lock function ``perm_above(Players)`` require you to have a
|
||||
permission level higher than ``Players`` and so on. If the permission
|
||||
looked for is not in the hierarchy, an exact match is required.
|
||||
|
||||
::
|
||||
|
||||
obj1.permissions = ["Builders", "cool_guy"] obj2.locks.add("enter:perm_above(Players) and perm(cool_guy)")obj2.access(obj1, "enter") # this returns True!
|
||||
|
||||
Superusers
|
||||
----------
|
||||
|
||||
There is normally only one *superuser* account and that is the 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 use a secondary
|
||||
character rather than #1 so your locks get tested correctly.
|
||||
|
||||
More Lock definition examples
|
||||
=============================
|
||||
|
||||
::
|
||||
|
||||
examine: attr(eyesight, excellent) or perm(Builders)
|
||||
|
||||
You are only allowed to do *examine* on this object if you have
|
||||
'excellent' eyesight or is a Builder.
|
||||
|
||||
::
|
||||
|
||||
# lock for the tell command
|
||||
cmd: perm(Builders) and not perm(no_tell)
|
||||
|
||||
Locks can be used to implement highly specific bans. Set this on e.g.
|
||||
the ``tell`` command, then give a player the "permission" ``no_tell`` to
|
||||
disable their use of this particular command henceforth.
|
||||
|
||||
::
|
||||
|
||||
open: holds('the green key') or perm(Builder)
|
||||
|
||||
This could be called by the ``open`` command on a "door" object. The
|
||||
check is passed if you are a Builder or has the right key in your
|
||||
inventory.
|
||||
|
||||
::
|
||||
|
||||
# this limits what commands are visible to the user
|
||||
cmd: perm(Builders)
|
||||
|
||||
Evennia's command handler looks for a lock of type ``cmd`` to determine
|
||||
if a user is allowed to even call upon a particular command or not. When
|
||||
you define a command, this is the kind of lock you must set. See the
|
||||
default command set for lots of examples.
|
||||
|
||||
::
|
||||
|
||||
dbref = caller.id
|
||||
lockstring = "control:id(%s);examine:perm(Builders);delete:id(%s) or perm(Wizards);get:all()" % (dbref, dbref)
|
||||
new_obj.locks.add(lockstring)
|
||||
|
||||
This is how the ``@create`` command sets up new objects. In sequence,
|
||||
this permission string sets the owner of this object be the creator (the
|
||||
one running ``@create``). Builders may examine the object whereas only
|
||||
Wizards and the creator may delete it. Everyone can pick it up.
|
||||
|
||||
A complete example of setting locks on an object
|
||||
================================================
|
||||
|
||||
Assume we have two objects - one is ourselves (not superuser) and the
|
||||
other is an `Object <Objects.html>`_ called ``box``.
|
||||
|
||||
::
|
||||
|
||||
> @create/drop box > @desc box = "This is a very big and heavy box."
|
||||
|
||||
We want to limit which objects can pick up this heavy box. Let's say
|
||||
that to do that we require the would-be lifter to to have an attribute
|
||||
*strength* on themselves, with a value greater than 50. We assign it to
|
||||
ourselves to begin with.
|
||||
|
||||
::
|
||||
|
||||
> @set self/strength = 45
|
||||
|
||||
Ok, so for testing we made ourselves strong, but not strong enough. Now
|
||||
we need to look at what happens when someone tries to pick up the the
|
||||
box - they use the ``get`` command (in the default set). This is defined
|
||||
in ``game/gamesrc/commands/default/general.py``. In its code we find
|
||||
this snippet:
|
||||
|
||||
::
|
||||
|
||||
if not obj.access(caller, 'get'): if obj.db.get_err_msg: caller.msg(obj.db.get_err_msg) else: caller.msg("You can't get that.") return
|
||||
|
||||
So the ``get`` command looks for a lock with the type *get* (not so
|
||||
surprising). It also looks for an `Attribute <Attributes.html>`_ on the
|
||||
checked object called *get*err*msg* in order to return a customized
|
||||
error message. Sounds good! Let's start by setting that on the box:
|
||||
|
||||
::
|
||||
|
||||
> @set box/get_err_msg = You are not strong enough to lift this box.
|
||||
|
||||
Next we need to craft a Lock of type *get* on our box. We want it to
|
||||
only be passed if the accessing object has the attribute *strength* of
|
||||
the right value. For this we would need to create a lock function that
|
||||
checks if attributes have a value greater than a given value. Luckily
|
||||
there is already such a one included in evennia (see
|
||||
``src/permissions/lockfuncs.py``), called``attr_gt``.
|
||||
|
||||
So the lock string will look like this: "``get:attr_gt(strength, 50)``".
|
||||
We put this on the box now:
|
||||
|
||||
::
|
||||
|
||||
@lock box = get:attr_gt(strength, 50)
|
||||
|
||||
Try to ``get`` the object and you should get the message that we are not
|
||||
strong enough. Increase your strength above 50 however and you'll pick
|
||||
it up no problem. Done! A very heavy box!
|
||||
|
||||
If you wanted to set this up in python code, it would look something
|
||||
like this:
|
||||
|
||||
::
|
||||
|
||||
from src.utils import create box = create.create_object(None, key="box", locks="get:attr_gt(strength, 50)")# or, if we don't set the locks right awaybox.locks.add("get:attr_gt(strength, 50)")# set the attributesbox.db.desc = "This is a very big and heavy box." box.db.get_err_msg = "You are not strong enough to lift this box."# one heavy box, ready to withstand all but the strongest...
|
||||
|
||||
On Django's permission system
|
||||
=============================
|
||||
|
||||
Django also implements a comprehensive permission/security system out of
|
||||
the box. The reason we don't use that is because it is app-centric (app
|
||||
in the Django sense). Its permission strings are of the form
|
||||
``appname.permstring`` and it automatically adds three of them for each
|
||||
database model in the app - for the app src/object this would be for
|
||||
example 'object.create', 'object.admin' and 'object.edit'. This makes a
|
||||
lot of sense for a web application, not so much for a MUD, especially
|
||||
when we try to hide away as much of the underlying architecture as
|
||||
possible.
|
||||
|
||||
The django permissions are not completely gone however. We use it for
|
||||
logging in users (the ``User`` object tied to `Players <Players.html>`_
|
||||
is a part of Djangos's auth system). It is also used exclusively for
|
||||
managing Evennia's web-based admin site, which is a graphical front-end
|
||||
for the database of Evennia. You edit and assign such permissions
|
||||
directly from the web interface. It's stand-alone from the permissions
|
||||
described above.
|
||||
Loading…
Add table
Add a link
Reference in a new issue