Acknowledgment: This text is a formatted
version of a message posted to ??? on BioMOO by:
Copyright 1995 Eric Mercer
last updated 7/11/95
EricM @ BioMOO, Diversity University, others....
mercer@caltech.edu
Check out the MOO programmer's manuel, e.g. the callers function.
Any verb that either
The main considerations are 1) is the verb +x, and 2) can the verb be called from the command line (ie. does it have args other than this none this). Note that these are not linked and you can have +x command line verbs, for instance.
Verbs should start with a few lines of comments (describing what they do, etc.) followed by:
if (perms_test_fails)
return failure_report;
endif
....->rest of verb
where "failure_report" should be 'E_PERM' for +x this none this verbs, and 'player:tell("Sorry, you don't have permission to do that.")' for -x command line verb). For +x command line verbs, add the player:tell line before returning E_PERM.
Replace "perms_test_fails" with one of the constructs given later in this note.
I think the following list is complete. I've also given an example of a typical "perms_test_fails" line for each, but note that you'll often want to use some combination or hybrid.
1. Calls from other verbs on the object itself.
type:object-based security
example: if (caller!=this)
2. Wiz-permed calls
type: permissions-based security
example: if (!$perm_utils:controls(caller_perms(), this)) <-preferred
if (!caller_perms().wizard) <- less flexible
3. Calls from verbs owned by the same programmer
type: perms-based security
example: if (caller_perms() != $code_utils:verb_perms())
note: Gives a lot of power to the programmer, but sometimes useful.
Note that we DON'T use the object number of the programmer,
because this will be different if someone ports the object.
4. Calls from verbs owned by the object owner
type:perms-based security
example: if (caller_perms() != this.owner)
note: rarely useful, and not at all useful on generic objects
5. Calls from objects owned by the programmer
type: object-based security
example: if (caller.owner != $code_utils:verb_perms())
note: only useful in some special cases
6. Calls from "permitted" objects (generally stored as a list on a property)
type: object-based security
example: if (!(caller in this.permitted_callers))
note: useful only in some specialized circumstances
if (!$perm_utils:controls(player,this))
This allows the object owner, additional owners, or wizards access to the
verb.
I'll give an example. Loro the lazy wizard writes a +x verb that can recycle any object and tests permissions with "if (!$perm_utils:controls(player,this))" at the verb's beginning. Semli the sneaky programmer builds an object and adds a "tell" verb to it (ie. a verb that gets called any time someone in the same room speaks). The "tell" verb calls Loro's +x verb and tells it to recycle all of Loro's objects. Semli puts the object in Loro's room...and Loro gets a nasty surprise after connecting. Neato eh! Note that "player" will be the person speaking (Loro in this case), because "player" is set to whoever initiates the action, and can only be changed by wiz-permed verbs. Generally, it stays the same from the task's start to it's finish. Now, if Loro had tested caller_perms(), then Semli's call would have been caught as one that did not have permission to be recycling objects. Got it?
if (caller != this)
When in doubt, use this one. It's the least flexible but the most secure.
The problem here is that you can't test caller_perms() on a command
line verb, since the perms will be #-1. Note that this isn't a
problem for object-based security, since "caller" for a command line
verb will be the same as "player." To test security on a +x command
line verb, replace "player" in a construct like:
if (!$perm_utils:controls(player,this))
with an expression that will handle both command line and verb calls:
if (!$perm_utils:controls( (valid(caller_perms())?caller_perms()|player) ,this))
Note that if caller_perms is #-1, then "player" is used, otherwise
caller_perms is used.
One of the great things about MOO code is that the object-oriented nature lets you "cover" verbs by adding verbs of the same name on child objects. These can handle special cases, but otherwise simply pass(@args) down to the verb on the parent object instead. The problem is that if you use only perms-based security, this call (a legitimate one) will fail. Let's say you are testing with
if (!$perm_utils:controls(caller_perms(),this))
which is generally very reasonable. If someone makes a new generic as a child of your verb, then caller_perms() will be that person, who is unlikely to "control" the object. The solution is to use a combination object-based and perms-based test such as:
if ( (caller==this) || !$perm_utils:controls(caller_perms(),this) )
Here's an example for us to dissect:
if ( (caller!=this) && !$perm_utils:controls(cp=caller_perms(),this)
&& (cp!=$code_utils:verb_perms()))
What's going on here? First, there's a test to see if caller!=this. That gives access to calls from other verbs on the same object and to calls via pass(). Then we check with $perm_utils:controls, which gives access to the object's owner, any additional owners, and to wizards. Finally, we test if the caller_perms are the same as the perms of the verb running (ie. the calling verb was written by us). This allows us to design objects that interact with each other. Note that this is a special circumstance, but one that's not terribly uncommon (eg. a class of objects and a feature object that interacts with them).