mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
OBS: You'll need to resync/rebuild your database!
- This implements an updated, clearer and more robust access system. The policy is now to lock that which is not explicitly left open. - Permission strings -> Lock strings. Separating permissions and locks makes more sense security-wise - No more permissiongroup table; permissions instead use a simple tuple PERMISSIONS_HIERARCHY to define an access hierarchy - Cleaner lock-definition syntax, all based on function calls. - New objects/players/channels get a default security policy during creation (set through typeclass) As part of rebuilding and testing the new lock/permission system I got into testing and debugging several other systems, fixing some outstanding issues: - @reload now fully updates the database asynchronously. No need to reboot server when changing cmdsets - Dozens of new test suites added for about 30 commands so far - Help for channels made more clever and informative.
This commit is contained in:
parent
c2030c2c0c
commit
08b3de9e5e
49 changed files with 1714 additions and 1877 deletions
|
|
@ -10,7 +10,6 @@ See src/commands/default/muxcommand.py for an example.
|
|||
|
||||
from src.commands.command import Command as BaseCommand
|
||||
from src.commands.default.muxcommand import MuxCommand as BaseMuxCommand
|
||||
from src.permissions import permissions
|
||||
from src.utils import utils
|
||||
|
||||
class MuxCommand(BaseMuxCommand):
|
||||
|
|
@ -41,7 +40,7 @@ class MuxCommand(BaseMuxCommand):
|
|||
cmdhandler):
|
||||
self.key - the name of this command ('look')
|
||||
self.aliases - the aliases of this cmd ('l')
|
||||
self.permissions - permission string for this command
|
||||
self.locks - lock definition for this command, usually cmd:<func>
|
||||
self.help_category - overall category of command
|
||||
|
||||
self.caller - the object calling this command
|
||||
|
|
@ -105,7 +104,7 @@ class Command(BaseCommand):
|
|||
used by Evennia to create the automatic help entry for
|
||||
the command, so make sure to document consistently here.
|
||||
"""
|
||||
def has_perm(self, srcobj):
|
||||
def access(self, srcobj):
|
||||
"""
|
||||
This is called by the cmdhandler to determine
|
||||
if srcobj is allowed to execute this command. This
|
||||
|
|
@ -114,7 +113,7 @@ class Command(BaseCommand):
|
|||
By default, We use checks of the 'cmd' type of lock to determine
|
||||
if the command should be run.
|
||||
"""
|
||||
return permissions.has_perm(srcobj, self, 'cmd')
|
||||
return super(Command, self).access(srcobj)
|
||||
|
||||
def at_pre_cmd(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ class Object(BaseObject):
|
|||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Character(BaseCharacter):
|
||||
"""
|
||||
This is the default object created for a new user connecting - the
|
||||
|
|
@ -103,7 +103,6 @@ class Character(BaseCharacter):
|
|||
def at_object_creation(self):
|
||||
# This adds the default cmdset to the player every time they log
|
||||
# in. Don't change this unless you really know what you are doing.
|
||||
#self.scripts.add(scripts.AddDefaultCmdSet)
|
||||
super(Character, self).at_object_creation()
|
||||
|
||||
# expand with whatever customizations you want below...
|
||||
|
|
@ -147,6 +146,7 @@ class Exit(BaseExit):
|
|||
clean up a bit after themselves though, easiest accomplished
|
||||
by letting by_object_delete() call the object's parent.
|
||||
"""
|
||||
|
||||
def at_object_delete(self):
|
||||
"""
|
||||
The game needs to do some cache cleanups when deleting an exit,
|
||||
|
|
@ -158,7 +158,6 @@ class Exit(BaseExit):
|
|||
# custom modifications below.
|
||||
# ...
|
||||
|
||||
|
||||
class Player(BasePlayer):
|
||||
"""
|
||||
This class describes the actual OOC player (i.e. the user connecting
|
||||
|
|
|
|||
|
|
@ -338,7 +338,7 @@ def cmdhandler(caller, raw_string, unloggedin=False, testing=False):
|
|||
cmd_candidate, cmd = matches[0]
|
||||
|
||||
# Check so we have permission to use this command.
|
||||
if not cmd.has_perm(caller):
|
||||
if not cmd.access(caller):
|
||||
cmd = cmdset.get(CMD_NOPERM)
|
||||
if cmd:
|
||||
sysarg = raw_string
|
||||
|
|
|
|||
|
|
@ -28,9 +28,11 @@ class CmdSetMeta(type):
|
|||
# by default we key the cmdset the same as the
|
||||
# name of its class.
|
||||
mcs.key = mcs.__name__
|
||||
mcs.path = "%s.%s" % (mcs.__module__, mcs.__name__)
|
||||
|
||||
if not type(mcs.key_mergetypes) == dict:
|
||||
mcs.key_mergetypes = {}
|
||||
|
||||
super(CmdSetMeta, mcs).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -99,7 +99,6 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
|
|||
module = __import__(modulepath, fromlist=[True])
|
||||
cmdsetclass = module.__dict__[classname]
|
||||
CACHED_CMDSETS[wanted_cache_key] = cmdsetclass
|
||||
#print "cmdset %s found." % wanted_cache_key
|
||||
#instantiate the cmdset (and catch its errors)
|
||||
if callable(cmdsetclass):
|
||||
cmdsetclass = cmdsetclass(cmdsetobj)
|
||||
|
|
@ -362,3 +361,21 @@ class CmdSetHandler(object):
|
|||
self.cmdset_stack = [self.cmdset_stack[0]]
|
||||
self.mergetype_stack = [self.cmdset_stack[0].mergetype]
|
||||
self.update()
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
Force reload of all cmdsets in handler. This should be called
|
||||
after CACHED_CMDSETS have been cleared (normally by @reload).
|
||||
"""
|
||||
new_cmdset_stack = []
|
||||
new_mergetype_stack = []
|
||||
for cmdset in self.cmdset_stack:
|
||||
if cmdset.key == "Empty":
|
||||
new_cmdset_stack.append(cmdset)
|
||||
new_mergetype_stack.append("Union")
|
||||
else:
|
||||
new_cmdset_stack.append(self.import_cmdset(cmdset.path))
|
||||
new_mergetype_stack.append(cmdset.mergetype)
|
||||
self.cmdset_stack = new_cmdset_stack
|
||||
self.mergetype_stack = new_mergetype_stack
|
||||
self.update()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ All commands in Evennia inherit from the 'Command' class in this module.
|
|||
|
||||
"""
|
||||
|
||||
from src.permissions import permissions
|
||||
from src.locks.lockhandler import LockHandler
|
||||
from src.utils.utils import is_iter
|
||||
|
||||
class CommandMeta(type):
|
||||
|
|
@ -17,18 +17,26 @@ class CommandMeta(type):
|
|||
"""
|
||||
Simply make sure all data are stored as lowercase and
|
||||
do checking on all properties that should be in list form.
|
||||
Sets up locks to be more forgiving.
|
||||
"""
|
||||
mcs.key = mcs.key.lower()
|
||||
if mcs.aliases and not is_iter(mcs.aliases):
|
||||
mcs.aliases = mcs.aliases.split(',')
|
||||
mcs.aliases = [str(alias).strip().lower() for alias in mcs.aliases]
|
||||
if mcs.permissions and not is_iter(mcs.permissions) :
|
||||
mcs.permissions = mcs.permissions.split(',')
|
||||
mcs.permissions = [str(perm).strip().lower() for perm in mcs.permissions]
|
||||
|
||||
# pre-process locks as defined in class definition
|
||||
temp = []
|
||||
if hasattr(mcs, 'permissions'):
|
||||
mcs.locks = mcs.permissions
|
||||
for lockstring in mcs.locks.split(';'):
|
||||
if lockstring and not ':' in lockstring:
|
||||
lockstring = "cmd:%s" % lockstring
|
||||
temp.append(lockstring)
|
||||
mcs.lock_storage = ";".join(temp)
|
||||
|
||||
mcs.help_category = mcs.help_category.lower()
|
||||
super(CommandMeta, mcs).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
# The Command class is the basic unit of an Evennia command; when
|
||||
# defining new commands, the admin subclass this class and
|
||||
# define their own parser method to handle the input. The
|
||||
|
|
@ -69,15 +77,17 @@ class Command(object):
|
|||
key = "command"
|
||||
# alternative ways to call the command (e.g. 'l', 'glance', 'examine')
|
||||
aliases = []
|
||||
# a list of permission strings or comma-separated string limiting
|
||||
# access to this command.
|
||||
permissions = []
|
||||
# a list of lock definitions on the form cmd:[NOT] func(args) [ AND|OR][ NOT] func2(args)
|
||||
locks = ""
|
||||
# used by the help system to group commands in lists.
|
||||
help_category = "general"
|
||||
# There is also the property 'obj'. This gets set by the system
|
||||
# on the fly to tie this particular command to a certain in-game entity.
|
||||
# self.obj should NOT be defined here since it will not be overwritten
|
||||
# if it already exists.
|
||||
|
||||
def __init__(self):
|
||||
self.lockhandler = LockHandler(self)
|
||||
|
||||
def __str__(self):
|
||||
"Print the command"
|
||||
|
|
@ -115,15 +125,15 @@ class Command(object):
|
|||
"""
|
||||
return (cmdname == self.key) or (cmdname in self.aliases)
|
||||
|
||||
def has_perm(self, srcobj):
|
||||
def access(self, srcobj, access_type="cmd", default=False):
|
||||
"""
|
||||
This hook is called by the cmdhandler to determine if srcobj
|
||||
is allowed to execute this command. It should return a boolean
|
||||
value and is not normally something that need to be changed since
|
||||
it's using the Evennia permission system directly.
|
||||
"""
|
||||
return permissions.has_perm(srcobj, self, 'cmd')
|
||||
|
||||
return self.lockhandler.check(srcobj, access_type, default=default)
|
||||
|
||||
# Common Command hooks
|
||||
|
||||
def at_pre_cmd(self):
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ from django.conf import settings
|
|||
from django.contrib.auth.models import User
|
||||
from src.players.models import PlayerDB
|
||||
from src.server.sessionhandler import SESSIONS
|
||||
from src.permissions.permissions import has_perm, has_perm_string
|
||||
from src.permissions.models import PermissionGroup
|
||||
from src.utils import utils
|
||||
from src.commands.default.muxcommand import MuxCommand
|
||||
|
||||
|
|
@ -29,7 +27,7 @@ class CmdBoot(MuxCommand):
|
|||
"""
|
||||
|
||||
key = "@boot"
|
||||
permissions = "cmd:boot"
|
||||
locks = "cmd:perm(boot) or perm(Wizard)"
|
||||
help_category = "Admin"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -60,7 +58,7 @@ class CmdBoot(MuxCommand):
|
|||
if not pobj:
|
||||
return
|
||||
if pobj.character.has_player:
|
||||
if not has_perm(caller, pobj, 'can_boot'):
|
||||
if not pobj.access(caller, 'boot'):
|
||||
string = "You don't have the permission to boot %s."
|
||||
pobj.msg(string)
|
||||
return
|
||||
|
|
@ -107,7 +105,7 @@ class CmdDelPlayer(MuxCommand):
|
|||
"""
|
||||
|
||||
key = "@delplayer"
|
||||
permissions = "cmd:delplayer"
|
||||
locks = "cmd:perm(delplayer) or perm(Immortals)"
|
||||
help_category = "Admin"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -149,7 +147,7 @@ class CmdDelPlayer(MuxCommand):
|
|||
except Exception:
|
||||
player = None
|
||||
|
||||
if not has_perm_string(caller, 'manage_players'):
|
||||
if player and not player.access(caller, 'delete'):
|
||||
string = "You don't have the permissions to delete this player."
|
||||
caller.msg(string)
|
||||
return
|
||||
|
|
@ -166,20 +164,19 @@ class CmdDelPlayer(MuxCommand):
|
|||
caller.msg(string)
|
||||
return
|
||||
|
||||
elif len(players) > 1:
|
||||
string = "There where multiple matches:"
|
||||
elif utils.is_iter(players):
|
||||
string = "There were multiple matches:"
|
||||
for player in players:
|
||||
string += "\n %s %s" % (player.id, player.key)
|
||||
return
|
||||
|
||||
else:
|
||||
# one single match
|
||||
|
||||
player = players[0]
|
||||
player = players
|
||||
user = player.user
|
||||
character = player.character
|
||||
|
||||
if not has_perm(caller, player, 'manage_players'):
|
||||
if not player.access(caller, 'delete'):
|
||||
string = "You don't have the permissions to delete that player."
|
||||
caller.msg(string)
|
||||
return
|
||||
|
|
@ -209,7 +206,7 @@ class CmdEmit(MuxCommand):
|
|||
@pemit [<obj>, <obj>, ... =] <message>
|
||||
|
||||
Switches:
|
||||
room : limit emits to rooms only
|
||||
room : limit emits to rooms only (default)
|
||||
players : limit emits to players only
|
||||
contents : send to the contents of matched objects too
|
||||
|
||||
|
|
@ -221,7 +218,7 @@ class CmdEmit(MuxCommand):
|
|||
"""
|
||||
key = "@emit"
|
||||
aliases = ["@pemit", "@remit"]
|
||||
permissions = "cmd:emit"
|
||||
locks = "cmd:perm(emit) or perm(Builders)"
|
||||
help_category = "Admin"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -266,7 +263,7 @@ class CmdEmit(MuxCommand):
|
|||
if players_only and not obj.has_player:
|
||||
caller.msg("%s has no active player. Ignored." % objname)
|
||||
continue
|
||||
if has_perm(caller, obj, 'send_to'):
|
||||
if obj.access(caller, 'tell'):
|
||||
obj.msg(message)
|
||||
if send_to_contents:
|
||||
for content in obj.contents:
|
||||
|
|
@ -290,7 +287,7 @@ class CmdNewPassword(MuxCommand):
|
|||
"""
|
||||
|
||||
key = "@userpassword"
|
||||
permissions = "cmd:newpassword"
|
||||
locks = "cmd:perm(newpassword) or perm(Wizards)"
|
||||
help_category = "Admin"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -303,14 +300,13 @@ class CmdNewPassword(MuxCommand):
|
|||
return
|
||||
|
||||
# the player search also matches 'me' etc.
|
||||
character = caller.search("*%s" % self.lhs, global_search=True)
|
||||
if not character:
|
||||
player = caller.search("*%s" % self.lhs, global_search=True)
|
||||
if not player:
|
||||
return
|
||||
player = character.player
|
||||
player.user.set_password(self.rhs)
|
||||
player.user.save()
|
||||
caller.msg("%s - new password set to '%s'." % (player.name, self.rhs))
|
||||
if character != caller:
|
||||
if player.character != caller:
|
||||
player.msg("%s has changed your password to '%s'." % (caller.name, self.rhs))
|
||||
|
||||
|
||||
|
|
@ -333,7 +329,7 @@ class CmdPerm(MuxCommand):
|
|||
"""
|
||||
key = "@perm"
|
||||
aliases = "@setperm"
|
||||
permissions = "cmd:perm"
|
||||
locks = "cmd:perm(perm) or perm(Immortals)"
|
||||
help_category = "Admin"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -434,7 +430,7 @@ class CmdPuppet(MuxCommand):
|
|||
"""
|
||||
|
||||
key = "@puppet"
|
||||
permissions = "cmd:puppet"
|
||||
locks = "cmd:perm(puppet) or perm(Builders)"
|
||||
help_category = "Admin"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -453,6 +449,8 @@ class CmdPuppet(MuxCommand):
|
|||
if not utils.inherits_from(new_character, settings.BASE_CHARACTER_TYPECLASS):
|
||||
caller.msg("%s is not a Character." % self.args)
|
||||
return
|
||||
if new_character.player:
|
||||
caller.msg("This character is already under the control of a player.")
|
||||
if player.swap_character(new_character):
|
||||
new_character.msg("You now control %s." % new_character.name)
|
||||
else:
|
||||
|
|
@ -468,7 +466,7 @@ class CmdWall(MuxCommand):
|
|||
Announces a message to all connected players.
|
||||
"""
|
||||
key = "@wall"
|
||||
permissions = "cmd:wall"
|
||||
locks = "cmd:perm(wall) or perm(Wizards)"
|
||||
help_category = "Admin"
|
||||
|
||||
def func(self):
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ class CmdBatchCommands(MuxCommand):
|
|||
"""
|
||||
key = "@batchcommands"
|
||||
aliases = ["@batchcommand", "@batchcmd"]
|
||||
permissions = "cmd:batchcommands"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -277,7 +277,7 @@ class CmdBatchCode(MuxCommand):
|
|||
"""
|
||||
key = "@batchcode"
|
||||
aliases = ["@batchcodes"]
|
||||
permissions = "cmd:batchcodes"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -359,6 +359,7 @@ class CmdStateAbort(MuxCommand):
|
|||
"""
|
||||
key = "@abort"
|
||||
help_category = "BatchProcess"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
|
||||
def func(self):
|
||||
"Exit back to default."
|
||||
|
|
@ -374,6 +375,7 @@ class CmdStateLL(MuxCommand):
|
|||
"""
|
||||
key = "ll"
|
||||
help_category = "BatchProcess"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
|
||||
def func(self):
|
||||
show_curr(self.caller, showall=True)
|
||||
|
|
@ -386,6 +388,7 @@ class CmdStatePP(MuxCommand):
|
|||
"""
|
||||
key = "pp"
|
||||
help_category = "BatchProcess"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
|
|
@ -407,6 +410,7 @@ class CmdStateRR(MuxCommand):
|
|||
"""
|
||||
key = "rr"
|
||||
help_category = "BatchProcess"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
|
||||
def func(self):
|
||||
caller = self.caller
|
||||
|
|
@ -426,6 +430,7 @@ class CmdStateRRR(MuxCommand):
|
|||
"""
|
||||
key = "rrr"
|
||||
help_category = "BatchProcess"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
|
||||
def func(self):
|
||||
caller = self.caller
|
||||
|
|
@ -445,6 +450,7 @@ class CmdStateNN(MuxCommand):
|
|||
"""
|
||||
key = "nn"
|
||||
help_category = "BatchProcess"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
|
||||
def func(self):
|
||||
caller = self.caller
|
||||
|
|
@ -465,6 +471,7 @@ class CmdStateNL(MuxCommand):
|
|||
"""
|
||||
key = "nl"
|
||||
help_category = "BatchProcess"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
|
||||
def func(self):
|
||||
caller = self.caller
|
||||
|
|
@ -485,6 +492,7 @@ class CmdStateBB(MuxCommand):
|
|||
"""
|
||||
key = "bb"
|
||||
help_category = "BatchProcess"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
|
||||
def func(self):
|
||||
caller = self.caller
|
||||
|
|
@ -505,6 +513,7 @@ class CmdStateBL(MuxCommand):
|
|||
"""
|
||||
key = "bl"
|
||||
help_category = "BatchProcess"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
|
||||
def func(self):
|
||||
caller = self.caller
|
||||
|
|
@ -526,6 +535,7 @@ class CmdStateSS(MuxCommand):
|
|||
"""
|
||||
key = "ss"
|
||||
help_category = "BatchProcess"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
|
||||
def func(self):
|
||||
caller = self.caller
|
||||
|
|
@ -553,6 +563,7 @@ class CmdStateSL(MuxCommand):
|
|||
"""
|
||||
key = "sl"
|
||||
help_category = "BatchProcess"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
|
||||
def func(self):
|
||||
caller = self.caller
|
||||
|
|
@ -579,6 +590,7 @@ class CmdStateCC(MuxCommand):
|
|||
"""
|
||||
key = "cc"
|
||||
help_category = "BatchProcess"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
|
||||
def func(self):
|
||||
caller = self.caller
|
||||
|
|
@ -608,6 +620,7 @@ class CmdStateJJ(MuxCommand):
|
|||
"""
|
||||
key = "j"
|
||||
help_category = "BatchProcess"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
|
||||
def func(self):
|
||||
caller = self.caller
|
||||
|
|
@ -630,6 +643,7 @@ class CmdStateJL(MuxCommand):
|
|||
"""
|
||||
key = "jl"
|
||||
help_category = "BatchProcess"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
|
||||
def func(self):
|
||||
caller = self.caller
|
||||
|
|
@ -652,6 +666,7 @@ class CmdStateQQ(MuxCommand):
|
|||
"""
|
||||
key = "qq"
|
||||
help_category = "BatchProcess"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
|
||||
def func(self):
|
||||
purge_processor(self.caller)
|
||||
|
|
@ -662,6 +677,7 @@ class CmdStateHH(MuxCommand):
|
|||
|
||||
key = "hh"
|
||||
help_category = "BatchProcess"
|
||||
locks = "cmd:perm(batchcommands)"
|
||||
|
||||
def func(self):
|
||||
string = """
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ Building and world design commands
|
|||
"""
|
||||
|
||||
from django.conf import settings
|
||||
from src.permissions.permissions import has_perm, has_perm_string
|
||||
from src.objects.models import ObjectDB, ObjAttribute
|
||||
from src.utils import create, utils, debug
|
||||
from src.commands.default.muxcommand import MuxCommand
|
||||
|
|
@ -121,7 +120,7 @@ class CmdSetObjAlias(MuxCommand):
|
|||
|
||||
key = "@alias"
|
||||
aliases = "@setobjalias"
|
||||
permissions = "cmd:setobjalias"
|
||||
locks = "cmd:perm(setobjalias) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -143,7 +142,7 @@ class CmdSetObjAlias(MuxCommand):
|
|||
else:
|
||||
caller.msg("No aliases exist for '%s'." % obj.key)
|
||||
return
|
||||
if not has_perm(caller, obj, 'modify_attributes'):
|
||||
if not obj.access(caller, 'edit'):
|
||||
caller.msg("You don't have permission to do that.")
|
||||
return
|
||||
if not aliases or not aliases[0]:
|
||||
|
|
@ -164,7 +163,7 @@ class CmdSetObjAlias(MuxCommand):
|
|||
aliases = list(set(old_aliases))
|
||||
# save back to object.
|
||||
obj.aliases = aliases
|
||||
caller.msg("Aliases for '%s' are now set to %s." % (obj.key, obj.aliases))
|
||||
caller.msg("Aliases for '%s' are now set to %s." % (obj.key, ", ".join(obj.aliases)))
|
||||
|
||||
class CmdCopy(ObjManipCommand):
|
||||
"""
|
||||
|
|
@ -183,7 +182,7 @@ class CmdCopy(ObjManipCommand):
|
|||
"""
|
||||
|
||||
key = "@copy"
|
||||
permissions = "cmd:copy"
|
||||
locks = "cmd:perm(copy) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -252,7 +251,7 @@ class CmdCpAttr(MuxCommand):
|
|||
Copy the attribute one object to one or more attributes on another object.
|
||||
"""
|
||||
key = "@cpattr"
|
||||
permissions = "cmd:cpattr"
|
||||
locks = "cmd:perm(cpattr) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -334,7 +333,7 @@ class CmdCreate(ObjManipCommand):
|
|||
"""
|
||||
|
||||
key = "@create"
|
||||
permissions = "cmd:create"
|
||||
locks = "cmd:perm(create) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -366,8 +365,10 @@ class CmdCreate(ObjManipCommand):
|
|||
|
||||
# create object (if not a valid typeclass, the default
|
||||
# object typeclass will automatically be used)
|
||||
|
||||
lockstring = "owner:id(%s);examine:perm(Builders);delete:id(%s) or perm(Wizards);get:all()" % (caller.id, caller.id)
|
||||
obj = create.create_object(typeclass, name, caller,
|
||||
home=caller, aliases=aliases)
|
||||
home=caller, aliases=aliases, locks=lockstring)
|
||||
if not obj:
|
||||
string = "Error when creating object."
|
||||
continue
|
||||
|
|
@ -412,7 +413,7 @@ class CmdDebug(MuxCommand):
|
|||
"""
|
||||
|
||||
key = "@debug"
|
||||
permissions = "cmd:debug"
|
||||
locks = "cmd:perm(debug) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -462,7 +463,7 @@ class CmdDesc(MuxCommand):
|
|||
"""
|
||||
key = "@desc"
|
||||
aliases = "@describe"
|
||||
permissions = "cmd:desc"
|
||||
locks = "cmd:perm(desc) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -504,7 +505,7 @@ class CmdDestroy(MuxCommand):
|
|||
|
||||
key = "@destroy"
|
||||
aliases = "@delete"
|
||||
permissions = "cmd:destroy"
|
||||
locks = "cmd:perm(destroy) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -525,7 +526,7 @@ class CmdDestroy(MuxCommand):
|
|||
if obj.player and not 'override' in self.switches:
|
||||
string = "Object %s is a player object. Use /override to delete anyway." % objname
|
||||
continue
|
||||
if not has_perm(caller, obj, 'create'):
|
||||
if not obj.access(caller, 'delete'):
|
||||
string = "You don't have permission to delete %s." % objname
|
||||
continue
|
||||
# do the deletion
|
||||
|
|
@ -558,7 +559,7 @@ class CmdDig(ObjManipCommand):
|
|||
like to the name of the room and the exits in question; an example would be 'north;no;n'.
|
||||
"""
|
||||
key = "@dig"
|
||||
permissions = "cmd:dig"
|
||||
locks = "cmd:perm(dig) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -681,7 +682,7 @@ class CmdLink(MuxCommand):
|
|||
"""
|
||||
|
||||
key = "@link"
|
||||
permissions = "cmd:link"
|
||||
locks = "cmd:perm(link) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -759,7 +760,7 @@ class CmdListCmdSets(MuxCommand):
|
|||
"""
|
||||
key = "@cmdsets"
|
||||
aliases = "@listcmsets"
|
||||
permissions = "cmd:listcmdsets"
|
||||
locks = "cmd:perm(listcmdsets) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -789,7 +790,7 @@ class CmdMvAttr(ObjManipCommand):
|
|||
and target are the same, in which case this is like a copy operation)
|
||||
"""
|
||||
key = "@mvattr"
|
||||
permissions = "cmd:mvattr"
|
||||
locks = "cmd:perm(mvattr) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -861,7 +862,7 @@ class CmdName(ObjManipCommand):
|
|||
|
||||
key = "@name"
|
||||
aliases = ["@rename"]
|
||||
permissions = "cmd:rename"
|
||||
locks = "cmd:perm(rename) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -912,7 +913,7 @@ class CmdOpen(ObjManipCommand):
|
|||
|
||||
"""
|
||||
key = "@open"
|
||||
permissions = "cmd:open"
|
||||
locks = "cmd:perm(open) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
# a custom member method to chug out exits and do checks
|
||||
|
|
@ -1053,7 +1054,7 @@ class CmdSetAttribute(ObjManipCommand):
|
|||
"""
|
||||
|
||||
key = "@set"
|
||||
permissions = "cmd:set"
|
||||
locks = "cmd:perm(set) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -1128,7 +1129,7 @@ class CmdTypeclass(MuxCommand):
|
|||
|
||||
key = "@typeclass"
|
||||
aliases = "@type, @parent"
|
||||
permissions = "cmd:typeclass"
|
||||
locks = "cmd:perm(typeclass) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -1166,7 +1167,7 @@ class CmdTypeclass(MuxCommand):
|
|||
typeclass = "%s.%s" % (settings.BASE_TYPECLASS_PATH,
|
||||
typeclass)
|
||||
|
||||
if not has_perm(caller, obj, 'change_typeclass'):
|
||||
if not obj.access(caller, 'edit'):
|
||||
caller.msg("You are not allowed to do that.")
|
||||
return
|
||||
|
||||
|
|
@ -1208,7 +1209,7 @@ class CmdWipe(ObjManipCommand):
|
|||
matching the given attribute-wildcard search string.
|
||||
"""
|
||||
key = "@wipe"
|
||||
permissions = "cmd:wipe"
|
||||
locks = "cmd:perm(wipe) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -1229,6 +1230,9 @@ class CmdWipe(ObjManipCommand):
|
|||
obj = caller.search(objname)
|
||||
if not obj:
|
||||
return
|
||||
if not obj.access(caller, 'edit'):
|
||||
caller.msg("You are not allowed to do that.")
|
||||
return
|
||||
if not attrs:
|
||||
# wipe everything
|
||||
for attr in obj.get_all_attributes():
|
||||
|
|
@ -1241,7 +1245,90 @@ class CmdWipe(ObjManipCommand):
|
|||
string = string % (",".join(attrs), obj.name)
|
||||
caller.msg(string)
|
||||
|
||||
class CmdLock(ObjManipCommand):
|
||||
"""
|
||||
lock - assign a lock definition to an object
|
||||
|
||||
Usage:
|
||||
@lock <object>[ = <lockstring>]
|
||||
or
|
||||
@lock[/switch] object/<access_type>
|
||||
|
||||
Switch:
|
||||
del - delete given access type
|
||||
view - view lock associated with given access type (default)
|
||||
|
||||
If no lockstring is given, shows all locks on
|
||||
object.
|
||||
|
||||
Lockstring is on the form
|
||||
'access_type:[NOT] func1(args)[ AND|OR][ NOT] func2(args) ...]
|
||||
Where func1, func2 ... valid lockfuncs with or without arguments.
|
||||
Separator expressions need not be capitalized.
|
||||
|
||||
For example:
|
||||
'get: id(25) or perm(Wizards)'
|
||||
The 'get' access_type is checked by the get command and will
|
||||
an object locked with this string will only be possible to
|
||||
pick up by Wizards or by object with id 25.
|
||||
|
||||
You can add several access_types after oneanother by separating
|
||||
them by ';', i.e:
|
||||
'get:id(25);delete:perm(Builders)'
|
||||
"""
|
||||
key = "@lock"
|
||||
aliases = ["@locks", "lock", "locks"]
|
||||
locks = "cmd: perm(@locks) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
"Sets up the command"
|
||||
|
||||
caller = self.caller
|
||||
if not self.args:
|
||||
string = "@lock <object>[ = <lockstring>] or @lock[/switch] object/<access_type>"
|
||||
caller.msg(string)
|
||||
return
|
||||
if '/' in self.lhs:
|
||||
# call on the form @lock obj/access_type
|
||||
objname, access_type = [p.strip() for p in self.lhs.split('/', 1)]
|
||||
obj = caller.search(objname)
|
||||
if not obj:
|
||||
return
|
||||
lockdef = obj.locks.get(access_type)
|
||||
if lockdef:
|
||||
string = lockdef[2]
|
||||
if 'del' in self.switches:
|
||||
if obj.access(caller, 'edit'):
|
||||
caller.msg("You are not allowed to do that.")
|
||||
return
|
||||
obj.locks.delete(access_type)
|
||||
string = "deleted lock %s" % string
|
||||
else:
|
||||
string = "%s has no lock of access type '%s'." % (obj, access_type)
|
||||
caller.msg(string)
|
||||
return
|
||||
|
||||
if self.rhs:
|
||||
# we have a = separator, so we are assigning a new lock
|
||||
objname, lockdef = self.lhs, self.rhs
|
||||
obj = caller.search(objname)
|
||||
if not obj:
|
||||
return
|
||||
if obj.access(caller, 'edit'):
|
||||
caller.msg("You are not allowed to do that.")
|
||||
return
|
||||
ok = obj.locks.add(lockdef, caller)
|
||||
if ok:
|
||||
caller.msg("Added lock '%s' to %s." % (lockdef, obj))
|
||||
return
|
||||
|
||||
# if we get here, we are just viewing all locks
|
||||
obj = caller.search(self.lhs)
|
||||
if not obj:
|
||||
return
|
||||
caller.msg(obj.locks)
|
||||
|
||||
class CmdExamine(ObjManipCommand):
|
||||
"""
|
||||
examine - detailed info on objects
|
||||
|
|
@ -1255,7 +1342,7 @@ class CmdExamine(ObjManipCommand):
|
|||
"""
|
||||
key = "@examine"
|
||||
aliases = ["@ex","ex", "exam"]
|
||||
permissions = "cmd:examine"
|
||||
locks = "cmd:perm(examine) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def crop_line(self, text, heading="", line_width=79):
|
||||
|
|
@ -1324,7 +1411,10 @@ class CmdExamine(ObjManipCommand):
|
|||
string += "\n{wLocation{n: %s" % obj.location
|
||||
perms = obj.permissions
|
||||
if perms:
|
||||
string += "\n{wObj Perms/Locks{n: %s" % (", ".join(perms))
|
||||
string += "\n{wPermissions{n: %s" % (", ".join(perms))
|
||||
locks = str(obj.locks)
|
||||
if locks:
|
||||
string += "\n{wLocks{n: %s" % ("; ".join([lock for lock in locks.split(';')]))
|
||||
if not (len(obj.cmdset.all()) == 1 and obj.cmdset.current.key == "Empty"):
|
||||
string += "\n{wCurrent Cmdset (before permission checks){n:\n\r %s" % obj.cmdset
|
||||
if obj.scripts.all():
|
||||
|
|
@ -1360,7 +1450,7 @@ class CmdExamine(ObjManipCommand):
|
|||
if not self.args:
|
||||
# If no arguments are provided, examine the invoker's location.
|
||||
obj = caller.location
|
||||
if not has_perm(caller, obj, 'obj_info'):
|
||||
if not obj.access(caller, 'examine'):
|
||||
#If we don't have special info access, just look at the object instead.
|
||||
caller.exec_cmd('look %s' % obj.name)
|
||||
return
|
||||
|
|
@ -1379,7 +1469,7 @@ class CmdExamine(ObjManipCommand):
|
|||
obj = caller.search(obj_name)
|
||||
if not obj:
|
||||
continue
|
||||
if not has_perm(caller, obj, 'obj_info'):
|
||||
if not obj.access(caller, 'examine'):
|
||||
#If we don't have special info access, just look at the object instead.
|
||||
caller.exec_cmd('look %s' % obj_name)
|
||||
continue
|
||||
|
|
@ -1407,7 +1497,7 @@ class CmdFind(MuxCommand):
|
|||
|
||||
key = "@find"
|
||||
aliases = "@locate, find, locate"
|
||||
permissions = "cmd:find"
|
||||
locks = "cmd:perm(find) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -1447,7 +1537,7 @@ class CmdTeleport(MuxCommand):
|
|||
"""
|
||||
key = "@tel"
|
||||
aliases = "@teleport"
|
||||
permissions = "cmd:teleport"
|
||||
locks = "cmd:perm(teleport) or perm(Builders)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -1501,7 +1591,7 @@ class CmdUnLink(CmdLink):
|
|||
# this is just a child of CmdLink
|
||||
|
||||
key = "@unlink"
|
||||
permissions = "cmd:unlink"
|
||||
locks = "cmd:perm(unlink) or perm(Builders)"
|
||||
help_key = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class DefaultCmdSet(CmdSet):
|
|||
self.add(general.CmdDrop())
|
||||
self.add(general.CmdWho())
|
||||
self.add(general.CmdSay())
|
||||
self.add(general.CmdGroup())
|
||||
self.add(general.CmdAccess())
|
||||
self.add(general.CmdEncoding())
|
||||
|
||||
# The help system
|
||||
|
|
@ -37,16 +37,15 @@ class DefaultCmdSet(CmdSet):
|
|||
# System commands
|
||||
self.add(system.CmdReload())
|
||||
self.add(system.CmdPy())
|
||||
self.add(system.CmdListScripts())
|
||||
self.add(system.CmdListObjects())
|
||||
self.add(system.CmdScripts())
|
||||
self.add(system.CmdObjects())
|
||||
self.add(system.CmdService())
|
||||
self.add(system.CmdShutdown())
|
||||
self.add(system.CmdVersion())
|
||||
self.add(system.CmdTime())
|
||||
self.add(system.CmdList())
|
||||
self.add(system.CmdServerLoad())
|
||||
self.add(system.CmdPs())
|
||||
self.add(system.CmdStats())
|
||||
|
||||
|
||||
# Admin commands
|
||||
self.add(admin.CmdBoot())
|
||||
self.add(admin.CmdDelPlayer())
|
||||
|
|
@ -77,6 +76,7 @@ class DefaultCmdSet(CmdSet):
|
|||
self.add(building.CmdDestroy())
|
||||
self.add(building.CmdExamine())
|
||||
self.add(building.CmdTypeclass())
|
||||
self.add(building.CmdLock())
|
||||
|
||||
# Comm commands
|
||||
self.add(comms.CmdAddCom())
|
||||
|
|
@ -93,5 +93,5 @@ class DefaultCmdSet(CmdSet):
|
|||
|
||||
# Testing/Utility commands
|
||||
self.add(utils.CmdTest())
|
||||
self.add(utils.CmdTestPerms())
|
||||
#self.add(utils.CmdTestPerms())
|
||||
self.add(utils.TestCom())
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ Comsys command module.
|
|||
from src.comms.models import Channel, Msg, ChannelConnection
|
||||
from src.comms.channelhandler import CHANNELHANDLER
|
||||
from src.utils import create, utils
|
||||
from src.permissions.permissions import has_perm
|
||||
from src.commands.default.muxcommand import MuxCommand
|
||||
|
||||
def find_channel(caller, channelname, silent=False):
|
||||
|
|
@ -41,6 +40,7 @@ class CmdAddCom(MuxCommand):
|
|||
key = "addcom"
|
||||
aliases = ["aliaschan","chanalias"]
|
||||
help_category = "Comms"
|
||||
locks = "cmd:not perm(channel_banned)"
|
||||
|
||||
def func(self):
|
||||
"Implement the command"
|
||||
|
|
@ -67,7 +67,7 @@ class CmdAddCom(MuxCommand):
|
|||
return
|
||||
|
||||
# check permissions
|
||||
if not has_perm(player, channel, 'chan_listen'):
|
||||
if not channel.access(player, 'listen'):
|
||||
caller.msg("You are not allowed to listen to this channel.")
|
||||
return
|
||||
|
||||
|
|
@ -83,7 +83,7 @@ class CmdAddCom(MuxCommand):
|
|||
|
||||
if alias:
|
||||
# create a nick and add it to the caller.
|
||||
caller.nickhandler(alias, channel.key, nick_type="channel")
|
||||
caller.nicks.add(alias, channel.key, nick_type="channel")
|
||||
string += "You can now refer to the channel %s with the alias '%s'."
|
||||
caller.msg(string % (channel.key, alias))
|
||||
else:
|
||||
|
|
@ -106,6 +106,7 @@ class CmdDelCom(MuxCommand):
|
|||
key = "delcom"
|
||||
aliases = ["delaliaschan, delchanalias"]
|
||||
help_category = "Comms"
|
||||
locks = "cmd:not perm(channel_banned)"
|
||||
|
||||
def func(self):
|
||||
"Implementing the command. "
|
||||
|
|
@ -126,20 +127,20 @@ class CmdDelCom(MuxCommand):
|
|||
return
|
||||
chkey = channel.key.lower()
|
||||
# find all nicks linked to this channel and delete them
|
||||
for nick in [nick for nick in caller.nicks
|
||||
if nick.db_type == "channel" and nick.db_real.lower() == chkey]:
|
||||
for nick in [nick for nick in caller.nicks.get(nick_type="channel")
|
||||
if nick.db_real.lower() == chkey]:
|
||||
nick.delete()
|
||||
channel.disconnect_from(player)
|
||||
caller.msg("You stop listening to channel '%s'. Eventual aliases were removed." % channel.key)
|
||||
return
|
||||
else:
|
||||
# we are removing a channel nick
|
||||
channame = caller.nickhandler(ostring, nick_type="channel")
|
||||
channame = caller.nicks.get(ostring, nick_type="channel")
|
||||
channel = find_channel(caller, channame, silent=True)
|
||||
if not channel:
|
||||
caller.msg("No channel with alias '%s' was found." % ostring)
|
||||
else:
|
||||
caller.nickhandler(ostring, nick_type="channel", delete=True)
|
||||
caller.nicks.delete(ostring, nick_type="channel")
|
||||
caller.msg("Your alias '%s' for channel %s was cleared." % (ostring, channel.key))
|
||||
|
||||
# def cmd_allcom(command):
|
||||
|
|
@ -249,6 +250,7 @@ class CmdChannels(MuxCommand):
|
|||
key = "@channels"
|
||||
aliases = ["@clist", "channels", "comlist", "chanlist", "channellist", "all channels"]
|
||||
help_category = "Comms"
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"Implement function"
|
||||
|
|
@ -256,7 +258,7 @@ class CmdChannels(MuxCommand):
|
|||
caller = self.caller
|
||||
|
||||
# all channels we have available to listen to
|
||||
channels = [chan for chan in Channel.objects.get_all_channels() if has_perm(caller, chan, 'can_listen')]
|
||||
channels = [chan for chan in Channel.objects.get_all_channels() if chan.access(caller, 'listen')]
|
||||
if not channels:
|
||||
caller.msg("No channels available")
|
||||
return
|
||||
|
|
@ -265,7 +267,7 @@ class CmdChannels(MuxCommand):
|
|||
|
||||
if self.cmdstring != "comlist":
|
||||
|
||||
string = "\nAll available channels:"
|
||||
string = "\nChannels available:"
|
||||
cols = [[" "], ["Channel"], ["Aliases"], ["Perms"], ["Description"]]
|
||||
for chan in channels:
|
||||
if chan in subs:
|
||||
|
|
@ -274,7 +276,7 @@ class CmdChannels(MuxCommand):
|
|||
cols[0].append(" ")
|
||||
cols[1].append(chan.key)
|
||||
cols[2].append(",".join(chan.aliases))
|
||||
cols[3].append(",".join(chan.permissions))
|
||||
cols[3].append(str(chan.locks))
|
||||
cols[4].append(chan.desc)
|
||||
# put into table
|
||||
for ir, row in enumerate(utils.format_table(cols)):
|
||||
|
|
@ -284,18 +286,18 @@ class CmdChannels(MuxCommand):
|
|||
string += "\n" + "".join(row)
|
||||
self.caller.msg(string)
|
||||
|
||||
string = "\nYour channel subscriptions:"
|
||||
string = "\nChannel subscriptions:"
|
||||
if not subs:
|
||||
string += "(None)"
|
||||
else:
|
||||
nicks = [nick for nick in caller.nicks if nick.db_type == 'channel']
|
||||
print nicks
|
||||
cols = [["Channel"], ["Aliases"], ["Description"]]
|
||||
nicks = [nick for nick in caller.nicks.get(nick_type="channel")]
|
||||
cols = [[" "], ["Channel"], ["Aliases"], ["Description"]]
|
||||
for chan in subs:
|
||||
cols[0].append(chan.key)
|
||||
cols[1].append(",".join([nick.db_nick for nick in nicks
|
||||
cols[0].append(" ")
|
||||
cols[1].append(chan.key)
|
||||
cols[2].append(",".join([nick.db_nick for nick in nicks
|
||||
if nick.db_real.lower() == chan.key.lower()] + chan.aliases))
|
||||
cols[2].append(chan.desc)
|
||||
cols[3].append(chan.desc)
|
||||
# put into table
|
||||
for ir, row in enumerate(utils.format_table(cols)):
|
||||
if ir == 0:
|
||||
|
|
@ -316,6 +318,7 @@ class CmdCdestroy(MuxCommand):
|
|||
|
||||
key = "@cdestroy"
|
||||
help_category = "Comms"
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"Destroy objects cleanly."
|
||||
|
|
@ -328,7 +331,7 @@ class CmdCdestroy(MuxCommand):
|
|||
if not channel:
|
||||
caller.msg("Could not find channel %s." % self.args)
|
||||
return
|
||||
if not has_perm(caller, channel, 'chan_admin', default_deny=True):
|
||||
if not channel.access(caller, 'admin'):
|
||||
caller.msg("You are not allowed to do that.")
|
||||
return
|
||||
|
||||
|
|
@ -572,7 +575,7 @@ class CmdChannelCreate(MuxCommand):
|
|||
|
||||
key = "@ccreate"
|
||||
aliases = "channelcreate"
|
||||
permissions = "cmd:ccreate"
|
||||
locks = "cmd:not perm(channel_banned)"
|
||||
help_category = "Comms"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -601,9 +604,8 @@ class CmdChannelCreate(MuxCommand):
|
|||
caller.msg("A channel with that name already exists.")
|
||||
return
|
||||
# Create and set the channel up
|
||||
permissions = "chan_send:%s,chan_listen:%s,chan_admin:has_id(%s)" % \
|
||||
("Players","Players",caller.id)
|
||||
new_chan = create.create_channel(channame, aliases, description, permissions)
|
||||
lockstring = "send:all();listen:all();admin:id(%s)" % caller.id
|
||||
new_chan = create.create_channel(channame, aliases, description, locks=lockstring)
|
||||
new_chan.connect_to(caller)
|
||||
caller.msg("Created channel %s and connected to it." % new_chan.key)
|
||||
|
||||
|
|
@ -663,7 +665,7 @@ class CmdCdesc(MuxCommand):
|
|||
"""
|
||||
|
||||
key = "@cdesc"
|
||||
permissions = "cmd:cdesc"
|
||||
locks = "cmd:not perm(channel_banned)"
|
||||
help_category = "Comms"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -679,7 +681,7 @@ class CmdCdesc(MuxCommand):
|
|||
caller.msg("Channel '%s' not found." % self.lhs)
|
||||
return
|
||||
#check permissions
|
||||
if not has_perm(caller, channel, 'channel_admin'):
|
||||
if not caller.access(caller, 'admin'):
|
||||
caller.msg("You cant admin this channel.")
|
||||
return
|
||||
# set the description
|
||||
|
|
@ -694,19 +696,19 @@ class CmdPage(MuxCommand):
|
|||
Usage:
|
||||
page[/switches] [<player>,<player>,... = <message>]
|
||||
tell ''
|
||||
page/list <number>
|
||||
page <number>
|
||||
|
||||
Switch:
|
||||
list - show your last <number> of tells/pages.
|
||||
last - shows who you last messaged
|
||||
list - show your last <number> of tells/pages (default)
|
||||
|
||||
Send a message to target user (if online). If no
|
||||
argument is given, you will instead see who was the last
|
||||
person you paged to.
|
||||
argument is given, you will get a list of your latest messages.
|
||||
"""
|
||||
|
||||
key = "page"
|
||||
aliases = ['tell']
|
||||
permissions = "cmd:tell"
|
||||
locks = "cmd:not perm(page_banned)"
|
||||
help_category = "Comms"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -722,17 +724,29 @@ class CmdPage(MuxCommand):
|
|||
if msg.receivers]
|
||||
# get last messages we've got
|
||||
pages_we_got = list(Msg.objects.get_messages_by_receiver(player))
|
||||
|
||||
if 'list' in self.switches:
|
||||
|
||||
if 'last' in self.switches:
|
||||
if pages_we_sent:
|
||||
string = "You last paged {c%s{n." % (", ".join([obj.name
|
||||
for obj in pages_we_sent[-1].receivers]))
|
||||
caller.msg(string)
|
||||
return
|
||||
else:
|
||||
string = "You haven't paged anyone yet."
|
||||
caller.msg(string)
|
||||
return
|
||||
|
||||
if not self.args or not self.rhs:
|
||||
pages = pages_we_sent + pages_we_got
|
||||
pages.sort(lambda x, y: cmp(x.date_sent, y.date_sent))
|
||||
|
||||
number = 10
|
||||
number = 5
|
||||
if self.args:
|
||||
try:
|
||||
number = int(self.args)
|
||||
except ValueError:
|
||||
pass
|
||||
caller.msg("Usage: tell [<player> = msg]")
|
||||
return
|
||||
|
||||
if len(pages) > number:
|
||||
lastpages = pages[-number:]
|
||||
|
|
@ -744,19 +758,14 @@ class CmdPage(MuxCommand):
|
|||
"{n,{c ".join([obj.name for obj in page.receivers]),
|
||||
page.message)
|
||||
for page in lastpages])
|
||||
caller.msg("Your latest pages:\n %s" % lastpages )
|
||||
return
|
||||
|
||||
if not self.args or not self.rhs:
|
||||
if pages_we_sent:
|
||||
string = "You last paged {c%s{n." % (", ".join([obj.name
|
||||
for obj in pages_we_sent[-1].receivers]))
|
||||
caller.msg(string)
|
||||
return
|
||||
if lastpages:
|
||||
string = "Your latest pages:\n %s" % lastpages
|
||||
else:
|
||||
string = "You haven't paged anyone yet."
|
||||
caller.msg(string)
|
||||
return
|
||||
caller.msg(string)
|
||||
return
|
||||
|
||||
|
||||
# We are sending. Build a list of targets
|
||||
|
||||
|
|
@ -786,7 +795,7 @@ class CmdPage(MuxCommand):
|
|||
if not recobjs:
|
||||
caller.msg("No players matching your target were found.")
|
||||
return
|
||||
|
||||
|
||||
header = "{wPlayer{n {c%s{n {wpages:{n" % caller.key
|
||||
message = self.rhs
|
||||
|
||||
|
|
@ -800,12 +809,17 @@ class CmdPage(MuxCommand):
|
|||
|
||||
# tell the players they got a message.
|
||||
received = []
|
||||
rstrings = []
|
||||
for pobj in recobjs:
|
||||
pobj.msg("%s %s" % (header, message))
|
||||
if not pobj.access(caller, 'msg'):
|
||||
rstrings.append("You are not allowed to page %s." % pobj)
|
||||
continue
|
||||
pobj.msg("%s %s" % (header, message))
|
||||
if hasattr(pobj, 'has_player') and not pobj.has_player:
|
||||
received.append("{C%s{n" % pobj.name)
|
||||
caller.msg("%s is offline. They will see your message if they list their pages later." % received[-1])
|
||||
rstrings.append("%s is offline. They will see your message if they list their pages later." % received[-1])
|
||||
else:
|
||||
received.append("{c%s{n" % pobj.name)
|
||||
received = ", ".join(received)
|
||||
caller.msg("You paged %s with: '%s'." % (received, message))
|
||||
if rstrings:
|
||||
caller.msg(rstrings = "\n".join(rstrings))
|
||||
caller.msg("You paged %s with: '%s'." % (", ".join(received), message))
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ now.
|
|||
import time
|
||||
from django.conf import settings
|
||||
from src.server.sessionhandler import SESSIONS
|
||||
from src.permissions.models import PermissionGroup
|
||||
from src.permissions.permissions import has_perm, has_perm_string
|
||||
from src.objects.models import HANDLE_SEARCH_ERRORS
|
||||
from src.utils import utils
|
||||
from src.objects.models import Nick
|
||||
|
|
@ -23,7 +21,7 @@ class CmdHome(MuxCommand):
|
|||
"""
|
||||
|
||||
key = "home"
|
||||
permissions = "cmd:home"
|
||||
locks = "cmd:perm(home) or perm(Builders)"
|
||||
|
||||
def func(self):
|
||||
"Implement the command"
|
||||
|
|
@ -48,6 +46,7 @@ class CmdLook(MuxCommand):
|
|||
"""
|
||||
key = "look"
|
||||
aliases = ["l"]
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
|
|
@ -84,6 +83,7 @@ class CmdPassword(MuxCommand):
|
|||
Changes your password. Make sure to pick a safe one.
|
||||
"""
|
||||
key = "@password"
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"hook function."
|
||||
|
|
@ -138,7 +138,8 @@ class CmdNick(MuxCommand):
|
|||
"""
|
||||
key = "nick"
|
||||
aliases = ["nickname", "nicks", "@nick", "alias"]
|
||||
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"Create the nickname"
|
||||
|
||||
|
|
@ -185,7 +186,7 @@ class CmdNick(MuxCommand):
|
|||
if oldnick:
|
||||
# clear the alias
|
||||
string += "\nNick '%s' (= '%s') was cleared." % (nick, oldnick[0].db_real)
|
||||
caller.nickhandler(nick, nick_type=switch, delete=True)
|
||||
caller.nicks.delete(nick, nick_type=switch)
|
||||
else:
|
||||
string += "\nNo nick '%s' found, so it could not be removed." % nick
|
||||
else:
|
||||
|
|
@ -194,7 +195,7 @@ class CmdNick(MuxCommand):
|
|||
string += "\nNick %s changed from '%s' to '%s'." % (nick, oldnick[0].db_real, real)
|
||||
else:
|
||||
string += "\nNick set: '%s' = '%s'." % (nick, real)
|
||||
caller.nickhandler(nick, real, nick_type=switch)
|
||||
caller.nicks.add(nick, real, nick_type=switch)
|
||||
caller.msg(string)
|
||||
|
||||
class CmdInventory(MuxCommand):
|
||||
|
|
@ -209,6 +210,7 @@ class CmdInventory(MuxCommand):
|
|||
"""
|
||||
key = "inventory"
|
||||
aliases = ["inv", "i"]
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"hook function"
|
||||
|
|
@ -237,7 +239,8 @@ class CmdGet(MuxCommand):
|
|||
"""
|
||||
key = "get"
|
||||
aliases = "grab"
|
||||
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"implements the command."
|
||||
|
||||
|
|
@ -256,7 +259,7 @@ class CmdGet(MuxCommand):
|
|||
# don't allow picking up player objects, nor exits.
|
||||
caller.msg("You can't get that.")
|
||||
return
|
||||
if not has_perm(caller, obj, 'get'):
|
||||
if not obj.access(caller, 'get'):
|
||||
if obj.db.get_err_msg:
|
||||
caller.msg(obj.db.get_err_msg)
|
||||
else:
|
||||
|
|
@ -285,6 +288,7 @@ class CmdDrop(MuxCommand):
|
|||
"""
|
||||
|
||||
key = "drop"
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"Implement command"
|
||||
|
|
@ -322,14 +326,15 @@ class CmdQuit(MuxCommand):
|
|||
Gracefully disconnect from the game.
|
||||
"""
|
||||
key = "@quit"
|
||||
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"hook function"
|
||||
sessions = self.caller.sessions
|
||||
for session in sessions:
|
||||
session.msg("Quitting. Hope to see you soon again.")
|
||||
session.at_disconnect()
|
||||
|
||||
session.session_disconnect()
|
||||
|
||||
class CmdWho(MuxCommand):
|
||||
"""
|
||||
who
|
||||
|
|
@ -357,7 +362,7 @@ class CmdWho(MuxCommand):
|
|||
if self.cmdstring == "doing":
|
||||
show_session_data = False
|
||||
else:
|
||||
show_session_data = has_perm_string(caller, "Immortals,Wizards")
|
||||
show_session_data = caller.check_permstring("Immortals") or caller.check_permstring("Wizards")
|
||||
|
||||
if show_session_data:
|
||||
table = [["Player Name"], ["On for"], ["Idle"], ["Room"], ["Cmds"], ["Host"]]
|
||||
|
|
@ -411,6 +416,7 @@ class CmdSay(MuxCommand):
|
|||
|
||||
key = "say"
|
||||
aliases = ['"']
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"Run the say command"
|
||||
|
|
@ -505,6 +511,7 @@ class CmdPose(MuxCommand):
|
|||
"""
|
||||
key = "pose"
|
||||
aliases = [":", "emote"]
|
||||
locks = "cmd:all()"
|
||||
|
||||
def parse(self):
|
||||
"""
|
||||
|
|
@ -608,6 +615,7 @@ class CmdEncoding(MuxCommand):
|
|||
|
||||
key = "@encoding"
|
||||
aliases = "@encode"
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
|
|
@ -640,39 +648,31 @@ class CmdEncoding(MuxCommand):
|
|||
string = "Your custom text encoding was changed from '%s' to '%s'." % (old_encoding, encoding)
|
||||
caller.msg(string)
|
||||
|
||||
class CmdGroup(MuxCommand):
|
||||
class CmdAccess(MuxCommand):
|
||||
"""
|
||||
group - show your groups
|
||||
access - show access groups
|
||||
|
||||
Usage:
|
||||
group
|
||||
access
|
||||
|
||||
This command shows you which user permission groups
|
||||
you are a member of, if any.
|
||||
This command shows you the permission hierarchy and
|
||||
which permission groups you are a member of.
|
||||
"""
|
||||
key = "access"
|
||||
aliases = "groups"
|
||||
aliases = ["groups", "hierarchy"]
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"Load the permission groups"
|
||||
|
||||
caller = self.caller
|
||||
|
||||
string = ""
|
||||
if caller.player and caller.player.is_superuser:
|
||||
string += "\n This is a SUPERUSER account! Group membership does not matter."
|
||||
else:
|
||||
# get permissions and determine if they are groups
|
||||
perms = list(set(caller.permissions + caller.player.permissions))
|
||||
|
||||
for group in [group for group in PermissionGroup.objects.all()
|
||||
if group.key in perms]:
|
||||
string += "\n {w%s{n\n%s" % (group.key, ", ".join(group.group_permissions))
|
||||
if string:
|
||||
string = "\nGroup memberships for you (Player %s + Character %s): %s" % (caller.player.name,
|
||||
caller.name, string)
|
||||
else:
|
||||
string = "\nYou are not not a member of any groups."
|
||||
hierarchy_full = settings.PERMISSION_HIERARCHY
|
||||
string = "\n{wPermission Hierarchy{n (climbing):\n %s" % ", ".join(hierarchy_full)
|
||||
hierarchy = [p.lower() for p in hierarchy_full]
|
||||
string += "\n{wYour access{n:"
|
||||
string += "\nCharacter %s: %s" % (caller.key, ", ".join(caller.permissions))
|
||||
if hasattr(caller, 'player'):
|
||||
string += "\nPlayer %s: %s" % (caller.player.key, ", ".join(caller.player.permissions))
|
||||
caller.msg(string)
|
||||
|
||||
## def cmd_apropos(command):
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ creation of other help topics such as RP help or game-world aides.
|
|||
from src.utils.utils import fill, dedent
|
||||
from src.commands.command import Command
|
||||
from src.help.models import HelpEntry
|
||||
from src.permissions.permissions import has_perm
|
||||
from src.utils import create
|
||||
from src.commands.default.muxcommand import MuxCommand
|
||||
|
||||
|
|
@ -65,6 +64,8 @@ class CmdHelp(Command):
|
|||
topics related to the game.
|
||||
"""
|
||||
key = "help"
|
||||
locks = "cmd:all()"
|
||||
|
||||
# this is a special cmdhandler flag that makes the cmdhandler also pack
|
||||
# the current cmdset with the call to self.func().
|
||||
return_cmdset = True
|
||||
|
|
@ -73,6 +74,7 @@ class CmdHelp(Command):
|
|||
"""
|
||||
inp is a string containing the command or topic match.
|
||||
"""
|
||||
self.original_args = self.args.strip()
|
||||
self.args = self.args.strip().lower()
|
||||
|
||||
def func(self):
|
||||
|
|
@ -94,7 +96,7 @@ class CmdHelp(Command):
|
|||
if query in LIST_ARGS:
|
||||
# we want to list all available help entries
|
||||
hdict_cmd = {}
|
||||
for cmd in (cmd for cmd in cmdset if has_perm(caller, cmd, 'cmd')
|
||||
for cmd in (cmd for cmd in cmdset if cmd.access(caller)
|
||||
if not cmd.key.startswith('__')
|
||||
and not (hasattr(cmd, 'is_exit') and cmd.is_exit)):
|
||||
if hdict_cmd.has_key(cmd.help_category):
|
||||
|
|
@ -103,7 +105,7 @@ class CmdHelp(Command):
|
|||
hdict_cmd[cmd.help_category] = [cmd.key]
|
||||
hdict_db = {}
|
||||
for topic in (topic for topic in HelpEntry.objects.get_all_topics()
|
||||
if has_perm(caller, topic, 'view')):
|
||||
if topic.access(caller, 'view', default=True)):
|
||||
if hdict_db.has_key(topic.help_category):
|
||||
hdict_db[topic.help_category].append(topic.key)
|
||||
else:
|
||||
|
|
@ -116,7 +118,7 @@ class CmdHelp(Command):
|
|||
|
||||
# Cmd auto-help dynamic entries
|
||||
cmdmatches = [cmd for cmd in cmdset
|
||||
if query in cmd and has_perm(caller, cmd, 'cmd')]
|
||||
if query in cmd and cmd.access(caller)]
|
||||
if len(cmdmatches) > 1:
|
||||
# multiple matches. Try to limit it down to exact match
|
||||
exactmatches = [cmd for cmd in cmdmatches if cmd == query]
|
||||
|
|
@ -127,12 +129,12 @@ class CmdHelp(Command):
|
|||
dbmatches = \
|
||||
[topic for topic in
|
||||
HelpEntry.objects.find_topicmatch(query, exact=False)
|
||||
if has_perm(caller, topic, 'view')]
|
||||
if topic.access(caller, 'view', default=True)]
|
||||
if len(dbmatches) > 1:
|
||||
exactmatches = \
|
||||
[topic for topic in
|
||||
HelpEntry.objects.find_topicmatch(query, exact=True)
|
||||
if has_perm(caller, topic, 'view')]
|
||||
if topic.access(caller, 'view', default=True)]
|
||||
if exactmatches:
|
||||
dbmatches = exactmatches
|
||||
|
||||
|
|
@ -140,11 +142,11 @@ class CmdHelp(Command):
|
|||
if (not cmdmatches) and (not dbmatches):
|
||||
# no normal match. Check if this is a category match instead
|
||||
categ_cmdmatches = [cmd.key for cmd in cmdset
|
||||
if query == cmd.help_category and has_perm(caller, cmd, 'cmd')]
|
||||
if query == cmd.help_category and cmd.access(caller)]
|
||||
categ_dbmatches = \
|
||||
[topic.key for topic in
|
||||
HelpEntry.objects.find_topics_with_category(query)
|
||||
if has_perm(caller, topic, 'view')]
|
||||
if topic.access(caller, 'view', default=True)]
|
||||
cmddict = None
|
||||
dbdict = None
|
||||
if categ_cmdmatches:
|
||||
|
|
@ -154,7 +156,7 @@ class CmdHelp(Command):
|
|||
if cmddict or dbdict:
|
||||
help_entry = format_help_list(cmddict, dbdict)
|
||||
else:
|
||||
help_entry = "No help entry found for '%s'" % query
|
||||
help_entry = "No help entry found for '%s'" % self.original_args
|
||||
|
||||
elif len(cmdmatches) == 1:
|
||||
# we matched against a command name or alias. Show its help entry.
|
||||
|
|
@ -184,7 +186,7 @@ class CmdSetHelp(MuxCommand):
|
|||
@help - edit the help database
|
||||
|
||||
Usage:
|
||||
@help[/switches] <topic>[,category[,permission,permission,...]] = <text>
|
||||
@help[/switches] <topic>[,category[,locks]] = <text>
|
||||
|
||||
Switches:
|
||||
add - add or replace a new topic with text.
|
||||
|
|
@ -203,7 +205,7 @@ class CmdSetHelp(MuxCommand):
|
|||
"""
|
||||
key = "@help"
|
||||
aliases = "@sethelp"
|
||||
permissions = "cmd:sethelp"
|
||||
locks = "cmd:perm(PlayerHelpers)"
|
||||
help_category = "Building"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -214,36 +216,33 @@ class CmdSetHelp(MuxCommand):
|
|||
lhslist = self.lhslist
|
||||
rhs = self.rhs
|
||||
|
||||
if not self.rhs:
|
||||
caller.msg("Usage: @sethelp/[add|del|append|merge] <topic>[,category[,permission,..] = <text>]")
|
||||
if not self.args:
|
||||
caller.msg("Usage: @sethelp/[add|del|append|merge] <topic>[,category[,locks,..] = <text>]")
|
||||
return
|
||||
|
||||
topicstr = ""
|
||||
category = ""
|
||||
permissions = ""
|
||||
lockstring = ""
|
||||
try:
|
||||
topicstr = lhslist[0]
|
||||
category = lhslist[1]
|
||||
permissions = ",".join(lhslist[2:])
|
||||
lockstring = ",".join(lhslist[2:])
|
||||
except Exception:
|
||||
pass
|
||||
if not topicstr:
|
||||
caller.msg("You have to define a topic!")
|
||||
return
|
||||
string = ""
|
||||
print topicstr, category, permissions
|
||||
#print topicstr, category, lockstring
|
||||
|
||||
if switches and switches[0] in ('append', 'app','merge'):
|
||||
# add text to the end of a help topic
|
||||
# find the topic to append to
|
||||
old_entry = None
|
||||
try:
|
||||
old_entry = HelpEntry.objects.get(key=topicstr)
|
||||
except Exception:
|
||||
pass
|
||||
old_entry = HelpEntry.objects.filter(db_key__iexact=topicstr)
|
||||
if not old_entry:
|
||||
string = "Could not find topic '%s'. You must give an exact name." % topicstr
|
||||
else:
|
||||
old_entry = old_entry[0]
|
||||
entrytext = old_entry.entrytext
|
||||
if switches[0] == 'merge':
|
||||
old_entry.entrytext = "%s %s" % (entrytext, self.rhs)
|
||||
|
|
@ -255,15 +254,11 @@ class CmdSetHelp(MuxCommand):
|
|||
|
||||
elif switches and switches[0] in ('delete','del'):
|
||||
#delete a help entry
|
||||
old_entry = None
|
||||
try:
|
||||
old_entry = HelpEntry.objects.get(key=topicstr)
|
||||
except Exception:
|
||||
pass
|
||||
old_entry = HelpEntry.objects.filter(db_key__iexact=topicstr)
|
||||
if not old_entry:
|
||||
string = "Could not find topic. You must give an exact name."
|
||||
string = "Could not find topic '%s'." % topicstr
|
||||
else:
|
||||
old_entry.delete()
|
||||
old_entry[0].delete()
|
||||
string = "Deleted the help entry '%s'." % topicstr
|
||||
|
||||
else:
|
||||
|
|
@ -279,7 +274,8 @@ class CmdSetHelp(MuxCommand):
|
|||
old_entry.key = topicstr
|
||||
old_entry.entrytext = self.rhs
|
||||
old_entry.help_category = category
|
||||
old_entry.permissions = permissions
|
||||
old_entry.locks.clear()
|
||||
old_entry.locks.add(lockstring)
|
||||
old_entry.save()
|
||||
string = "Overwrote the old topic '%s' with a new one." % topicstr
|
||||
else:
|
||||
|
|
@ -287,7 +283,8 @@ class CmdSetHelp(MuxCommand):
|
|||
else:
|
||||
# no old entry. Create a new one.
|
||||
new_entry = create.create_help_entry(topicstr,
|
||||
rhs, category, permissions)
|
||||
rhs, category, lockstring)
|
||||
|
||||
if new_entry:
|
||||
string = "Topic '%s' was successfully created." % topicstr
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ the line is just added to the editor buffer).
|
|||
|
||||
from src.comms.models import Channel
|
||||
from src.utils import create
|
||||
from src.permissions.permissions import has_perm
|
||||
|
||||
# The command keys the engine is calling
|
||||
# (the actual names all start with __)
|
||||
|
|
@ -41,6 +40,7 @@ class SystemNoInput(MuxCommand):
|
|||
This is called when there is no input given
|
||||
"""
|
||||
key = CMD_NOINPUT
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"Do nothing."
|
||||
|
|
@ -56,7 +56,8 @@ class SystemNoMatch(MuxCommand):
|
|||
No command was found matching the given input.
|
||||
"""
|
||||
key = CMD_NOMATCH
|
||||
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
This is given the failed raw string as input.
|
||||
|
|
@ -79,6 +80,7 @@ class SystemMultimatch(MuxCommand):
|
|||
and cmd is an an instantiated Command object matching the candidate.
|
||||
"""
|
||||
key = CMD_MULTIMATCH
|
||||
locks = "cmd:all()"
|
||||
|
||||
def format_multimatches(self, caller, matches):
|
||||
"""
|
||||
|
|
@ -131,6 +133,7 @@ class SystemNoPerm(MuxCommand):
|
|||
correct permissions to use a particular command.
|
||||
"""
|
||||
key = CMD_NOPERM
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
|
|
@ -154,7 +157,7 @@ class SystemSendToChannel(MuxCommand):
|
|||
"""
|
||||
|
||||
key = CMD_CHANNEL
|
||||
permissions = "cmd:use_channels"
|
||||
locks = "cmd:all()"
|
||||
|
||||
def parse(self):
|
||||
channelname, msg = self.args.split(':', 1)
|
||||
|
|
@ -178,7 +181,7 @@ class SystemSendToChannel(MuxCommand):
|
|||
string = "You are not connected to channel '%s'."
|
||||
caller.msg(string % channelkey)
|
||||
return
|
||||
if not has_perm(caller, channel, 'chan_send'):
|
||||
if not channel.access(caller, 'send'):
|
||||
string = "You are not permitted to send to channel '%s'."
|
||||
caller.msg(string % channelkey)
|
||||
return
|
||||
|
|
@ -199,6 +202,7 @@ class SystemUseExit(MuxCommand):
|
|||
as a command. It receives the raw string as input.
|
||||
"""
|
||||
key = CMD_EXIT
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
|
|
@ -214,7 +218,7 @@ class SystemUseExit(MuxCommand):
|
|||
destination = exi.attr('_destination')
|
||||
if not destination:
|
||||
return
|
||||
if has_perm(caller, exit, 'traverse'):
|
||||
if exit.access(caller, 'traverse'):
|
||||
caller.move_to(destination)
|
||||
else:
|
||||
caller.msg("You cannot enter")
|
||||
|
|
|
|||
|
|
@ -9,9 +9,11 @@ import os, datetime
|
|||
import django, twisted
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.conf import settings
|
||||
from src.server.sessionhandler import SESSIONS
|
||||
from src.scripts.models import ScriptDB
|
||||
from src.objects.models import ObjectDB
|
||||
from src.players.models import PlayerDB
|
||||
from src.config.models import ConfigValue
|
||||
from src.utils import reloads, create, logger, utils, gametime
|
||||
from src.commands.default.muxcommand import MuxCommand
|
||||
|
|
@ -28,7 +30,7 @@ class CmdReload(MuxCommand):
|
|||
re-validates all scripts.
|
||||
"""
|
||||
key = "@reload"
|
||||
permissions = "cmd:reload"
|
||||
locks = "cmd:perm(reload) or perm(Immortals)"
|
||||
help_category = "System"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -84,7 +86,7 @@ class CmdPy(MuxCommand):
|
|||
"""
|
||||
key = "@py"
|
||||
aliases = ["!"]
|
||||
permissions = "cmd:py"
|
||||
locks = "cmd:perm(py) or perm(Immortals)"
|
||||
help_category = "System"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -130,7 +132,7 @@ class CmdPy(MuxCommand):
|
|||
obj.delete()
|
||||
script.delete()
|
||||
|
||||
class CmdListScripts(MuxCommand):
|
||||
class CmdScripts(MuxCommand):
|
||||
"""
|
||||
Operate on scripts.
|
||||
|
||||
|
|
@ -149,7 +151,7 @@ class CmdListScripts(MuxCommand):
|
|||
"""
|
||||
key = "@scripts"
|
||||
aliases = "@listscripts"
|
||||
permissions = "cmd:listscripts"
|
||||
locks = "cmd:perm(listscripts) or perm(Wizards)"
|
||||
help_category = "System"
|
||||
|
||||
def format_script_list(self, scripts):
|
||||
|
|
@ -255,7 +257,7 @@ class CmdListScripts(MuxCommand):
|
|||
|
||||
|
||||
|
||||
class CmdListObjects(MuxCommand):
|
||||
class CmdObjects(MuxCommand):
|
||||
"""
|
||||
Give a summary of object types in database
|
||||
|
||||
|
|
@ -267,8 +269,8 @@ class CmdListObjects(MuxCommand):
|
|||
given, <nr> defaults to 10.
|
||||
"""
|
||||
key = "@objects"
|
||||
aliases = ["@listobjects", "@listobjs"]
|
||||
permissions = "cmd:listobjects"
|
||||
aliases = ["@listobjects", "@listobjs", '@stats', '@db']
|
||||
locks = "cmd:perm(listobjects) or perm(Builders)"
|
||||
help_category = "System"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -280,9 +282,24 @@ class CmdListObjects(MuxCommand):
|
|||
nlim = int(self.args)
|
||||
else:
|
||||
nlim = 10
|
||||
|
||||
string = "\n{wDatabase totals:{n"
|
||||
|
||||
nplayers = PlayerDB.objects.count()
|
||||
nobjs = ObjectDB.objects.count()
|
||||
base_typeclass = settings.BASE_CHARACTER_TYPECLASS
|
||||
nchars = ObjectDB.objects.filter(db_typeclass_path=base_typeclass).count()
|
||||
nrooms = ObjectDB.objects.filter(db_location=None).exclude(db_typeclass_path=base_typeclass).count()
|
||||
nexits = sum([1 for obj in ObjectDB.objects.filter(db_location=None) if obj.get_attribute('_destination')])
|
||||
|
||||
string += "\n{wPlayers:{n %i" % nplayers
|
||||
string += "\n{wObjects:{n %i" % nobjs
|
||||
string += "\n{w Characters (base type):{n %i" % nchars
|
||||
string += "\n{w Rooms (location==None):{n %i" % nrooms
|
||||
string += "\n{w Exits (.db._destination!=None):{n %i" % nexits
|
||||
string += "\n{w Other:{n %i\n" % (nobjs - nchars - nrooms - nexits)
|
||||
|
||||
dbtotals = ObjectDB.objects.object_totals()
|
||||
#print dbtotals
|
||||
string = "\n{wDatase Object totals:{n"
|
||||
table = [["Count"], ["Typeclass"]]
|
||||
for path, count in dbtotals.items():
|
||||
table[0].append(count)
|
||||
|
|
@ -332,7 +349,7 @@ class CmdService(MuxCommand):
|
|||
"""
|
||||
|
||||
key = "@service"
|
||||
permissions = "cmd:service"
|
||||
locks = "cmd:perm(service) or perm(Immortals)"
|
||||
help_category = "System"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -351,7 +368,7 @@ class CmdService(MuxCommand):
|
|||
sessions = caller.sessions
|
||||
if not sessions:
|
||||
return
|
||||
service_collection = sessions[0].server.service_collection
|
||||
service_collection = SESSIONS.server.services
|
||||
|
||||
if switch == "list":
|
||||
# Just display the list of installed services and their
|
||||
|
|
@ -417,7 +434,7 @@ class CmdShutdown(MuxCommand):
|
|||
Shut the game server down gracefully.
|
||||
"""
|
||||
key = "@shutdown"
|
||||
permissions = "cmd:shutdown"
|
||||
locks = "cmd:perm(shutdown) or perm(Immortals)"
|
||||
help_category = "System"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -468,159 +485,124 @@ class CmdTime(MuxCommand):
|
|||
"""
|
||||
key = "@time"
|
||||
aliases = "@uptime"
|
||||
permissions = "cmd:time"
|
||||
locks = "cmd:perm(time) or perm(Players)"
|
||||
help_category = "System"
|
||||
|
||||
def func(self):
|
||||
"Show times."
|
||||
|
||||
string1 = "\nCurrent server uptime: \t"
|
||||
string1 += "{w%s{n" % (utils.time_format(gametime.uptime(format=False), 2))
|
||||
|
||||
string2 = "\nTotal server running time: \t"
|
||||
string2 += "{w%s{n" % (utils.time_format(gametime.runtime(format=False), 2))
|
||||
|
||||
string3 = "\nTotal in-game time (realtime x %g):\t" % (gametime.TIMEFACTOR)
|
||||
string3 += "{w%s{n" % (utils.time_format(gametime.gametime(format=False), 2))
|
||||
|
||||
string4 = "\nServer time stamp: \t"
|
||||
string4 += "{w%s{n" % (str(datetime.datetime.now()))
|
||||
string5 = ""
|
||||
if not utils.host_os_is('nt'):
|
||||
# os.getloadavg() is not available on Windows.
|
||||
table = [["Current server uptime:",
|
||||
"Total server running time:",
|
||||
"Total in-game time (realtime x %g):" % (gametime.TIMEFACTOR),
|
||||
"Server time stamp:"
|
||||
],
|
||||
[utils.time_format(gametime.uptime(format=False), 2),
|
||||
utils.time_format(gametime.runtime(format=False), 2),
|
||||
utils.time_format(gametime.gametime(format=False), 2),
|
||||
datetime.datetime.now()
|
||||
]]
|
||||
if utils.host_os_is('posix'):
|
||||
loadavg = os.getloadavg()
|
||||
string5 += "\nServer load (per minute): \t"
|
||||
string5 += "{w%g%%{n" % (100 * loadavg[0])
|
||||
string = "%s%s%s%s%s" % (string1, string2, string3, string4, string5)
|
||||
table[0].append("Server load (per minute):")
|
||||
table[1].append("{w%g%%{n" % (100 * loadavg[0]))
|
||||
stable = []
|
||||
for col in table:
|
||||
stable.append([str(val).strip() for val in col])
|
||||
ftable = utils.format_table(stable, 5)
|
||||
string = ""
|
||||
for row in ftable:
|
||||
string += "\n " + "{w%s{n" % row[0] + "".join(row[1:])
|
||||
self.caller.msg(string)
|
||||
|
||||
class CmdList(MuxCommand):
|
||||
class CmdServerLoad(MuxCommand):
|
||||
"""
|
||||
@list - list info
|
||||
server load statistics
|
||||
|
||||
Usage:
|
||||
@list <option>
|
||||
@serverload
|
||||
|
||||
Options:
|
||||
process - list processes
|
||||
objects - list objects
|
||||
scripts - list scripts
|
||||
perms - list permission keys and groups
|
||||
|
||||
Shows game related information depending
|
||||
on which argument is given.
|
||||
Show server load statistics in a table.
|
||||
"""
|
||||
key = "@list"
|
||||
permissions = "cmd:list"
|
||||
key = "@serverload"
|
||||
locks = "cmd:perm(list) or perm(Immortals)"
|
||||
help_category = "System"
|
||||
|
||||
def func(self):
|
||||
"Show list."
|
||||
|
||||
caller = self.caller
|
||||
if not self.args:
|
||||
caller.msg("Usage: @list process|objects|scripts|perms")
|
||||
return
|
||||
|
||||
string = ""
|
||||
if self.arglist[0] in ["proc","process"]:
|
||||
# display active processes
|
||||
|
||||
# display active processes
|
||||
|
||||
if utils.host_os_is('nt'):
|
||||
string = "Feature not available on Windows."
|
||||
else:
|
||||
import resource
|
||||
loadavg = os.getloadavg()
|
||||
psize = resource.getpagesize()
|
||||
rusage = resource.getrusage(resource.RUSAGE_SELF)
|
||||
table = [["Server load (1 min):",
|
||||
"Process ID:",
|
||||
"Bytes per page:",
|
||||
"Time used:",
|
||||
"Integral memory:",
|
||||
"Max res memory:",
|
||||
"Page faults:",
|
||||
"Disk I/O:",
|
||||
"Network I/O",
|
||||
"Context switching:"
|
||||
],
|
||||
["%g%%" % (100 * loadavg[0]),
|
||||
"%10d" % os.getpid(),
|
||||
"%10d " % psize,
|
||||
"%10d" % rusage[0],
|
||||
"%10d shared" % rusage[3],
|
||||
"%10d pages" % rusage[2],
|
||||
"%10d hard" % rusage[7],
|
||||
"%10d reads" % rusage[9],
|
||||
"%10d in" % rusage[12],
|
||||
"%10d vol" % rusage[14]
|
||||
],
|
||||
["", "", "",
|
||||
"(user: %g)" % rusage[1],
|
||||
"%10d private" % rusage[4],
|
||||
"%10d bytes" % (rusage[2] * psize),
|
||||
"%10d soft" % rusage[6],
|
||||
"%10d writes" % rusage[10],
|
||||
"%10d out" % rusage[11],
|
||||
"%10d forced" % rusage[15]
|
||||
],
|
||||
["", "", "", "",
|
||||
"%10d stack" % rusage[5],
|
||||
"",
|
||||
"%10d swapouts" % rusage[8],
|
||||
"", "",
|
||||
"%10d sigs" % rusage[13]
|
||||
]
|
||||
]
|
||||
stable = []
|
||||
for col in table:
|
||||
stable.append([str(val).strip() for val in col])
|
||||
ftable = utils.format_table(stable, 5)
|
||||
string = ""
|
||||
for row in ftable:
|
||||
string += "\n " + "{w%s{n" % row[0] + "".join(row[1:])
|
||||
|
||||
# string = "\n Server load (1 min) : %.2f " % loadavg[0]
|
||||
# string += "\n Process ID: %10d" % os.getpid()
|
||||
# string += "\n Bytes per page: %10d" % psize
|
||||
# string += "\n Time used: %10d, user: %g" % (rusage[0], rusage[1])
|
||||
# string += "\n Integral mem: %10d shared, %10d, private, %10d stack " % \
|
||||
# (rusage[3], rusage[4], rusage[5])
|
||||
# string += "\n Max res mem: %10d pages %10d bytes" % \
|
||||
# (rusage[2],rusage[2] * psize)
|
||||
# string += "\n Page faults: %10d hard %10d soft %10d swapouts " % \
|
||||
# (rusage[7], rusage[6], rusage[8])
|
||||
# string += "\n Disk I/O: %10d reads %10d writes " % \
|
||||
# (rusage[9], rusage[10])
|
||||
# string += "\n Network I/O: %10d in %10d out " % \
|
||||
# (rusage[12], rusage[11])
|
||||
# string += "\n Context swi: %10d vol %10d forced %10d sigs " % \
|
||||
# (rusage[14], rusage[15], rusage[13])
|
||||
|
||||
elif self.arglist[0] in ["obj", "objects"]:
|
||||
caller.execute_cmd("@objects")
|
||||
elif self.arglist[0] in ["scr", "scripts"]:
|
||||
caller.execute_cmd("@scripts")
|
||||
elif self.arglist[0] in ["perm", "perms","permissions"]:
|
||||
caller.execute_cmd("@perm/list")
|
||||
if not utils.host_os_is('posix'):
|
||||
string = "Process listings are only available under Linux/Unix."
|
||||
else:
|
||||
string = "'%s' is not a valid option." % self.arglist[0]
|
||||
# send info
|
||||
import resource
|
||||
loadavg = os.getloadavg()
|
||||
psize = resource.getpagesize()
|
||||
rusage = resource.getrusage(resource.RUSAGE_SELF)
|
||||
table = [["Server load (1 min):",
|
||||
"Process ID:",
|
||||
"Bytes per page:",
|
||||
"Time used:",
|
||||
"Integral memory:",
|
||||
"Max res memory:",
|
||||
"Page faults:",
|
||||
"Disk I/O:",
|
||||
"Network I/O",
|
||||
"Context switching:"
|
||||
],
|
||||
["%g%%" % (100 * loadavg[0]),
|
||||
"%10d" % os.getpid(),
|
||||
"%10d " % psize,
|
||||
"%10d" % rusage[0],
|
||||
"%10d shared" % rusage[3],
|
||||
"%10d pages" % rusage[2],
|
||||
"%10d hard" % rusage[7],
|
||||
"%10d reads" % rusage[9],
|
||||
"%10d in" % rusage[12],
|
||||
"%10d vol" % rusage[14]
|
||||
],
|
||||
["", "", "",
|
||||
"(user: %g)" % rusage[1],
|
||||
"%10d private" % rusage[4],
|
||||
"%10d bytes" % (rusage[2] * psize),
|
||||
"%10d soft" % rusage[6],
|
||||
"%10d writes" % rusage[10],
|
||||
"%10d out" % rusage[11],
|
||||
"%10d forced" % rusage[15]
|
||||
],
|
||||
["", "", "", "",
|
||||
"%10d stack" % rusage[5],
|
||||
"",
|
||||
"%10d swapouts" % rusage[8],
|
||||
"", "",
|
||||
"%10d sigs" % rusage[13]
|
||||
]
|
||||
]
|
||||
stable = []
|
||||
for col in table:
|
||||
stable.append([str(val).strip() for val in col])
|
||||
ftable = utils.format_table(stable, 5)
|
||||
string = ""
|
||||
for row in ftable:
|
||||
string += "\n " + "{w%s{n" % row[0] + "".join(row[1:])
|
||||
|
||||
caller.msg(string)
|
||||
|
||||
|
||||
#TODO - expand @ps as we add irc/imc2 support.
|
||||
class CmdPs(MuxCommand):
|
||||
"""
|
||||
@ps - list processes
|
||||
list processes
|
||||
|
||||
Usage
|
||||
@ps
|
||||
|
||||
Shows the process/event table.
|
||||
"""
|
||||
key = "@ps"
|
||||
permissions = "cmd:ps"
|
||||
locks = "cmd:perm(ps) or perm(Builders)"
|
||||
help_category = "System"
|
||||
|
||||
def func(self):
|
||||
|
|
@ -651,36 +633,4 @@ class CmdPs(MuxCommand):
|
|||
string += "\n{wTotal{n: %d scripts." % len(all_scripts)
|
||||
self.caller.msg(string)
|
||||
|
||||
class CmdStats(MuxCommand):
|
||||
"""
|
||||
@stats - show object stats
|
||||
|
||||
Usage:
|
||||
@stats
|
||||
|
||||
Shows stats about the database.
|
||||
"""
|
||||
|
||||
key = "@stats"
|
||||
aliases = "@db"
|
||||
permissions = "cmd:stats"
|
||||
help_category = "System"
|
||||
|
||||
def func(self):
|
||||
"Show all stats"
|
||||
|
||||
# get counts for all typeclasses
|
||||
stats_dict = ObjectDB.objects.object_totals()
|
||||
# get all objects
|
||||
stats_allobj = ObjectDB.objects.all().count()
|
||||
# get all rooms
|
||||
stats_room = ObjectDB.objects.filter(db_location=None).count()
|
||||
# get all players
|
||||
stats_users = User.objects.all().count()
|
||||
|
||||
string = "\n{wNumber of users:{n %i" % stats_users
|
||||
string += "\n{wTotal number of objects:{n %i" % stats_allobj
|
||||
string += "\n{wNumber of rooms (location==None):{n %i" % stats_room
|
||||
string += "\n (Use @objects for detailed info)"
|
||||
self.caller.msg(string)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@ try:
|
|||
except ImportError:
|
||||
from django.test import TestCase
|
||||
from django.conf import settings
|
||||
from src.utils import create
|
||||
from src.utils import create, ansi
|
||||
from src.server import session, sessionhandler
|
||||
from src.locks.lockhandler import LockHandler
|
||||
from src.config.models import ConfigValue
|
||||
|
||||
#------------------------------------------------------------
|
||||
|
|
@ -29,6 +30,7 @@ from src.config.models import ConfigValue
|
|||
|
||||
# print all feedback from test commands (can become very verbose!)
|
||||
VERBOSE = False
|
||||
NOMANGLE = False
|
||||
|
||||
class FakeSession(session.Session):
|
||||
"""
|
||||
|
|
@ -55,8 +57,13 @@ class FakeSession(session.Session):
|
|||
else:
|
||||
rstring = return_list
|
||||
self.player.character.ndb.return_string = None
|
||||
if not message.startswith(rstring):
|
||||
retval = "Returned message ('%s') != desired message ('%s')" % (message, rstring)
|
||||
message_noansi = ansi.parse_ansi(message, strip_ansi=True).strip()
|
||||
rstring = rstring.strip()
|
||||
if not message_noansi.startswith(rstring):
|
||||
sep1 = "\n" + "="*30 + "Wanted message" + "="*34 + "\n"
|
||||
sep2 = "\n" + "="*30 + "Returned message" + "="*32 + "\n"
|
||||
sep3 = "\n" + "="*78
|
||||
retval = sep1 + rstring + sep2 + message_noansi + sep3
|
||||
raise AssertionError(retval)
|
||||
if VERBOSE:
|
||||
print message
|
||||
|
|
@ -77,17 +84,28 @@ class CommandTest(TestCase):
|
|||
self.room2 = create.create_object(settings.BASE_ROOM_TYPECLASS, key="room2")
|
||||
|
||||
# create a faux player/character for testing.
|
||||
self.char1 = create.create_player("TestingPlayer", "testplayer@test.com", "testpassword", location=self.room1)
|
||||
self.char1 = create.create_player("TestChar", "testplayer@test.com", "testpassword", location=self.room1)
|
||||
self.char1.player.user.is_superuser = True
|
||||
self.char1.lock_storage = ""
|
||||
self.char1.locks = LockHandler(self.char1)
|
||||
self.char1.ndb.return_string = None
|
||||
sess = FakeSession()
|
||||
sess.connectionMade()
|
||||
sess.session_login(self.char1.player)
|
||||
# create second player and some objects
|
||||
self.char2 = create.create_object(settings.BASE_CHARACTER_TYPECLASS, key="char2", location=self.room1)
|
||||
# create second player
|
||||
self.char2 = create.create_player("TestChar2", "testplayer2@test.com", "testpassword2", location=self.room1)
|
||||
self.char2.player.user.is_superuser = False
|
||||
self.char2.lock_storage = ""
|
||||
self.char2.locks = LockHandler(self.char2)
|
||||
self.char2.ndb.return_string = None
|
||||
sess2 = FakeSession()
|
||||
sess2.connectionMade()
|
||||
sess2.session_login(self.char2.player)
|
||||
# A non-player-controlled character
|
||||
self.char3 = create.create_object(settings.BASE_CHARACTER_TYPECLASS, key="TestChar3", location=self.room1)
|
||||
# create some objects
|
||||
self.obj1 = create.create_object(settings.BASE_OBJECT_TYPECLASS, key="obj1", location=self.room1)
|
||||
self.obj2 = create.create_object(settings.BASE_OBJECT_TYPECLASS, key="obj2", location=self.room1)
|
||||
self.obj2 = create.create_object(settings.BASE_OBJECT_TYPECLASS, key="obj2", location=self.room1)
|
||||
self.exit1 = create.create_object(settings.BASE_EXIT_TYPECLASS, key="exit1", location=self.room1)
|
||||
self.exit2 = create.create_object(settings.BASE_EXIT_TYPECLASS, key="exit2", location=self.room2)
|
||||
|
||||
|
|
@ -110,7 +128,7 @@ class CommandTest(TestCase):
|
|||
This also mangles the input in various ways to test if the command
|
||||
will be fooled.
|
||||
"""
|
||||
if not VERBOSE:
|
||||
if not VERBOSE and not NOMANGLE:
|
||||
# only mangle if not VERBOSE, to make fewer return lines
|
||||
test1 = re.sub(r'\s', '', raw_string) # remove all whitespace inside it
|
||||
test2 = "%s/åäö öäö;-:$£@*~^' 'test" % raw_string # inserting weird characters in call
|
||||
|
|
@ -129,52 +147,129 @@ class CommandTest(TestCase):
|
|||
# Default set Command testing
|
||||
#------------------------------------------------------------
|
||||
|
||||
# general.py tests
|
||||
|
||||
class TestLook(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("look here")
|
||||
class TestHome(CommandTest):
|
||||
def test_call(self):
|
||||
self.char1.location = self.room1
|
||||
self.char1.home = self.room2
|
||||
self.execute_cmd("home")
|
||||
self.assertEqual(self.char1.location, self.room2)
|
||||
class TestLook(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("look here")
|
||||
class TestPassword(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@password testpassword = newpassword")
|
||||
class TestInventory(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("inv")
|
||||
class TestQuit(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@quit")
|
||||
class TestPose(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("pose is testing","TestChar is testing")
|
||||
class TestNick(CommandTest):
|
||||
def test_call(self):
|
||||
self.char1.player.user.is_superuser = False
|
||||
self.execute_cmd("nickname testalias = testaliasedstring1")
|
||||
self.execute_cmd("nickname/player testalias = testaliasedstring2")
|
||||
self.execute_cmd("nickname/object testalias = testaliasedstring3")
|
||||
self.assertEquals(u"testaliasedstring1", self.char1.nickhandler("testalias"))
|
||||
self.assertEquals(u"testaliasedstring2", self.char1.nickhandler("testalias",nick_type="player"))
|
||||
self.assertEquals(u"testaliasedstring3", self.char1.nickhandler("testalias",nick_type="object"))
|
||||
self.assertEquals(u"testaliasedstring1", self.char1.nicks.get("testalias"))
|
||||
self.assertEquals(u"testaliasedstring2", self.char1.nicks.get("testalias",nick_type="player"))
|
||||
self.assertEquals(u"testaliasedstring3", self.char1.nicks.get("testalias",nick_type="object"))
|
||||
class TestGet(CommandTest):
|
||||
def test_call(self):
|
||||
self.obj1.location = self.room1
|
||||
self.execute_cmd("get obj1", "You pick up obj1.")
|
||||
class TestDrop(CommandTest):
|
||||
def test_call(self):
|
||||
self.obj1.location = self.char1
|
||||
self.execute_cmd("drop obj1", "You drop obj1.")
|
||||
class TestWho(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("who")
|
||||
class TestSay(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("say Hello", 'You say, "Hello')
|
||||
class TestAccess(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("access")
|
||||
class TestEncoding(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@encoding", "Supported encodings")
|
||||
|
||||
# help.py command tests
|
||||
|
||||
class TestHelpSystem(CommandTest):
|
||||
def test_call(self):
|
||||
global NOMANGLE
|
||||
NOMANGLE = True
|
||||
sep = "-"*70 + "\n"
|
||||
self.execute_cmd("@help/add TestTopic,TestCategory = Test1", )
|
||||
self.execute_cmd("help TestTopic",sep + "Help topic for Testtopic\nTest1")
|
||||
self.execute_cmd("@help/merge TestTopic = Test2", "Added the new text right after")
|
||||
self.execute_cmd("help TestTopic", sep + "Help topic for Testtopic\nTest1 Test2")
|
||||
self.execute_cmd("@help/append TestTopic = Test3", "Added the new text as a")
|
||||
self.execute_cmd("help TestTopic",sep + "Help topic for Testtopic\nTest1 Test2\n\nTest3")
|
||||
self.execute_cmd("@help/delete TestTopic","Deleted the help entry")
|
||||
self.execute_cmd("help TestTopic","No help entry found for 'TestTopic'")
|
||||
NOMANGLE = False
|
||||
|
||||
# system.py command tests
|
||||
class TestPy(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@py 1+2", [">>> 1+2", "<<< 3"])
|
||||
class TestListScripts(CommandTest):
|
||||
class TestScripts(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@scripts")
|
||||
class TestListObjects(CommandTest):
|
||||
self.execute_cmd("@scripts", "id")
|
||||
class TestObjects(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@objects")
|
||||
class TestListService(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@service")
|
||||
self.execute_cmd("@objects", "Database totals")
|
||||
# Cannot be tested since we don't have an active server running at this point.
|
||||
# class TestListService(CommandTest):
|
||||
# def test_call(self):
|
||||
# self.execute_cmd("@service/list", "---")
|
||||
class TestVersion(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@version")
|
||||
self.execute_cmd("@version", '---')
|
||||
class TestTime(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@time")
|
||||
class TestList(CommandTest):
|
||||
self.execute_cmd("@time", "Current server uptime")
|
||||
class TestServerLoad(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@list")
|
||||
self.execute_cmd("@serverload", "Server load")
|
||||
class TestPs(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@ps","\n{wNon-timed scripts")
|
||||
class TestStats(CommandTest):
|
||||
self.execute_cmd("@ps","Non-timed scripts")
|
||||
|
||||
# admin.py command tests
|
||||
|
||||
class TestBoot(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@boot TestChar2","You booted TestChar2.")
|
||||
class TestDelPlayer(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@delplayer TestChar2","Booting and informing player ...")
|
||||
class TestEmit(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@stats")
|
||||
self.execute_cmd("@emit Test message", "Emitted to room1.")
|
||||
class TestUserPassword(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@userpassword TestChar2 = newpass", "TestChar2 - new password set to 'newpass'.")
|
||||
class TestPerm(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@perm TestChar2 = Builders", "Permission 'Builders' given to")
|
||||
# cannot test this at the moment, screws up the test suite
|
||||
#class TestPuppet(CommandTest):
|
||||
# def test_call(self):
|
||||
# self.execute_cmd("@puppet TestChar3", "You now control TestChar3.")
|
||||
# self.execute_cmd("@puppet TestChar", "You now control TestChar.")
|
||||
class TestWall(CommandTest):
|
||||
def test_call(self):
|
||||
self.execute_cmd("@wall = This is a test message", "TestChar shouts")
|
||||
|
||||
# building.py command tests
|
||||
|
||||
#TODO
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class CmdConnect(MuxCommand):
|
|||
"""
|
||||
key = "connect"
|
||||
aliases = ["conn", "con", "co"]
|
||||
locks = "cmd:all()" # not really needed
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
|
|
@ -116,6 +117,7 @@ class CmdCreate(MuxCommand):
|
|||
"""
|
||||
key = "create"
|
||||
aliases = ["cre", "cr"]
|
||||
locks = "cmd:all()"
|
||||
|
||||
def parse(self):
|
||||
"""
|
||||
|
|
@ -193,6 +195,9 @@ class CmdCreate(MuxCommand):
|
|||
location=default_home,
|
||||
typeclass=typeclass,
|
||||
home=default_home)
|
||||
# character safety features
|
||||
new_character.locks.delete("get")
|
||||
new_character.locks.add("get:perm(Wizards)")
|
||||
|
||||
# set a default description
|
||||
new_character.db.desc = "This is a Player."
|
||||
|
|
@ -227,6 +232,7 @@ class CmdQuit(MuxCommand):
|
|||
"""
|
||||
key = "quit"
|
||||
aliases = ["q", "qu"]
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"Simply close the connection."
|
||||
|
|
@ -241,6 +247,7 @@ class CmdUnconnectedLook(MuxCommand):
|
|||
"""
|
||||
key = "look"
|
||||
aliases = "l"
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"Show the connect screen."
|
||||
|
|
@ -259,6 +266,7 @@ class CmdUnconnectedHelp(MuxCommand):
|
|||
"""
|
||||
key = "help"
|
||||
aliases = ["h", "?"]
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
"Shows help"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ This defines some test commands for use while testing the MUD and its components
|
|||
from django.conf import settings
|
||||
from django.db import IntegrityError
|
||||
from src.comms.models import Msg
|
||||
from src.permissions import permissions
|
||||
from src.utils import create, debug, utils
|
||||
from src.commands.default.muxcommand import MuxCommand
|
||||
|
||||
|
|
@ -29,7 +28,7 @@ class CmdTest(MuxCommand):
|
|||
key = "@test"
|
||||
aliases = ["@te", "@test all"]
|
||||
help_category = "Utils"
|
||||
permissions = "cmd:Immortals" #Wizards
|
||||
locks = "cmd:perm(Wizards)"
|
||||
|
||||
# the muxcommand class itself handles the display
|
||||
# so we just defer to it by not adding any function.
|
||||
|
|
@ -62,131 +61,6 @@ class CmdTest(MuxCommand):
|
|||
#self.caller.msg("Imported %s" % cmdsetname)
|
||||
#self.caller.msg(cmdsethandler.CACHED_CMDSETS)
|
||||
|
||||
|
||||
|
||||
|
||||
class CmdTestPerms(MuxCommand):
|
||||
"""
|
||||
Test command - test permissions
|
||||
|
||||
Usage:
|
||||
@testperm [[lockstring] [=permstring]]
|
||||
|
||||
With no arguments, runs a sequence of tests for the
|
||||
permission system using the calling player's permissions.
|
||||
|
||||
If <lockstring> is given, match caller's permissions
|
||||
against these locks. If also <permstring> is given,
|
||||
match this against the given locks instead.
|
||||
|
||||
"""
|
||||
key = "@testperm"
|
||||
permissions = "cmd:Immortals Wizards"
|
||||
help_category = "Utils"
|
||||
def func(self):
|
||||
"""
|
||||
Run tests
|
||||
"""
|
||||
caller = self.caller
|
||||
|
||||
if caller.user.is_superuser:
|
||||
caller.msg("You are a superuser. Permission tests are pointless.")
|
||||
return
|
||||
# create a test object
|
||||
obj = create.create_object(None, "accessed_object") # this will use default typeclass
|
||||
obj_id = obj.id
|
||||
caller.msg("obj_attr: %s" % obj.attr("testattr"))
|
||||
|
||||
# perms = ["has_permission", "has permission", "skey:has_permission",
|
||||
# "has_id(%s)" % obj_id, "has_attr(testattr)",
|
||||
# "has_attr(testattr, testattr_value)"]
|
||||
|
||||
# test setting permissions
|
||||
uprofile = caller.user.get_profile()
|
||||
# do testing
|
||||
caller.msg("----------------")
|
||||
|
||||
permissions.set_perm(obj, "has_permission")
|
||||
permissions.add_perm(obj, "skey:has_permission")
|
||||
caller.msg(" keys:[%s] locks:[%s]" % (uprofile.permissions, obj.permissions))
|
||||
caller.msg("normal permtest: %s" % permissions.has_perm(uprofile, obj))
|
||||
caller.msg("skey permtest: %s" % permissions.has_perm(uprofile, obj, 'skey'))
|
||||
|
||||
permissions.set_perm(uprofile, "has_permission")
|
||||
caller.msg(" keys:[%s] locks:[%s]" % (uprofile.permissions, obj.permissions))
|
||||
caller.msg("normal permtest: %s" % permissions.has_perm(uprofile, obj))
|
||||
caller.msg("skey permtest: %s" % permissions.has_perm(uprofile, obj, 'skey'))
|
||||
|
||||
# function tests
|
||||
permissions.set_perm(obj, "has_id(%s)" % (uprofile.id))
|
||||
caller.msg(" keys:[%s] locks:[%s]" % (uprofile.permissions, obj.permissions))
|
||||
caller.msg("functest: %s" % permissions.has_perm(uprofile, obj))
|
||||
|
||||
uprofile.attr("testattr", "testattr_value")
|
||||
permissions.set_perm(obj, "has_attr(testattr, testattr_value)")
|
||||
caller.msg(" keys:[%s] locks:[%s]" % (uprofile.permissions, obj.permissions))
|
||||
caller.msg("functest: %s" % permissions.has_perm(uprofile, obj))
|
||||
|
||||
# cleanup of test permissions
|
||||
permissions.del_perm(uprofile, "has_permission")
|
||||
caller.msg(" cleanup: keys:[%s] locks:[%s]" % (uprofile.permissions, obj.permissions))
|
||||
obj.delete()
|
||||
uprofile.attr("testattr", delete=True)
|
||||
|
||||
|
||||
# # Add/remove states (removed; not valid.)
|
||||
|
||||
# EXAMPLE_STATE="game.gamesrc.commands.examples.example.EXAMPLESTATE"
|
||||
|
||||
# class CmdTestState(MuxCommand):
|
||||
# """
|
||||
# Test command - add a state.
|
||||
|
||||
# Usage:
|
||||
# @teststate[/switch] [<python path to state instance>]
|
||||
# Switches:
|
||||
# add - add a state
|
||||
# clear - remove all added states.
|
||||
# list - view current state stack
|
||||
# reload - reload current state stack
|
||||
|
||||
# If no python path is given, an example state will be added.
|
||||
# You will know it worked if you can use the commands '@testcommand'
|
||||
# and 'smile'.
|
||||
# """
|
||||
|
||||
# key = "@teststate"
|
||||
# alias = "@testingstate"
|
||||
# permissions = "cmd:Immortals Wizards"
|
||||
|
||||
# def func(self):
|
||||
# """
|
||||
# inp is the dict returned from MuxCommand's parser.
|
||||
# """
|
||||
# caller = self.caller
|
||||
# switches = self.switches
|
||||
|
||||
# if not switches or switches[0] not in ["add", "clear", "list", "reload"]:
|
||||
# string = "Usage: @teststate[/add|clear|list|reload] [<python path>]"
|
||||
# caller.msg(string)
|
||||
# elif "clear" in switches:
|
||||
# caller.cmdset.clear()
|
||||
# caller.msg("All cmdset cleared.")
|
||||
# return
|
||||
# elif "list" in switches:
|
||||
# string = "%s" % caller.cmdset
|
||||
# caller.msg(string)
|
||||
# elif "reload" in switches:
|
||||
# caller.cmdset.load()
|
||||
# caller.msg("Cmdset reloaded.")
|
||||
# else: #add
|
||||
# arg = inp["raw"]
|
||||
# if not arg:
|
||||
# arg = EXAMPLE_STATE
|
||||
# caller.cmdset.add(arg)
|
||||
# string = "Added state '%s'." % caller.cmdset.state.key
|
||||
# caller.msg(string)
|
||||
|
||||
class TestCom(MuxCommand):
|
||||
"""
|
||||
Test the command system
|
||||
|
|
@ -195,7 +69,7 @@ class TestCom(MuxCommand):
|
|||
@testcom/create/list [channel]
|
||||
"""
|
||||
key = "@testcom"
|
||||
permissions = "cmd:Immortals Wizards"
|
||||
locks = "cmd:perm(Wizards)"
|
||||
help_category = "Utils"
|
||||
def func(self):
|
||||
"Run the test program"
|
||||
|
|
|
|||
|
|
@ -25,14 +25,14 @@ does this for you.
|
|||
"""
|
||||
from src.comms.models import Channel, Msg
|
||||
from src.commands import cmdset, command
|
||||
from src.permissions.permissions import has_perm
|
||||
from src.utils import utils
|
||||
|
||||
class ChannelCommand(command.Command):
|
||||
"""
|
||||
Channel
|
||||
|
||||
Usage:
|
||||
<channel name or alias> <message>
|
||||
<channel name or alias> <message>
|
||||
|
||||
This is a channel. If you have subscribed to it, you can send to
|
||||
it by entering its name or alias, followed by the text you want to
|
||||
|
|
@ -41,17 +41,17 @@ class ChannelCommand(command.Command):
|
|||
# this flag is what identifies this cmd as a channel cmd
|
||||
# and branches off to the system send-to-channel command
|
||||
# (which is customizable by admin)
|
||||
is_channel = True
|
||||
key = "general"
|
||||
help_category = "Channel Names"
|
||||
permissions = "cmd:use_channels"
|
||||
is_channel = True
|
||||
locks = "cmd:all()"
|
||||
obj = None
|
||||
|
||||
def parse(self):
|
||||
"""
|
||||
Simple parser
|
||||
"""
|
||||
channelname, msg = self.args.split(":", 1)
|
||||
channelname, msg = self.args.split(":", 1) # cmdhandler sends channame:msg here.
|
||||
self.args = (channelname.strip(), msg.strip())
|
||||
|
||||
def func(self):
|
||||
|
|
@ -73,7 +73,7 @@ class ChannelCommand(command.Command):
|
|||
string = "You are not connected to channel '%s'."
|
||||
caller.msg(string % channelkey)
|
||||
return
|
||||
if not has_perm(caller, channel, 'chan_send'):
|
||||
if not channel.access(caller, 'send'):
|
||||
string = "You are not permitted to send to channel '%s'."
|
||||
caller.msg(string % channelkey)
|
||||
return
|
||||
|
|
@ -102,6 +102,25 @@ class ChannelHandler(object):
|
|||
"""
|
||||
self.cached_channel_cmds = []
|
||||
|
||||
def _format_help(self, channel):
|
||||
"builds a doc string"
|
||||
key = channel.key
|
||||
aliases = channel.aliases
|
||||
if not utils.is_iter(aliases):
|
||||
aliases = [aliases]
|
||||
ustring = "%s <message>" % key.lower() + "".join(["\n %s <message>" % alias.lower() for alias in aliases])
|
||||
desc = channel.desc
|
||||
string = \
|
||||
"""
|
||||
Channel '%s'
|
||||
|
||||
Usage (not including your personal aliases):
|
||||
%s
|
||||
|
||||
%s
|
||||
""" % (key, ustring, desc)
|
||||
return string
|
||||
|
||||
def add_channel(self, channel):
|
||||
"""
|
||||
Add an individual channel to the handler. This should be
|
||||
|
|
@ -113,8 +132,12 @@ class ChannelHandler(object):
|
|||
cmd = ChannelCommand()
|
||||
cmd.key = channel.key.strip().lower()
|
||||
cmd.obj = channel
|
||||
cmd.__doc__= self._format_help(channel)
|
||||
if channel.aliases:
|
||||
cmd.aliases = channel.aliases
|
||||
cmd.lock_storage = "cmd:all();%s" % channel.locks
|
||||
cmd.lockhandler.reset()
|
||||
|
||||
self.cached_channel_cmds.append(cmd)
|
||||
|
||||
def update(self):
|
||||
|
|
@ -133,8 +156,9 @@ class ChannelHandler(object):
|
|||
chan_cmdset.key = '_channelset'
|
||||
chan_cmdset.priority = 10
|
||||
chan_cmdset.duplicates = True
|
||||
for cmd in [cmd for cmd in self.cached_channel_cmds
|
||||
if has_perm(source_object, cmd, 'chan_send')]:
|
||||
|
||||
for cmd in [cmd for cmd in self.cached_channel_cmds
|
||||
if cmd.access(source_object, 'listen')]:
|
||||
chan_cmdset.add(cmd)
|
||||
return chan_cmdset
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class MsgManager(models.Manager):
|
|||
try:
|
||||
idnum = int(idnum)
|
||||
return self.get(id=id)
|
||||
except:
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def get_messages_by_sender(self, player):
|
||||
|
|
@ -259,7 +259,10 @@ class ChannelManager(models.Manager):
|
|||
pass
|
||||
if not channels:
|
||||
# no id match. Search on the key.
|
||||
channels = self.filter(db_key=ostring)
|
||||
channels = self.filter(db_key__iexact=ostring)
|
||||
if not channels:
|
||||
# still no match. Search by alias.
|
||||
channels = [channel for channel in self.all() if ostring.lower in [a.lower for a in channel.aliases]]
|
||||
return channels
|
||||
|
||||
#
|
||||
|
|
|
|||
|
|
@ -16,10 +16,9 @@ be able to delete connections on the fly).
|
|||
|
||||
from django.db import models
|
||||
from src.utils.idmapper.models import SharedMemoryModel
|
||||
from src.players.models import PlayerDB
|
||||
from src.server.sessionhandler import SESSIONS
|
||||
from src.comms import managers
|
||||
from src.permissions.permissions import has_perm
|
||||
from src.locks.lockhandler import LockHandler
|
||||
from src.utils.utils import is_iter
|
||||
from src.utils.utils import dbref as is_dbref
|
||||
|
||||
|
|
@ -81,7 +80,6 @@ class Msg(SharedMemoryModel):
|
|||
permissions - perm strings
|
||||
|
||||
"""
|
||||
from src.players.models import PlayerDB
|
||||
#
|
||||
# Msg database model setup
|
||||
#
|
||||
|
|
@ -90,7 +88,7 @@ class Msg(SharedMemoryModel):
|
|||
# named same as the field, but withtout the db_* prefix.
|
||||
|
||||
# There must always be one sender of the message.
|
||||
db_sender = models.ForeignKey(PlayerDB, related_name='sender_set')
|
||||
db_sender = models.ForeignKey("players.PlayerDB", related_name='sender_set')
|
||||
# The destination objects of this message. Stored as a
|
||||
# comma-separated string of object dbrefs. Can be defined along
|
||||
# with channels below.
|
||||
|
|
@ -103,6 +101,8 @@ class Msg(SharedMemoryModel):
|
|||
# should itself handle eventual headers etc.
|
||||
db_message = models.TextField()
|
||||
db_date_sent = models.DateTimeField(editable=False, auto_now_add=True)
|
||||
# lock storage
|
||||
db_lock_storage = models.TextField(blank=True)
|
||||
# These are settable by senders/receivers/channels respectively.
|
||||
# Stored as a comma-separated string of dbrefs. Can be used by the
|
||||
# game to mask out messages from being visible in the archive (no
|
||||
|
|
@ -110,12 +110,16 @@ class Msg(SharedMemoryModel):
|
|||
db_hide_from_sender = models.BooleanField(default=False)
|
||||
db_hide_from_receivers = models.CharField(max_length=255, null=True, blank=True)
|
||||
db_hide_from_channels = models.CharField(max_length=255, null=True, blank=True)
|
||||
# permission strings, separated by commas
|
||||
db_permissions = models.CharField(max_length=255, blank=True)
|
||||
# Storage of lock strings
|
||||
db_lock_storage = models.TextField(null=True)
|
||||
|
||||
# Database manager
|
||||
objects = managers.MsgManager()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
SharedMemoryModel.__init__(self, *args, **kwargs)
|
||||
self.locks = LockHandler(self)
|
||||
|
||||
class Meta:
|
||||
"Define Django meta options"
|
||||
verbose_name = "Message"
|
||||
|
|
@ -278,29 +282,25 @@ class Msg(SharedMemoryModel):
|
|||
self.save()
|
||||
hide_from_channels = property(hide_from_channels_get, hide_from_channels_set, hide_from_channels_del)
|
||||
|
||||
# permissions property
|
||||
#@property
|
||||
def permissions_get(self):
|
||||
"Getter. Allows for value = self.permissions. Returns a list of permissions."
|
||||
if self.db_permissions:
|
||||
return [perm.strip() for perm in self.db_permissions.split(',')]
|
||||
return []
|
||||
#@permissions.setter
|
||||
def permissions_set(self, value):
|
||||
"Setter. Allows for self.permissions = value. Stores as a comma-separated string."
|
||||
if is_iter(value):
|
||||
value = ",".join([str(val).strip().lower() for val in value])
|
||||
self.db_permissions = value
|
||||
self.save()
|
||||
#@permissions.deleter
|
||||
def permissions_del(self):
|
||||
"Deleter. Allows for del self.permissions"
|
||||
self.db_permissions = ""
|
||||
# lock_storage property (wraps db_lock_storage)
|
||||
#@property
|
||||
def lock_storage_get(self):
|
||||
"Getter. Allows for value = self.lock_storage"
|
||||
return self.db_lock_storage
|
||||
#@nick.setter
|
||||
def lock_storage_set(self, value):
|
||||
"""Saves the lock_storagetodate. This is usually not called directly, but through self.lock()"""
|
||||
self.db_lock_storage = value
|
||||
self.save()
|
||||
permissions = property(permissions_get, permissions_set, permissions_del)
|
||||
#@nick.deleter
|
||||
def lock_storage_del(self):
|
||||
"Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead"""
|
||||
logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self)
|
||||
lock_storage = property(lock_storage_get, lock_storage_set, lock_storage_del)
|
||||
|
||||
|
||||
#
|
||||
# Msg class method
|
||||
# Msg class methods
|
||||
#
|
||||
|
||||
def __str__(self):
|
||||
|
|
@ -313,6 +313,15 @@ class Msg(SharedMemoryModel):
|
|||
return "%s -> %s: %s" % (self.sender.key,
|
||||
", ".join([rec.key for rec in self.receivers]),
|
||||
self.message)
|
||||
def access(self, accessing_obj, access_type='read', default=False):
|
||||
"""
|
||||
Determines if another object has permission to access.
|
||||
accessing_obj - object trying to access this one
|
||||
access_type - type of access sought
|
||||
default - what to return if no lock of access_type was found
|
||||
"""
|
||||
return self.locks.check(accessing_obj, access_type=access_type, default=default)
|
||||
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
|
|
@ -351,12 +360,16 @@ class Channel(SharedMemoryModel):
|
|||
db_aliases = models.CharField(max_length=255)
|
||||
# Whether this channel should remember its past messages
|
||||
db_keep_log = models.BooleanField(default=True)
|
||||
# Permission strings, separated by commas
|
||||
db_permissions = models.CharField(max_length=255, blank=True)
|
||||
# Storage of lock definitions
|
||||
db_lock_storage = models.TextField(blank=True)
|
||||
|
||||
# Database manager
|
||||
objects = managers.ChannelManager()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
SharedMemoryModel.__init__(self, *args, **kwargs)
|
||||
self.locks = LockHandler(self)
|
||||
|
||||
# Wrapper properties to easily set database fields. These are
|
||||
# @property decorators that allows to access these fields using
|
||||
# normal python operations (without having to remember to save()
|
||||
|
|
@ -436,26 +449,21 @@ class Channel(SharedMemoryModel):
|
|||
self.save()
|
||||
keep_log = property(keep_log_get, keep_log_set, keep_log_del)
|
||||
|
||||
# permissions property
|
||||
#@property
|
||||
def permissions_get(self):
|
||||
"Getter. Allows for value = self.permissions. Returns a list of permissions."
|
||||
if self.db_permissions:
|
||||
return [perm.strip() for perm in self.db_permissions.split(',')]
|
||||
return []
|
||||
#@permissions.setter
|
||||
def permissions_set(self, value):
|
||||
"Setter. Allows for self.permissions = value. Stores as a comma-separated string."
|
||||
if is_iter(value):
|
||||
value = ",".join([str(val).strip().lower() for val in value])
|
||||
self.db_permissions = value
|
||||
self.save()
|
||||
#@permissions.deleter
|
||||
def permissions_del(self):
|
||||
"Deleter. Allows for del self.permissions"
|
||||
self.db_permissions = ""
|
||||
# lock_storage property (wraps db_lock_storage)
|
||||
#@property
|
||||
def lock_storage_get(self):
|
||||
"Getter. Allows for value = self.lock_storage"
|
||||
return self.db_lock_storage
|
||||
#@nick.setter
|
||||
def lock_storage_set(self, value):
|
||||
"""Saves the lock_storagetodate. This is usually not called directly, but through self.lock()"""
|
||||
self.db_lock_storage = value
|
||||
self.save()
|
||||
permissions = property(permissions_get, permissions_set, permissions_del)
|
||||
#@nick.deleter
|
||||
def lock_storage_del(self):
|
||||
"Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead"""
|
||||
logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self)
|
||||
lock_storage = property(lock_storage_get, lock_storage_set, lock_storage_del)
|
||||
|
||||
class Meta:
|
||||
"Define Django meta options"
|
||||
|
|
@ -515,7 +523,7 @@ class Channel(SharedMemoryModel):
|
|||
|
||||
def connect_to(self, player):
|
||||
"Connect the user to this channel"
|
||||
if not has_perm(player, self, 'chan_listen'):
|
||||
if not self.access(player, 'listen'):
|
||||
return False
|
||||
conn = ChannelConnection.objects.create_connection(player, self)
|
||||
if conn:
|
||||
|
|
@ -531,7 +539,14 @@ class Channel(SharedMemoryModel):
|
|||
for connection in Channel.objects.get_all_connections(self):
|
||||
connection.delete()
|
||||
super(Channel, self).delete()
|
||||
|
||||
def access(self, accessing_obj, access_type='listen', default=False):
|
||||
"""
|
||||
Determines if another object has permission to access.
|
||||
accessing_obj - object trying to access this one
|
||||
access_type - type of access sought
|
||||
default - what to return if no lock of access_type was found
|
||||
"""
|
||||
return self.locks.check(accessing_obj, access_type=access_type, default=default)
|
||||
|
||||
class ChannelConnection(SharedMemoryModel):
|
||||
"""
|
||||
|
|
@ -539,9 +554,8 @@ class ChannelConnection(SharedMemoryModel):
|
|||
The advantage of making it like this is that one can easily
|
||||
break the connection just by deleting this object.
|
||||
"""
|
||||
from src.players.models import PlayerDB
|
||||
# Player connected to a channel
|
||||
db_player = models.ForeignKey(PlayerDB)
|
||||
db_player = models.ForeignKey("players.PlayerDB")
|
||||
# Channel the player is connected to
|
||||
db_channel = models.ForeignKey(Channel)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from django.db import models
|
|||
from src.utils.idmapper.models import SharedMemoryModel
|
||||
from src.help.manager import HelpEntryManager
|
||||
from src.utils import ansi
|
||||
from src.locks.lockhandler import LockHandler
|
||||
from src.utils.utils import is_iter
|
||||
|
||||
#------------------------------------------------------------
|
||||
|
|
@ -48,13 +49,18 @@ class HelpEntry(SharedMemoryModel):
|
|||
db_entrytext = models.TextField(blank=True)
|
||||
# a string of permissionstrings, separated by commas.
|
||||
db_permissions = models.CharField(max_length=255, blank=True)
|
||||
|
||||
# lock string storage
|
||||
db_lock_storage = models.TextField(blank=True)
|
||||
# (deprecated, only here to allow MUX helpfile load (don't use otherwise)).
|
||||
# TODO: remove this when not needed anymore.
|
||||
db_staff_only = models.BooleanField(default=False)
|
||||
|
||||
# Database manager
|
||||
objects = HelpEntryManager()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
SharedMemoryModel.__init__(self, *args, **kwargs)
|
||||
self.locks = LockHandler(self)
|
||||
|
||||
class Meta:
|
||||
"Define Django meta options"
|
||||
|
|
@ -138,6 +144,23 @@ class HelpEntry(SharedMemoryModel):
|
|||
self.save()
|
||||
permissions = property(permissions_get, permissions_set, permissions_del)
|
||||
|
||||
# lock_storage property (wraps db_lock_storage)
|
||||
#@property
|
||||
def lock_storage_get(self):
|
||||
"Getter. Allows for value = self.lock_storage"
|
||||
return self.db_lock_storage
|
||||
#@nick.setter
|
||||
def lock_storage_set(self, value):
|
||||
"""Saves the lock_storagetodate. This is usually not called directly, but through self.lock()"""
|
||||
self.db_lock_storage = value
|
||||
self.save()
|
||||
#@nick.deleter
|
||||
def lock_storage_del(self):
|
||||
"Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead"""
|
||||
logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self)
|
||||
lock_storage = property(lock_storage_get, lock_storage_set, lock_storage_del)
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
# HelpEntry main class methods
|
||||
|
|
@ -149,3 +172,12 @@ class HelpEntry(SharedMemoryModel):
|
|||
|
||||
def __unicode__(self):
|
||||
return u'%s' % self.key
|
||||
|
||||
def access(self, accessing_obj, access_type='read', default=False):
|
||||
"""
|
||||
Determines if another object has permission to access.
|
||||
accessing_obj - object trying to access this one
|
||||
access_type - type of access sought
|
||||
default - what to return if no lock of access_type was found
|
||||
"""
|
||||
return self.locks.check(accessing_obj, access_type=access_type, default=default)
|
||||
|
|
|
|||
|
|
@ -3,18 +3,18 @@ This module provides a set of permission lock functions for use
|
|||
with Evennia's permissions system.
|
||||
|
||||
To call these locks, make sure this module is included in the
|
||||
settings tuple PERMISSION_FUNC_MODULES then define a permission
|
||||
string of the form 'myfunction(myargs)' and store it in the
|
||||
'permissions' field or variable on your object/command/channel/whatever.
|
||||
As part of the permission check, such permission strings will be
|
||||
evaluated to call myfunction(checking_obj, checked_obj, *yourargs) in
|
||||
this module. A boolean value is expected back.
|
||||
settings tuple PERMISSION_FUNC_MODULES then define a lock on the form
|
||||
'<access_type>:func(args)' and add it to the object's lockhandler.
|
||||
Run the check method of the handler to execute the lock check.
|
||||
|
||||
Note that checking_obj and checked_obj can be any object type
|
||||
with a permissions variable/field, so be careful to not expect
|
||||
Note that accessing_obj and accessed_obj can be any object type
|
||||
with a lock variable/field, so be careful to not expect
|
||||
a certain object type.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
MUX locks
|
||||
|
||||
Below is a list nicked from the MUX docs on the locks available
|
||||
|
|
@ -100,42 +100,64 @@ DefaultLock: Exits: controls who may traverse the exit to
|
|||
|
||||
"""
|
||||
|
||||
from src.permissions.permissions import get_types, has_perm, has_perm_string
|
||||
from django.conf import settings
|
||||
from src.utils import search
|
||||
|
||||
PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
|
||||
|
||||
def noperm(checking_obj, checked_obj, *args):
|
||||
"""
|
||||
Usage:
|
||||
noperm(mypermstring)
|
||||
noperm(perm1, perm2, perm3, ...)
|
||||
|
||||
A negative permission; this will return False only if
|
||||
the checking object *has any* of the given permission(s), True
|
||||
otherwise. The searched permission cannot itself be a
|
||||
function-permission (i.e. you cannot wrap functions in
|
||||
functions).
|
||||
"""
|
||||
if not args:
|
||||
# this is an always-false permission
|
||||
return False
|
||||
return not has_perm_string(checking_obj, args)
|
||||
|
||||
def is_superuser(checking_obj, checked_obj, *args):
|
||||
"""
|
||||
Usage:
|
||||
is_superuser()
|
||||
|
||||
Determines if the checking object is superuser.
|
||||
"""
|
||||
if hasattr(checking_obj, 'is_superuser'):
|
||||
return checking_obj.is_superuser
|
||||
def true(*args, **kwargs):
|
||||
"Always returns True."
|
||||
return True
|
||||
def all(*args, **kwargs):
|
||||
return True
|
||||
def false(*args, **kwargs):
|
||||
"Always returns False"
|
||||
return False
|
||||
def none(*args, **kwargs):
|
||||
return False
|
||||
|
||||
def has_id(checking_obj, checked_obj, *args):
|
||||
def perm(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
The basic permission-checker. Ignores case.
|
||||
|
||||
Usage:
|
||||
perm(<permission>)
|
||||
|
||||
where <permission> is the permission accessing_obj must
|
||||
have in order to pass the lock. If the given permission
|
||||
is part of PERMISSION_HIERARCHY, permission is also granted
|
||||
to all ranks higher up in the hierarchy.
|
||||
"""
|
||||
if not args:
|
||||
return False
|
||||
perm = args[0].lower()
|
||||
if hasattr(accessing_obj, 'permissions'):
|
||||
if perm in [p.lower() for p in accessing_obj.permissions]:
|
||||
# simplest case - we have a direct match
|
||||
return True
|
||||
if perm in PERMISSION_HIERARCHY:
|
||||
# check if we have a higher hierarchy position
|
||||
ppos = PERMISSION_HIERARCHY.index(perm)
|
||||
return any(True for hpos, hperm in enumerate(PERMISSION_HIERARCHY)
|
||||
if hperm in [p.lower() for p in accessing_obj.permissions] and hpos > ppos)
|
||||
return False
|
||||
|
||||
def perm_above(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Only allow objects with a permission *higher* in the permission
|
||||
hierarchy than the one given. If there is no such higher rank,
|
||||
it's assumed we refer to superuser. If no hierarchy is defined,
|
||||
this function has no meaning and returns False.
|
||||
"""
|
||||
if args and args[0].lower() in PERMISSION_HIERARCHY:
|
||||
ppos = PERMISSION_HIERARCHY.index(args[0].lower())
|
||||
return any(True for hpos, hperm in enumerate(PERMISSION_HIERARCHY)
|
||||
if hperm in [p.lower() for p in accessing_obj.permissions] and hpos > ppos)
|
||||
|
||||
def dbref(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
has_id(3)
|
||||
dbref(3)
|
||||
|
||||
This lock type checks if the checking object
|
||||
has a particular dbref. Note that this only
|
||||
|
|
@ -145,19 +167,29 @@ def has_id(checking_obj, checked_obj, *args):
|
|||
if not args:
|
||||
return False
|
||||
try:
|
||||
dbref = int(args[0].strip())
|
||||
dbref = int(args[0].strip().strip('#'))
|
||||
except ValueError:
|
||||
return False
|
||||
if hasattr(checking_obj, 'id'):
|
||||
return dbref == checking_obj.id
|
||||
if hasattr(accessing_obj, 'id'):
|
||||
return dbref == accessing_obj.id
|
||||
return False
|
||||
|
||||
def has_attr(checking_obj, checked_obj, *args):
|
||||
def id(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"Alias to dbref"
|
||||
return dbref(accessing_obj, accessed_obj, *args, **kwargs)
|
||||
|
||||
def attr(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
has_attr(attrname)
|
||||
has_attr(attrname, value)
|
||||
has_attr(attrname, value, compare=type)
|
||||
|
||||
where compare's type is one of (eq,gt,lt,ge,le,ne) and signifies
|
||||
how the value should be compared with one on accessing_obj (so
|
||||
compare=gt means the accessing_obj must have a value greater than
|
||||
the one given).
|
||||
|
||||
Searches attributes *and* properties stored on the checking
|
||||
object. The first form works like a flag - if the attribute/property
|
||||
exists on the object, it returns True. The second form also requires
|
||||
|
|
@ -171,18 +203,101 @@ def has_attr(checking_obj, checked_obj, *args):
|
|||
value = None
|
||||
if len(args) > 1:
|
||||
value = args[1].strip()
|
||||
# first, look for normal properties on the object trying to gain access
|
||||
if hasattr(checking_obj, attrname):
|
||||
compare = 'eq'
|
||||
if kwargs:
|
||||
compare = kwargs.get('compare', 'eq')
|
||||
|
||||
def valcompare(val1, val2, typ='eq'):
|
||||
"compare based on type"
|
||||
try:
|
||||
if typ == 'eq':
|
||||
return val1 == val2 or int(val1) == int(val2)
|
||||
elif typ == 'gt':
|
||||
return int(val1) > int(val2)
|
||||
elif typ == 'lt':
|
||||
return int(val1) < int(val2)
|
||||
elif typ == 'ge':
|
||||
return int(val1) >= int(val2)
|
||||
elif typ == 'le':
|
||||
return int(val1) <= int(val2)
|
||||
elif typ == 'ne':
|
||||
return int(val1) != int(val2)
|
||||
else:
|
||||
return False
|
||||
except Exception, e:
|
||||
print e
|
||||
# this might happen if we try to compare two things that cannot be compared
|
||||
return False
|
||||
|
||||
# first, look for normal properties on the object trying to gain access
|
||||
if hasattr(accessing_obj, attrname):
|
||||
if value:
|
||||
return str(getattr(checking_obj, attrname)) == value
|
||||
return valcompare(str(getattr(accessing_obj, attrname)), value, compare)
|
||||
return True
|
||||
# check attributes, if they exist
|
||||
#print "lockfunc default: %s (%s)" % (checking_obj, attrname)
|
||||
if hasattr(checking_obj, 'has_attribute') \
|
||||
and checking_obj.has_attribute(attrname):
|
||||
# check attributes, if they exist
|
||||
if (hasattr(accessing_obj, 'has_attribute')
|
||||
and accessing_obj.has_attribute(attrname)):
|
||||
if value:
|
||||
return hasattr(checking_obj, 'attr') \
|
||||
and checking_obj.attr(attrname) == value
|
||||
return (hasattr(accessing_obj, 'get_attribute')
|
||||
and valcompare(accessing_obj.get_attribute(attrname), value, compare))
|
||||
return True
|
||||
return False
|
||||
|
||||
def attr_eq(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
attr_gt(attrname, 54)
|
||||
"""
|
||||
return attr(accessing_obj, accessed_obj, *args, **kwargs)
|
||||
|
||||
def attr_gt(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
attr_gt(attrname, 54)
|
||||
|
||||
Only true if access_obj's attribute > the value given.
|
||||
"""
|
||||
return attr(accessing_obj, accessed_obj, *args, **{'compare':'gt'})
|
||||
def attr_ge(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
attr_gt(attrname, 54)
|
||||
|
||||
Only true if access_obj's attribute >= the value given.
|
||||
"""
|
||||
return attr(accessing_obj, accessed_obj, *args, **{'compare':'ge'})
|
||||
def attr_lt(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
attr_gt(attrname, 54)
|
||||
|
||||
Only true if access_obj's attribute < the value given.
|
||||
"""
|
||||
return attr(accessing_obj, accessed_obj, *args, **{'compare':'lt'})
|
||||
def attr_le(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
attr_gt(attrname, 54)
|
||||
|
||||
Only true if access_obj's attribute <= the value given.
|
||||
"""
|
||||
return attr(accessing_obj, accessed_obj, *args, **{'compare':'le'})
|
||||
def attr_ne(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"""
|
||||
Usage:
|
||||
attr_gt(attrname, 54)
|
||||
|
||||
Only true if access_obj's attribute != the value given.
|
||||
"""
|
||||
return attr(accessing_obj, accessed_obj, *args, **{'compare':'ne'})
|
||||
|
||||
def superuser(*args, **kwargs):
|
||||
"""
|
||||
Only accepts an accesing_obj that is superuser (e.g. user #1)
|
||||
|
||||
Since a superuser would not ever reach this check (superusers
|
||||
bypass the lock entirely), any user who gets this far cannot be a
|
||||
superuser, hence we just return False. :)
|
||||
"""
|
||||
return False
|
||||
|
||||
367
src/locks/lockhandler.py
Normal file
367
src/locks/lockhandler.py
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
"""
|
||||
Locks
|
||||
|
||||
A lock defines access to a particular subsystem or property of
|
||||
Evennia. For example, the "owner" property can be impmemented as a
|
||||
lock. Or the disability to lift an object or to ban users.
|
||||
|
||||
A lock consists of two three parts:
|
||||
|
||||
- access_type - this defines what kind of access this lock regulates. This
|
||||
just a string.
|
||||
- function call - this is one or many calls to functions that will determine
|
||||
if the lock is passed or not.
|
||||
- lock function(s). These are regular python functions with a special
|
||||
set of allowed arguments. They should always return a boolean depending
|
||||
on if they allow access or not.
|
||||
|
||||
# Lock function
|
||||
|
||||
A lock function is defined by existing in one of the modules
|
||||
listed by settings.LOCK_FUNC_MODULES. It should also always
|
||||
take four arguments looking like this:
|
||||
|
||||
funcname(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
[...]
|
||||
|
||||
The accessing object is the object wanting to gain access.
|
||||
The accessed object is the object this lock resides on
|
||||
args and kwargs will hold optional arguments and/or keyword arguments
|
||||
to the function as a list and a dictionary respectively.
|
||||
|
||||
Example:
|
||||
|
||||
perm(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
"Checking if the object has a particular, desired permission"
|
||||
if args:
|
||||
desired_perm = args[0]
|
||||
return desired_perm in accessing_obj.permissions
|
||||
return False
|
||||
|
||||
Lock functions should most often be pretty general and ideally possible to
|
||||
re-use and combine in various ways to build clever locks.
|
||||
|
||||
|
||||
# Lock definition
|
||||
|
||||
A lock definition is a string with a special syntax. It is added to
|
||||
each object's lockhandler, making that lock available from then on.
|
||||
|
||||
The lock definition looks like this:
|
||||
|
||||
'access_type:[NOT] func1(args)[ AND|OR][NOT] func2() ...'
|
||||
|
||||
That is, the access_type, a colon followed by calls to lock functions
|
||||
combined with AND or OR. NOT negates the result of the following call.
|
||||
|
||||
Example:
|
||||
|
||||
We want to limit who may edit a particular object (let's call this access_type
|
||||
for 'edit', it depends on what the command is looking for). We want this to
|
||||
only work for those with the Permission 'Builder'. So we use our lock
|
||||
function above and call it like this:
|
||||
|
||||
'edit:perm(Builder)'
|
||||
|
||||
Here, the lock-function perm() will be called (accessing_obj and accessed_obj are added
|
||||
automatically, you only need to add the args/kwargs, if any).
|
||||
|
||||
If we wanted to make sure the accessing object was BOTH a Builder and a GoodGuy, we
|
||||
could use AND:
|
||||
|
||||
'edit:perm(Builder) AND perm(GoodGuy)'
|
||||
|
||||
To allow EITHER Builders and GoodGuys, we replace AND with OR. perm() is just one example,
|
||||
the lock function can do anything and compare any properties of the calling object to
|
||||
decide if the lock is passed or not.
|
||||
|
||||
'lift:attrib(very_strong) AND NOT attrib(bad_back)'
|
||||
|
||||
To make these work, add the string to the lockhandler of the object you want
|
||||
to apply the lock to:
|
||||
|
||||
obj.lockhandler.add('edit:perm(Builder)')
|
||||
|
||||
From then on, a command that wants to check for 'edit' access on this
|
||||
object would do something like this:
|
||||
|
||||
if not target_obj.lockhandler.has_perm(caller, 'edit'):
|
||||
caller.msg("Sorry you cannot edit that.")
|
||||
|
||||
|
||||
# Permissions
|
||||
|
||||
Permissions are just text strings stored in a comma-separated list on
|
||||
typeclassed objects. The default perm() lock function uses them,
|
||||
taking into account settings.PERMISSION_HIERARCHY. Also, the
|
||||
restricted @perm command sets them, but otherwise they are identical
|
||||
to any other identifier you can use.
|
||||
|
||||
"""
|
||||
|
||||
import re, inspect
|
||||
from django.conf import settings
|
||||
from src.utils import logger, utils
|
||||
|
||||
#
|
||||
# Cached lock functions
|
||||
#
|
||||
|
||||
LOCKFUNCS = {}
|
||||
def cache_lockfuncs():
|
||||
"Updates the cache."
|
||||
global LOCKFUNCS
|
||||
LOCKFUNCS = {}
|
||||
for modulepath in settings.LOCK_FUNC_MODULES:
|
||||
modulepath = utils.pypath_to_realpath(modulepath)
|
||||
mod = utils.mod_import(modulepath)
|
||||
if mod:
|
||||
for tup in (tup for tup in inspect.getmembers(mod) if callable(tup[1])):
|
||||
LOCKFUNCS[tup[0]] = tup[1]
|
||||
else:
|
||||
logger.log_errmsg("Couldn't load %s from PERMISSION_FUNC_MODULES." % modulepath)
|
||||
|
||||
#
|
||||
# pre-compiled regular expressions
|
||||
#
|
||||
|
||||
RE_FUNCS = re.compile(r"\w+\([^)]*\)")
|
||||
RE_SEPS = re.compile(r"(?<=[ )])AND(?=\s)|(?<=[ )])OR(?=\s)|(?<=[ )])NOT(?=\s)")
|
||||
RE_OK = re.compile(r"%s|and|or|not")
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
# Lock handler
|
||||
#
|
||||
#
|
||||
|
||||
class LockHandler(object):
|
||||
"""
|
||||
This handler should be attached to all objects implementing
|
||||
permission checks, under the property 'lockhandler'.
|
||||
"""
|
||||
|
||||
def __init__(self, obj):
|
||||
"""
|
||||
Loads and pre-caches all relevant locks and their
|
||||
functions.
|
||||
"""
|
||||
if not LOCKFUNCS:
|
||||
cache_lockfuncs()
|
||||
self.obj = obj
|
||||
self.locks = {}
|
||||
self.log_obj = None
|
||||
self.no_errors = True
|
||||
self.reset_flag = False
|
||||
|
||||
self._cache_locks(self.obj.lock_storage)
|
||||
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return ";".join(self.locks[key][2] for key in sorted(self.locks))
|
||||
|
||||
def _log_error(self, message):
|
||||
"Try to log errors back to object"
|
||||
if self.log_obj and hasattr(self.log_obj, 'msg'):
|
||||
self.log_obj.msg(message)
|
||||
elif hasattr(self.obj, 'msg'):
|
||||
self.obj.msg(message)
|
||||
else:
|
||||
logger.log_trace("%s: %s" % (self.obj, message))
|
||||
|
||||
def _parse_lockstring(self, storage_lockstring):
|
||||
"""
|
||||
Helper function.
|
||||
locks are stored as a string 'atype:[NOT] lock()[[ AND|OR [NOT] lock() [...]];atype...
|
||||
"""
|
||||
locks = {}
|
||||
if not storage_lockstring:
|
||||
return locks
|
||||
nlocks = storage_lockstring.count(';') + 1
|
||||
duplicates = 0
|
||||
elist = []
|
||||
for raw_lockstring in storage_lockstring.split(';'):
|
||||
lock_funcs = []
|
||||
access_type, rhs = (part.strip() for part in raw_lockstring.split(':', 1))
|
||||
|
||||
# parse the lock functions and separators
|
||||
funclist = RE_FUNCS.findall(rhs)
|
||||
evalstring = rhs.replace('AND','and').replace('OR','or').replace('NOT','not')
|
||||
nfuncs = len(funclist)
|
||||
for funcstring in funclist:
|
||||
funcname, rest = [part.strip().strip(')') for part in funcstring.split('(', 1)]
|
||||
func = LOCKFUNCS.get(funcname, None)
|
||||
if not callable(func):
|
||||
elist.append("Lock: function '%s' is not available." % funcstring)
|
||||
continue
|
||||
args = [arg.strip() for arg in rest.split(',') if not '=' in arg]
|
||||
kwargs = dict([arg.split('=', 1) for arg in rest.split(',') if '=' in arg])
|
||||
lock_funcs.append((func, args, kwargs))
|
||||
evalstring = evalstring.replace(funcstring, '%s')
|
||||
if len(lock_funcs) < nfuncs:
|
||||
continue
|
||||
try:
|
||||
# purge the eval string of any superfluos items, then test it
|
||||
evalstring = " ".join(RE_OK.findall(evalstring))
|
||||
eval(evalstring % tuple(True for func in funclist))
|
||||
except Exception:
|
||||
elist.append("Lock: definition '%s' has syntax errors." % raw_lockstring)
|
||||
continue
|
||||
if access_type in locks:
|
||||
duplicates += 1
|
||||
elist.append("Lock: access type '%s' changed from '%s' to '%s' " % \
|
||||
(access_type, locks[access_type][2], raw_lockstring))
|
||||
locks[access_type] = (evalstring, tuple(lock_funcs), raw_lockstring)
|
||||
if elist:
|
||||
self._log_error("\n".join(elist))
|
||||
self.no_errors = False
|
||||
|
||||
return locks
|
||||
|
||||
def _cache_locks(self, storage_lockstring):
|
||||
"""Store data"""
|
||||
self.locks = self._parse_lockstring(storage_lockstring)
|
||||
|
||||
def _save_locks(self):
|
||||
"Store locks to obj"
|
||||
self.obj.lock_storage = ";".join([tup[2] for tup in self.locks.values()])
|
||||
|
||||
def add(self, lockstring, log_obj=None):
|
||||
"""
|
||||
Add a new, single lockstring on the form '<access_type>:<functions>'
|
||||
|
||||
If log_obj is given, it will be fed error information.
|
||||
"""
|
||||
if log_obj:
|
||||
self.log_obj = log_obj
|
||||
self.no_errors = True
|
||||
# sanity checks
|
||||
for lockdef in lockstring.split(';'):
|
||||
if not ':' in lockstring:
|
||||
self._log_error("Lock: '%s' contains no colon (:)." % lockdef)
|
||||
return False
|
||||
access_type, rhs = [part.strip() for part in lockdef.split(':', 1)]
|
||||
if not access_type:
|
||||
self._log_error("Lock: '%s' has no access_type (left-side of colon is empty)." % lockdef)
|
||||
return False
|
||||
if rhs.count('(') != rhs.count(')'):
|
||||
self._log_error("Lock: '%s' has mismatched parentheses." % lockdef)
|
||||
return False
|
||||
if not RE_FUNCS.findall(rhs):
|
||||
self._log_error("Lock: '%s' has no valid lock functions." % lockdef)
|
||||
return False
|
||||
# get the lock string
|
||||
storage_lockstring = self.obj.lock_storage
|
||||
if storage_lockstring:
|
||||
storage_lockstring = storage_lockstring + ";" + lockstring
|
||||
else:
|
||||
storage_lockstring = lockstring
|
||||
# cache the locks will get rid of eventual doublets
|
||||
self._cache_locks(storage_lockstring)
|
||||
self._save_locks()
|
||||
self.log_obj = None
|
||||
return self.no_errors
|
||||
|
||||
def get(self, access_type):
|
||||
"get the lockstring of a particular type"
|
||||
return self.locks.get(access_type, None)
|
||||
|
||||
def delete(self, access_type):
|
||||
"Remove a lock from the handler"
|
||||
if access_type in self.locks:
|
||||
del self.locks[access_type]
|
||||
self._save_locks()
|
||||
return True
|
||||
return False
|
||||
|
||||
def clear(self):
|
||||
"Remove all locks"
|
||||
self.locks = {}
|
||||
self.lock_storage = ""
|
||||
def reset(self):
|
||||
"""
|
||||
Set the reset flag, so the the lock will be re-cached at next checking.
|
||||
This is usually set by @reload.
|
||||
"""
|
||||
self.reset_flag = True
|
||||
|
||||
def check(self, accessing_obj, access_type, default=False):
|
||||
"""
|
||||
Checks a lock of the correct type by passing execution
|
||||
off to the lock function(s).
|
||||
|
||||
accessing_obj - the object seeking access
|
||||
access_type - the type of access wanted
|
||||
default - if no suitable lock type is found, use this
|
||||
"""
|
||||
if self.reset_flag:
|
||||
# rebuild cache
|
||||
self._cache_locks(self.obj.lock_storage)
|
||||
self.reset_flag = False
|
||||
|
||||
if (hasattr(accessing_obj, 'player') and hasattr(accessing_obj.player, 'is_superuser') and accessing_obj.player.is_superuser) \
|
||||
or (hasattr(accessing_obj, 'get_player') and (accessing_obj.get_player()==None or accessing_obj.get_player().is_superuser)):
|
||||
# we grant access to superusers and also to protocol instances that not yet has any player assigned to them (the
|
||||
# latter is a safety feature since superuser cannot be authenticated at some point during the connection).
|
||||
return True
|
||||
if access_type in self.locks:
|
||||
# we have a lock, test it.
|
||||
evalstring, func_tup, raw_string = self.locks[access_type]
|
||||
# we have previously stored the function object and all the args/kwargs as list/dict,
|
||||
# now we just execute them all in sequence. The result will be a list of True/False
|
||||
# statements. Note that there is no eval here, these are normal command calls!
|
||||
true_false = (tup[0](accessing_obj, self.obj, *tup[1], **tup[2]) for tup in func_tup)
|
||||
# we now input these True/False list into the evalstring, which combines them with
|
||||
# AND/OR/NOT in order to get the final result
|
||||
return eval(evalstring % tuple(true_false))
|
||||
else:
|
||||
return default
|
||||
|
||||
def check_lockstring(self, accessing_obj, accessed_obj, lockstring):
|
||||
"""
|
||||
Do a direct check against a lockstring ('atype:func()..'), without any
|
||||
intermediary storage on the accessed object (this can be left
|
||||
to None if the lock functions called don't access it). atype can also be
|
||||
put to a dummy value since no lock selection is made.
|
||||
"""
|
||||
if (hasattr(accessing_obj, 'player') and hasattr(accessing_obj.player, 'user')
|
||||
and hasattr(accessing_obj.player.user, 'is_superuser')
|
||||
and accessing_obj.player.user.is_superuser):
|
||||
return True # always grant access to the superuser.
|
||||
locks = self. _parse_lockstring(lockstring)
|
||||
for access_type in locks:
|
||||
evalstring, func_tup, raw_string = locks[access_type]
|
||||
true_false = (tup[0](accessing_obj, self.obj, *tup[1], **tup[2]) for tup in func_tup)
|
||||
return eval(evalstring % tuple(true_false))
|
||||
|
||||
|
||||
|
||||
def test():
|
||||
# testing
|
||||
|
||||
class TestObj(object):
|
||||
pass
|
||||
|
||||
import pdb
|
||||
obj1 = TestObj()
|
||||
obj2 = TestObj()
|
||||
|
||||
obj1.lock_storage = "owner:dbref(#4);edit:dbref(#5) or perm(Wizards);examine:perm(Builders);delete:perm(Wizards);get:all()"
|
||||
#obj1.lock_storage = "cmd:all();admin:id(1);listen:all();send:all()"
|
||||
|
||||
pdb.set_trace()
|
||||
obj1.locks = LockHandler(obj1)
|
||||
obj2.permissions = ["Wizards"]
|
||||
obj2.id = 4
|
||||
|
||||
#obj1.locks.add("edit:attr(test)")
|
||||
|
||||
print "comparing obj2.permissions (%s) vs obj1.locks (%s)" % (obj2.permissions, obj1.locks)
|
||||
print obj1.locks.check(obj2, 'owner')
|
||||
print obj1.locks.check(obj2, 'edit')
|
||||
print obj1.locks.check(obj2, 'examine')
|
||||
print obj1.locks.check(obj2, 'delete')
|
||||
print obj1.locks.check(obj2, 'get')
|
||||
print obj1.locks.check(obj2, 'listen')
|
||||
63
src/locks/tests.py
Normal file
63
src/locks/tests.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
This is part of Evennia's unittest framework, for testing
|
||||
the stability and integrrity of the codebase during updates.
|
||||
|
||||
This module tests the lock functionality of Evennia.
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
# this is a special optimized Django version, only available in current Django devel
|
||||
from django.utils.unittest import TestCase
|
||||
except ImportError:
|
||||
from django.test import TestCase
|
||||
|
||||
from django.conf import settings
|
||||
from src.locks import lockhandler, lockfuncs
|
||||
from src.utils import create
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
# Lock testing
|
||||
#
|
||||
#------------------------------------------------------------
|
||||
|
||||
class LockTest(TestCase):
|
||||
"Defines the lock test base"
|
||||
def setUp(self):
|
||||
"sets up the testing environment"
|
||||
|
||||
self.obj1 = create.create_object(settings.BASE_OBJECT_TYPECLASS, key="obj1")
|
||||
self.obj2 = create.create_object(settings.BASE_OBJECT_TYPECLASS, key="obj2")
|
||||
|
||||
class TestLockCheck(LockTest):
|
||||
def testrun(self):
|
||||
dbref = self.obj2.dbref
|
||||
self.obj1.locks.add("owner:dbref(%s);edit:dbref(%s) or perm(Wizards);examine:perm(Builders) and id(%s);delete:perm(Wizards);get:all()" % (dbref, dbref, dbref))
|
||||
self.obj2.permissions = ['Wizards']
|
||||
self.assertEquals(True, self.obj1.locks.check(self.obj2, 'owner'))
|
||||
self.assertEquals(True, self.obj1.locks.check(self.obj2, 'edit'))
|
||||
self.assertEquals(True, self.obj1.locks.check(self.obj2, 'examine'))
|
||||
self.assertEquals(True, self.obj1.locks.check(self.obj2, 'delete'))
|
||||
self.assertEquals(True, self.obj1.locks.check(self.obj2, 'get'))
|
||||
self.obj1.locks.add("get:false()")
|
||||
self.assertEquals(False, self.obj1.locks.check(self.obj2, 'get'))
|
||||
self.assertEquals(True, self.obj1.locks.check(self.obj2, 'not_exist', default=True))
|
||||
class TestLockfuncs(LockTest):
|
||||
def testrun(self):
|
||||
self.obj2.permissions = ['Wizards']
|
||||
self.assertEquals(True, lockfuncs.true(self.obj2, self.obj1))
|
||||
self.assertEquals(False, lockfuncs.false(self.obj2, self.obj1))
|
||||
self.assertEquals(True, lockfuncs.perm(self.obj2, self.obj1, 'Wizards'))
|
||||
self.assertEquals(True, lockfuncs.perm_above(self.obj2, self.obj1, 'Builders'))
|
||||
dbref = self.obj2.dbref
|
||||
self.assertEquals(True, lockfuncs.dbref(self.obj2, self.obj1, '%s' % dbref))
|
||||
self.obj2.db.testattr = 45
|
||||
self.assertEquals(True, lockfuncs.attr(self.obj2, self.obj1, 'testattr', '45'))
|
||||
self.assertEquals(False, lockfuncs.attr_gt(self.obj2, self.obj1, 'testattr', '45'))
|
||||
self.assertEquals(True, lockfuncs.attr_ge(self.obj2, self.obj1, 'testattr', '45'))
|
||||
self.assertEquals(False, lockfuncs.attr_lt(self.obj2, self.obj1, 'testattr', '45'))
|
||||
self.assertEquals(True, lockfuncs.attr_le(self.obj2, self.obj1, 'testattr', '45'))
|
||||
self.assertEquals(False, lockfuncs.attr_ne(self.obj2, self.obj1, 'testattr', '45'))
|
||||
|
|
@ -4,18 +4,19 @@ an object's location for valid exit objects.
|
|||
"""
|
||||
|
||||
from src.commands import cmdset, command
|
||||
from src.permissions.permissions import has_perm
|
||||
|
||||
|
||||
class ExitCommand(command.Command):
|
||||
"Simple identifier command"
|
||||
is_exit = True
|
||||
locks = "cmd:all()" # should always be set to this.
|
||||
destination = None
|
||||
obj = None
|
||||
|
||||
def func(self):
|
||||
"Default exit traverse if no syscommand is defined."
|
||||
|
||||
if has_perm(self.caller, self.obj, 'traverse'):
|
||||
if self.obj.access(self.caller, 'traverse'):
|
||||
self.caller.move_to(self.destination)
|
||||
else:
|
||||
self.caller.msg("You cannot enter.")
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ from src.typeclasses.models import Attribute, TypedObject
|
|||
from src.typeclasses.typeclass import TypeClass
|
||||
from src.objects.manager import ObjectManager
|
||||
from src.config.models import ConfigValue
|
||||
from src.permissions.permissions import has_perm
|
||||
|
||||
from src.utils import logger
|
||||
from src.utils.utils import is_iter
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ FULL_PERSISTENCE = settings.FULL_PERSISTENCE
|
|||
|
||||
try:
|
||||
HANDLE_SEARCH_ERRORS = __import__(
|
||||
settings.ALTERNATE_OBJECT_SEARCH_ERROR_HANDLER).handle_search_errors
|
||||
settings.ALTERNATE_OBJECT_SEARCH_ERROR_HANDLER).handle_search_errors, fromlist=[None]
|
||||
except Exception:
|
||||
from src.objects.object_search_funcs \
|
||||
import handle_search_errors as HANDLE_SEARCH_ERRORS
|
||||
|
|
@ -70,6 +70,12 @@ class Alias(SharedMemoryModel):
|
|||
"Define Django meta options"
|
||||
verbose_name = "Object alias"
|
||||
verbose_name_plural = "Object aliases"
|
||||
def __unicode__(self):
|
||||
return u"%s" % self.db_key
|
||||
def __str__(self):
|
||||
return str(self.db_key)
|
||||
|
||||
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
|
|
@ -104,6 +110,47 @@ class Nick(SharedMemoryModel):
|
|||
verbose_name_plural = "Nicknames"
|
||||
unique_together = ("db_nick", "db_type", "db_obj")
|
||||
|
||||
class NickHandler(object):
|
||||
"""
|
||||
Handles nick access and setting. Accessed through ObjectDB.nicks
|
||||
"""
|
||||
|
||||
def __init__(self, obj):
|
||||
"Setup"
|
||||
self.obj = obj
|
||||
|
||||
def add(self, nick, realname, nick_type="inputline"):
|
||||
"We want to assign a new nick"
|
||||
if not nick or not nick.strip():
|
||||
return
|
||||
nick = nick.strip()
|
||||
real = realname.strip()
|
||||
query = Nick.objects.filter(db_obj=self.obj, db_nick__iexact=nick, db_type__iexact=nick_type)
|
||||
if query.count():
|
||||
old_nick = query[0]
|
||||
old_nick.db_real = real
|
||||
old_nick.save()
|
||||
else:
|
||||
new_nick = Nick(db_nick=nick, db_real=real, db_type=nick_type, db_obj=self.obj)
|
||||
new_nick.save()
|
||||
def delete(self, nick, nick_type="inputline"):
|
||||
"Removes a nick"
|
||||
nick = nick.strip()
|
||||
query = Nick.objects.filter(db_obj=self.obj, db_nick__iexact=nick, db_type__iexact=nick_type)
|
||||
if query.count():
|
||||
# remove the found nick(s)
|
||||
query.delete()
|
||||
def get(self, nick=None, nick_type="inputline"):
|
||||
if nick:
|
||||
query = Nick.objects.filter(db_obj=self.obj, db_nick__iexact=nick, db_type__iexact=nick_type)
|
||||
query = query.values_list("db_real", flat=True)
|
||||
if query.count():
|
||||
return query[0]
|
||||
else:
|
||||
return nick
|
||||
else:
|
||||
return Nick.objects.filter(db_obj=self.obj)
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
# ObjectDB
|
||||
|
|
@ -127,20 +174,24 @@ class ObjectDB(TypedObject):
|
|||
typeclass - auto-linked typeclass
|
||||
date_created - time stamp of object creation
|
||||
permissions - perm strings
|
||||
Dbref - #id of object
|
||||
locks - lock definitions (handler)
|
||||
dbref - #id of object
|
||||
db - persistent attribute storage
|
||||
ndb - non-persistent attribute storage
|
||||
|
||||
The ObjectDB adds the following properties:
|
||||
aliases - alternative names for object
|
||||
player - optional connected player
|
||||
location - in-game location of object
|
||||
home - safety location for object
|
||||
nicks - this objects nicknames for *other* objects
|
||||
home - safety location for object (handler)
|
||||
|
||||
scripts - scripts assigned to object (handler from typeclass)
|
||||
cmdset - active cmdset on object (handler from typeclass)
|
||||
aliases - aliases for this object (property)
|
||||
nicks - nicknames for *other* things in Evennia (handler)
|
||||
sessions - sessions connected to this object (see also player)
|
||||
has_player - bool if an active player is currently connected
|
||||
contents - other objects having this object as location
|
||||
|
||||
exits - exits from this object
|
||||
"""
|
||||
|
||||
#
|
||||
|
|
@ -155,9 +206,6 @@ class ObjectDB(TypedObject):
|
|||
# using their corresponding properties, named same as the field,
|
||||
# but withtout the db_* prefix.
|
||||
|
||||
# comma-separated list of alias-names of this object. Note that default
|
||||
# searches only search aliases in the same location as caller.
|
||||
db_aliases = models.ForeignKey(Alias, blank=True, null=True, db_index=True)
|
||||
# If this is a character object, the player is connected here.
|
||||
db_player = models.ForeignKey("players.PlayerDB", blank=True, null=True)
|
||||
# The location in the game world. Since this one is likely
|
||||
|
|
@ -168,13 +216,18 @@ class ObjectDB(TypedObject):
|
|||
# a safety location, this usually don't change much.
|
||||
db_home = models.ForeignKey('self', related_name="homes_set",
|
||||
blank=True, null=True)
|
||||
# pickled dictionary storing the object's assigned nicknames
|
||||
# (use the 'nicks' property to access)
|
||||
db_nicks = models.ForeignKey(Nick, blank=True, null=True, db_index=True)
|
||||
|
||||
# Database manager
|
||||
objects = ObjectManager()
|
||||
|
||||
# Add the object-specific handlers
|
||||
# (scripts and cmdset must be added from
|
||||
# typeclass, so not added here)
|
||||
def __init__(self, *args, **kwargs):
|
||||
"Parent must be initialized first."
|
||||
TypedObject.__init__(self, *args, **kwargs)
|
||||
self.nicks = NickHandler(self)
|
||||
|
||||
# Wrapper properties to easily set database fields. These are
|
||||
# @property decorators that allows to access these fields using
|
||||
# normal python operations (without having to remember to save()
|
||||
|
|
@ -304,22 +357,29 @@ class ObjectDB(TypedObject):
|
|||
self.db_home = None
|
||||
home = property(home_get, home_set, home_del)
|
||||
|
||||
# nicks property (wraps db_nicks)
|
||||
#@property
|
||||
def nicks_get(self):
|
||||
"Getter. Allows for value = self.aliases"
|
||||
return list(Nick.objects.filter(db_obj=self))
|
||||
#@nick.setter
|
||||
def nicks_set(self, nicks):
|
||||
"""Setter is disabled. Use the nickhandler instead."""
|
||||
logger.log_errmsg("Nicks (%s) cannot be set through obj.nicks. Use obj.nickhandler instead." % nicks)
|
||||
#@nick.deleter
|
||||
def nicks_del(self):
|
||||
"Deleter. Allows for del self.aliases"
|
||||
for nick in Nick.objects.filter(db_obj=self):
|
||||
nick.delete()
|
||||
nicks = property(nicks_get, nicks_set, nicks_del)
|
||||
|
||||
#@property for consistent aliases access throughout Evennia
|
||||
#@aliases.setter
|
||||
def aliases_set(self, aliases):
|
||||
"Adds an alias to object"
|
||||
if not is_iter(aliases):
|
||||
aliases = [aliases]
|
||||
for alias in aliases:
|
||||
query = Alias.objects.filter(db_obj=self, db_key__iexact=alias)
|
||||
if query.count():
|
||||
continue
|
||||
new_alias = Alias(db_key=alias, db_obj=self)
|
||||
new_alias.save()
|
||||
#@aliases.getter
|
||||
def aliases_get(self):
|
||||
"Return a list of all aliases defined on this object."
|
||||
return list(Alias.objects.filter(db_obj=self).values_list("db_key", flat=True))
|
||||
#@aliases.deleter
|
||||
def aliases_del(self):
|
||||
"Removes aliases from object"
|
||||
query = Alias.objects.filter(db_obj=self)
|
||||
if query:
|
||||
query.delete()
|
||||
aliases = property(aliases_get, aliases_set, aliases_del)
|
||||
|
||||
class Meta:
|
||||
"Define Django meta options"
|
||||
|
|
@ -378,55 +438,8 @@ class ObjectDB(TypedObject):
|
|||
return [exi for exi in self.contents
|
||||
if exi.has_attribute('_destination')]
|
||||
exits = property(exits_get)
|
||||
|
||||
def nickhandler(self, nick, realname=None, nick_type="inputline", startswith=False, delete=False):
|
||||
"""
|
||||
This is a central method for getting and setting nicks
|
||||
- mappings of nicks to strings, objects etc. This is the main
|
||||
access method, use it in preference over the 'nicks' property.
|
||||
|
||||
Map a nick to a realname. Be careful if mapping an
|
||||
existing realname into a nick - you could make that
|
||||
realname inaccessible until you deleted the alias.
|
||||
To delete - don't set realname.
|
||||
|
||||
nick_types can be defined freely depending on implementation.
|
||||
The default nick_types used in Evennia are:
|
||||
inputline (default) - match nick against all input
|
||||
player - match nick against player searches
|
||||
obj - match nick against object searches
|
||||
channel - match nick when checking for channel aliases
|
||||
|
||||
the delete keyword will delete the given nick.
|
||||
"""
|
||||
if not nick or not nick.strip():
|
||||
return
|
||||
nick = nick.strip()
|
||||
query = Nick.objects.filter(db_obj=self, db_nick__iexact=nick)
|
||||
if nick_type:
|
||||
query = query.filter(db_type__iexact=nick_type)
|
||||
if delete:
|
||||
# remove the found nick(s)
|
||||
query.delete()
|
||||
elif realname == None:
|
||||
# we want to get the real name for the nick. If none is
|
||||
# found, we return the nick again
|
||||
query = query.values_list("db_real", flat=True)
|
||||
if query:
|
||||
return query[0]
|
||||
else:
|
||||
return nick
|
||||
else:
|
||||
# we want to assign a new nick
|
||||
real = realname.strip()
|
||||
if query:
|
||||
old_nick = query[0]
|
||||
old_nick.db_real = real
|
||||
old_nick.save()
|
||||
else:
|
||||
new_nick = Nick(db_nick=nick, db_real=real, db_type=nick_type, db_obj=self)
|
||||
new_nick.save()
|
||||
|
||||
|
||||
#
|
||||
# Main Search method
|
||||
#
|
||||
|
|
@ -467,10 +480,10 @@ class ObjectDB(TypedObject):
|
|||
if use_nicks:
|
||||
if ostring.startswith('*'):
|
||||
# player nick replace
|
||||
ostring = "*%s" % self.nickhandler(ostring.lstrip('*'), nick_type="player")
|
||||
ostring = "*%s" % self.nicks.get(ostring.lstrip('*'), nick_type="player")
|
||||
else:
|
||||
# object nick replace
|
||||
ostring = self.nickhandler(ostring, nick_type="object")
|
||||
ostring = self.nicks.get(ostring, nick_type="object")
|
||||
|
||||
results = ObjectDB.objects.object_search(self, ostring,
|
||||
global_search=global_search,
|
||||
|
|
@ -485,25 +498,7 @@ class ObjectDB(TypedObject):
|
|||
#
|
||||
# Execution/action methods
|
||||
#
|
||||
|
||||
def has_perm(self, accessing_obj, lock_type):
|
||||
"""
|
||||
Determines if another object has permission to access
|
||||
this object.
|
||||
accessing_obj - the object trying to gain access.
|
||||
lock_type : type of access checked for
|
||||
"""
|
||||
return has_perm(accessing_obj, self, lock_type)
|
||||
|
||||
def has_perm_on(self, accessed_obj, lock_type):
|
||||
"""
|
||||
Determines if *this* object has permission to access
|
||||
another object.
|
||||
accessed_obj - the object being accessed by this one
|
||||
lock_type : type of access checked for
|
||||
"""
|
||||
return has_perm(self, accessed_obj, lock_type)
|
||||
|
||||
|
||||
def execute_cmd(self, raw_string):
|
||||
"""
|
||||
Do something as this object. This command transparently
|
||||
|
|
@ -536,6 +531,7 @@ class ObjectDB(TypedObject):
|
|||
|
||||
def emit_to(self, message, from_obj=None, data=None):
|
||||
"Deprecated. Alias for msg"
|
||||
logger.log_depmsg("emit_to() is deprecated. Use msg() instead.")
|
||||
self.msg(message, from_obj, data)
|
||||
|
||||
def msg_contents(self, message, exclude=None, from_obj=None, data=None):
|
||||
|
|
@ -555,6 +551,7 @@ class ObjectDB(TypedObject):
|
|||
|
||||
def emit_to_contents(self, message, exclude=None, from_obj=None, data=None):
|
||||
"Deprecated. Alias for msg_contents"
|
||||
logger.log_depmsg("emit_to_contents() is deprecated. Use msg_contents() instead.")
|
||||
self.msg_contents(message, exclude=exclude, from_obj=from_obj, data=data)
|
||||
|
||||
def move_to(self, destination, quiet=False,
|
||||
|
|
|
|||
|
|
@ -11,9 +11,6 @@ Both the replacing functions must have the same name and same input/output
|
|||
as the ones in this module.
|
||||
"""
|
||||
|
||||
from src.permissions.permissions import has_perm_string
|
||||
|
||||
|
||||
def handle_search_errors(emit_to_obj, ostring, results, global_search=False):
|
||||
"""
|
||||
Takes a search result (a list) and
|
||||
|
|
@ -35,7 +32,7 @@ def handle_search_errors(emit_to_obj, ostring, results, global_search=False):
|
|||
|
||||
# check if the emit_to_object may se dbrefs
|
||||
show_dbref = global_search and \
|
||||
has_perm_string(emit_to_obj, 'see_dbref')
|
||||
emit_to_obj.check_permstring('Builders')
|
||||
|
||||
string = "More than one match for '%s'" % ostring
|
||||
string += " (please narrow target):"
|
||||
|
|
|
|||
|
|
@ -49,10 +49,10 @@ class Object(TypeClass):
|
|||
create_cmdset = True
|
||||
try:
|
||||
dummy = object.__getattribute__(dbobj, 'scripts')
|
||||
create_scripts = type(dbobj.scripts) != ScriptHandler
|
||||
|
||||
create_scripts = type(dbobj.scripts) != ScriptHandler
|
||||
except AttributeError:
|
||||
create_scripts = True
|
||||
|
||||
if create_cmdset:
|
||||
dbobj.cmdset = CmdSetHandler(dbobj)
|
||||
if utils.inherits_from(self, settings.BASE_CHARACTER_TYPECLASS):
|
||||
|
|
@ -79,8 +79,18 @@ class Object(TypeClass):
|
|||
"""
|
||||
Called once, when this object is first
|
||||
created.
|
||||
"""
|
||||
pass
|
||||
"""
|
||||
|
||||
# the default security setup fallback for a generic
|
||||
# object. Overload in child for a custom setup. Also creation
|
||||
# commands may set this (create an item and you should its
|
||||
# owner, for example)
|
||||
dbref = self.dbobj.dbref
|
||||
self.locks.add("owner:id(%s)" % dbref)
|
||||
self.locks.add("examine:perm(Builders)")
|
||||
self.locks.add("edit:perm(Wizards)")
|
||||
self.locks.add("delete:perm(Wizards)")
|
||||
self.locks.add("get:all()")
|
||||
|
||||
def at_first_login(self):
|
||||
"""
|
||||
|
|
@ -326,11 +336,16 @@ class Character(Object):
|
|||
"""
|
||||
from settings import CMDSET_DEFAULT
|
||||
self.cmdset.add_default(CMDSET_DEFAULT, permanent=True)
|
||||
|
||||
# setup security
|
||||
super(Character, self).at_object_creation()
|
||||
self.locks.add("puppet:id(%s) or perm(Immortals)" % self.dbobj.dbref)
|
||||
|
||||
def at_after_move(self, source_location):
|
||||
"Default is to look around after a move."
|
||||
self.execute_cmd('look')
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Base Room object
|
||||
#
|
||||
|
|
@ -345,6 +360,7 @@ class Room(Object):
|
|||
Simple setup, shown as an example
|
||||
(since default is None anyway)
|
||||
"""
|
||||
super(Room, self).at_object_creation()
|
||||
self.location = None
|
||||
|
||||
class Exit(Object):
|
||||
|
|
@ -366,7 +382,13 @@ class Exit(Object):
|
|||
definition (unless you want an entire class of exits
|
||||
all leadning to the same hard-coded place ...)
|
||||
"""
|
||||
# this is what makes it an exit
|
||||
self.attr("_destination", "None")
|
||||
|
||||
# the lock is open to all by default
|
||||
super(Exit, self).at_object_creation()
|
||||
self.locks.add("traverse:all()")
|
||||
|
||||
def at_object_delete(self):
|
||||
"""
|
||||
We have to make sure to clean the exithandler cache
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ from django.conf import settings
|
|||
from src.objects import models, objects
|
||||
from src.utils import create
|
||||
from src.commands.default import tests as commandtests
|
||||
from src.locks import tests as locktests
|
||||
|
||||
class TestObjAttrs(TestCase):
|
||||
"""
|
||||
|
|
@ -55,4 +56,5 @@ def suite():
|
|||
tsuite = unittest.TestSuite()
|
||||
tsuite.addTest(unittest.defaultTestLoader.loadTestsFromModule(sys.modules[__name__]))
|
||||
tsuite.addTest(unittest.defaultTestLoader.loadTestsFromModule(commandtests))
|
||||
tsuite.addTest(unittest.defaultTestLoader.loadTestsFromModule(locktests))
|
||||
return tsuite
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
#
|
||||
# This sets up how models are displayed
|
||||
# in the web admin interface.
|
||||
#
|
||||
|
||||
from src.permissions.models import PermissionGroup
|
||||
from django.contrib import admin
|
||||
|
||||
class PermissionGroupAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'db_key', 'db_group_permissions')
|
||||
list_display_links = ('id', "db_key")
|
||||
ordering = ['db_key', 'db_group_permissions']
|
||||
readonly_fields = ['db_key', 'db_group_permissions', 'db_permissions']
|
||||
search_fields = ['db_key']
|
||||
save_as = True
|
||||
save_on_top = True
|
||||
list_select_related = True
|
||||
admin.site.register(PermissionGroup, PermissionGroupAdmin)
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
"""
|
||||
Setup the permission hierarchy and groups. This is
|
||||
read once during server startup. Further groups and
|
||||
permissions have to be added manually.
|
||||
|
||||
To set up your own permission scheme, have
|
||||
PERMISSION_SETUP_MODULE in game/settings point to
|
||||
a module of your own. This module must define two global
|
||||
dictionaries PERMS and GROUPS.
|
||||
|
||||
PERMS contains all permissions defined at server start
|
||||
on the form {key:desc, key:desc, ...}
|
||||
GROUPS gathers permissions (which must have been
|
||||
previously created as keys in PERMS) into clusters
|
||||
on the form {groupname: [key, key, ...], ...}
|
||||
"""
|
||||
|
||||
# Defining all permissions.
|
||||
PERMS = [
|
||||
'emit',
|
||||
'wall',
|
||||
|
||||
'teleport',
|
||||
'setobjalias',
|
||||
'wipe',
|
||||
'set',
|
||||
'cpattr',
|
||||
'mvattr',
|
||||
'find',
|
||||
'create',
|
||||
'copy',
|
||||
'open',
|
||||
'link',
|
||||
'unlink',
|
||||
'dig',
|
||||
'desc',
|
||||
'destroy',
|
||||
'examine',
|
||||
'typeclass',
|
||||
'debug',
|
||||
'puppet',
|
||||
'typeclass',
|
||||
|
||||
'batchcommands',
|
||||
'batchcodes',
|
||||
|
||||
'ccreate',
|
||||
'cdesc',
|
||||
'tell',
|
||||
'time',
|
||||
'list',
|
||||
|
||||
'ps',
|
||||
'stats',
|
||||
|
||||
'reload',
|
||||
'py',
|
||||
'listscripts',
|
||||
'listcmdsets',
|
||||
'listobjects',
|
||||
'boot',
|
||||
'delplayer',
|
||||
'newpassword',
|
||||
'home',
|
||||
'service',
|
||||
'shutdown',
|
||||
'perm',
|
||||
'sethelp',
|
||||
]
|
||||
|
||||
# Permission Groups
|
||||
# Permission groups clump the previously defined permissions into
|
||||
# larger chunks. {groupname: [permissionkey,... ]}
|
||||
|
||||
GROUPS = {
|
||||
"Immortals": PERMS,
|
||||
"Wizards": [perm for perm in PERMS
|
||||
if perm not in ['shutdown',
|
||||
'py',
|
||||
'reload',
|
||||
'service',
|
||||
'perm',
|
||||
'typeclass',
|
||||
'batchcommands',
|
||||
'batchcodes']],
|
||||
|
||||
"Builders": [perm for perm in PERMS
|
||||
if perm not in ['shutdown',
|
||||
'py',
|
||||
'reload',
|
||||
'service',
|
||||
'perm',
|
||||
'batchcommands',
|
||||
'batchcodes',
|
||||
'puppet',
|
||||
|
||||
'wall',
|
||||
'boot',
|
||||
'delplayer',
|
||||
'newpassword']],
|
||||
"PlayerHelpers": ['tell',
|
||||
'sethelp', 'ccreate', 'use_channels'],
|
||||
"Players": ['tell', 'ccreate', 'use_channels']
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
"""
|
||||
A simple manager for Permission groups
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
|
||||
class PermissionGroupManager(models.Manager):
|
||||
"Adds a search method to the default manager"
|
||||
|
||||
def search_permgroup(self, ostring):
|
||||
"""
|
||||
Find a permission group
|
||||
|
||||
ostring = permission group name (case sensitive)
|
||||
or database dbref
|
||||
"""
|
||||
groups = []
|
||||
try:
|
||||
dbref = int(ostring.strip('#'))
|
||||
groups = self.filter(id=dbref)
|
||||
except Exception:
|
||||
pass
|
||||
if not groups:
|
||||
groups = self.filter(db_key=ostring)
|
||||
return groups
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
"""
|
||||
The PermissionGroup model clumps permissions together into
|
||||
manageable chunks.
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
from src.utils.idmapper.models import SharedMemoryModel
|
||||
from src.permissions.manager import PermissionGroupManager
|
||||
from src.utils.utils import is_iter
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
# PermissionGroup
|
||||
#
|
||||
#------------------------------------------------------------
|
||||
|
||||
class PermissionGroup(SharedMemoryModel):
|
||||
"""
|
||||
This groups permissions into a clump.
|
||||
|
||||
The following properties are defined:
|
||||
key - main ident for group
|
||||
desc - optional description of group
|
||||
group_permissions - the perms stored in group
|
||||
permissions - the group's own permissions
|
||||
|
||||
"""
|
||||
|
||||
#
|
||||
# PermissionGroup database model setup
|
||||
#
|
||||
# These database fields are all set using their corresponding properties,
|
||||
# named same as the field, but withtout the db_* prefix.
|
||||
#
|
||||
|
||||
# identifier for the group
|
||||
db_key = models.CharField(max_length=80, unique=True)
|
||||
# description of the group's permission contents
|
||||
db_desc = models.CharField(max_length=255, null=True, blank=True)
|
||||
# the permissions stored in this group; comma separated string
|
||||
# (NOT to be confused with the group object's own permissions!)
|
||||
db_group_permissions = models.TextField(blank=True)
|
||||
# OBS - this is the groups OWN permissions, for accessing/changing
|
||||
# the group object itself (comma-separated string)!
|
||||
db_permissions = models.CharField(max_length=256, blank=True)
|
||||
|
||||
# Database manager
|
||||
objects = PermissionGroupManager()
|
||||
|
||||
# Wrapper properties to easily set database fields. These are
|
||||
# @property decorators that allows to access these fields using
|
||||
# normal python operations (without having to remember to save()
|
||||
# etc). So e.g. a property 'attr' has a get/set/del decorator
|
||||
# defined that allows the user to do self.attr = value,
|
||||
# value = self.attr and del self.attr respectively (where self
|
||||
# is the object in question).
|
||||
|
||||
# key property (wraps db_key)
|
||||
#@property
|
||||
def key_get(self):
|
||||
"Getter. Allows for value = self.key"
|
||||
return self.db_key
|
||||
#@key.setter
|
||||
def key_set(self, value):
|
||||
"Setter. Allows for self.key = value"
|
||||
self.db_key = value
|
||||
self.save()
|
||||
#@key.deleter
|
||||
def key_del(self):
|
||||
"Deleter. Allows for del self.key"
|
||||
self.db_key = None
|
||||
self.save()
|
||||
key = property(key_get, key_set, key_del)
|
||||
|
||||
# desc property (wraps db_desc)
|
||||
#@property
|
||||
def desc_get(self):
|
||||
"Getter. Allows for value = self.desc"
|
||||
return self.db_desc
|
||||
#@desc.setter
|
||||
def desc_set(self, value):
|
||||
"Setter. Allows for self.desc = value"
|
||||
self.db_desc = value
|
||||
self.save()
|
||||
#@desc.deleter
|
||||
def desc_del(self):
|
||||
"Deleter. Allows for del self.desc"
|
||||
self.db_desc = None
|
||||
self.save()
|
||||
desc = property(desc_get, desc_set, desc_del)
|
||||
|
||||
# group_permissions property
|
||||
#@property
|
||||
def group_permissions_get(self):
|
||||
"Getter. Allows for value = self.name. Returns a list of group_permissions."
|
||||
if self.db_group_permissions:
|
||||
return [perm.strip() for perm in self.db_group_permissions.split(',')]
|
||||
return []
|
||||
#@group_permissions.setter
|
||||
def group_permissions_set(self, value):
|
||||
"Setter. Allows for self.name = value. Stores as a comma-separated string."
|
||||
if is_iter(value):
|
||||
value = ",".join([str(val).strip().lower() for val in value])
|
||||
self.db_group_permissions = value
|
||||
self.save()
|
||||
#@group_permissions.deleter
|
||||
def group_permissions_del(self):
|
||||
"Deleter. Allows for del self.name"
|
||||
self.db_group_permissions = ""
|
||||
self.save()
|
||||
group_permissions = property(group_permissions_get, group_permissions_set, group_permissions_del)
|
||||
|
||||
# permissions property
|
||||
#@property
|
||||
def permissions_get(self):
|
||||
"Getter. Allows for value = self.name. Returns a list of permissions."
|
||||
if self.db_permissions:
|
||||
return [perm.strip() for perm in self.db_permissions.split(',')]
|
||||
return []
|
||||
#@permissions.setter
|
||||
def permissions_set(self, value):
|
||||
"Setter. Allows for self.name = value. Stores as a comma-separated string."
|
||||
if is_iter(value):
|
||||
value = ",".join([str(val).strip().lower() for val in value])
|
||||
self.db_permissions = value
|
||||
self.save()
|
||||
#@permissions.deleter
|
||||
def permissions_del(self):
|
||||
"Deleter. Allows for del self.name"
|
||||
self.db_permissions = ""
|
||||
self.save()
|
||||
permissions = property(permissions_get, permissions_set, permissions_del)
|
||||
|
||||
|
||||
class Meta:
|
||||
"Define Django meta options"
|
||||
verbose_name = "Permission Group"
|
||||
verbose_name_plural = "Permission Groups"
|
||||
|
||||
#
|
||||
# PermissionGroup help method
|
||||
#
|
||||
#
|
||||
|
||||
def contains(self, permission):
|
||||
"""
|
||||
Checks if the given permission string is defined in the
|
||||
permission group.
|
||||
"""
|
||||
return permission in self.group_permissions
|
||||
|
|
@ -1,643 +0,0 @@
|
|||
"""
|
||||
Permissions
|
||||
|
||||
Evennia's permission system can be as coarse or fine-grained as desired.
|
||||
|
||||
**Note: Django's in-built permission checking is still used for web-related access
|
||||
to the admin site etc. This uses the normal django permission
|
||||
strings of the form 'appname.permstring' and automatically adds three of them
|
||||
for each model - for src/object this would be for example
|
||||
'object.create', 'object.admin' and 'object.edit'. Checking and permissions
|
||||
are done completely separate from the present mud permission system, see
|
||||
Django manuals. **
|
||||
|
||||
|
||||
|
||||
Almost all engine elements (Commands, Players, Objects, Scripts,
|
||||
Channels) have a field or variable called 'permissions' - this holds a
|
||||
list or a comma-separated string of 'permission strings'.
|
||||
|
||||
Whenever a permission is to be checked there is always an
|
||||
'Accessing object' and an 'Accessed object'. The 'permissions' field
|
||||
on the two objects are matched against each other. A permission field
|
||||
can contain one of two types of strings - 'keys' and 'locks' and the
|
||||
accessing object's keys are matched against the accessing object's locks
|
||||
depending on which type of access is required. The check function
|
||||
has_perm (in this module) is used to handle the check.
|
||||
|
||||
access_obj.permissions (keys) ---> | |
|
||||
lock_type ---> | has_perm() | ---> False/True
|
||||
accessed_obj.permissions (locks) ---> | |
|
||||
|
||||
If there are no locks (of the right type) available on accessed_obj, access is
|
||||
granted by default. You can however give an argument to has_perm() to reverse
|
||||
this policy to one where default is no access until explicitly granted.
|
||||
|
||||
|
||||
- Keys
|
||||
|
||||
Keys are simple strings that can contain any alphanumerical symbol and
|
||||
be of any length. They are case-sensitive. Only whitespace, colons(:) and
|
||||
commas (,) are not allowed in keys.
|
||||
|
||||
examples:
|
||||
myperm
|
||||
look_perm
|
||||
Builders
|
||||
obj.create (this is what a general Django key looks like)
|
||||
|
||||
|
||||
- Locks
|
||||
|
||||
A lock always consists of two parts separated by a colon (:). A lock must nowhere
|
||||
contain commas (,) since these are used as separators between different locks and keys
|
||||
during storage.
|
||||
|
||||
-type header- -lock body-
|
||||
type1 type2 FLAG:string1 string2 string3()
|
||||
|
||||
type header
|
||||
|
||||
The first part is the 'type' of lock, i.e. what kind of functionality
|
||||
this lock regulates. The function has_perm() takes this as an argument and uses it to sort
|
||||
out which locks are checked (if any).
|
||||
A type name is not case sensitive but may not contain any whitespace (or colons). Use
|
||||
whitespaces to expand the number of types this lock represents.
|
||||
The last word in the type header may be a special flag that regulates how the second
|
||||
part of the lock is handled, especially if the second part is a space-separated list
|
||||
of permissions:
|
||||
- OR or ANY (or none, default) - ANY one match in list means that the entire lock is passed.
|
||||
- AND or ALL - ALL permissions in list must be matched by accessing obj for lock to pass
|
||||
- NOT - inverses the lock. A matched lock means False and vice versa.
|
||||
|
||||
lock body
|
||||
|
||||
The second part, after the comma, is the function body of the lock. The function body
|
||||
can look in several ways:
|
||||
- it could be a permission string, to be matched directly with a key
|
||||
- it could be a function call to a function defined in a safe module (this returns True/False)
|
||||
- it could be a space-separated list of any combination of the above.
|
||||
- it could be one of the non-passable keywords FALSE, LOCK or IMPASSABLE (can be put in a
|
||||
list too, but the list will then always return False, so they are usually
|
||||
used on their own).
|
||||
|
||||
examples:
|
||||
|
||||
look:look_perm
|
||||
|
||||
this lock is of type 'look' and simply checks if the accessing_obj has
|
||||
they key look_perm or not.
|
||||
|
||||
add edit:Builders
|
||||
|
||||
this lock regulates two lock types, 'add' and 'edit'. In both cases,
|
||||
having the key 'Builders' will pass the lock.
|
||||
|
||||
add edit:Builders Admin
|
||||
|
||||
this is like above, but it is passed if the accessing object has either
|
||||
the key Builders or the key Admin (or both)
|
||||
|
||||
system NOT: Untrusted
|
||||
|
||||
this lock of type 'system' is passed if the accessing object does NOT have
|
||||
the key 'Untrusted', since the NOT flag inverses the lock result.
|
||||
|
||||
delete:FALSE
|
||||
|
||||
this lock, of type 'delete' can never be passed since the keyword FALSE
|
||||
is always False (same with LOCK and IMPASSABLE).
|
||||
|
||||
open:
|
||||
|
||||
this lock has an empty lock body and is an always passable lock. Unless you
|
||||
change how has_perm() behaves you can usually just completely skip defining
|
||||
this lock to get the same effect.
|
||||
|
||||
enter:has_key()
|
||||
|
||||
this 'enter'-type lock might for example be placed on a door. The lock body
|
||||
is a function has_key() defined in a module set in the settings. See below
|
||||
for more info on lock functions. It is up the admin to create
|
||||
these lock functions ahead of time as needed for the game.
|
||||
|
||||
unlock:has_pickpocket_lvl(4,difficulty=7)
|
||||
|
||||
function locks can even take arguments, in this case it calls a function
|
||||
has_pick() with info on how hard the lock is.
|
||||
|
||||
delete AND: Builders can_delete has_credits()
|
||||
|
||||
Since the keyword AND is set (ALL would also work), the
|
||||
accessing object must have both the 'Builders', 'can_delete'
|
||||
*and* the function lock 'has_credits()' in order to pass the lock.
|
||||
|
||||
- More on functional locks
|
||||
|
||||
A function lock goes into the second part of a lock definition (after the colon).
|
||||
|
||||
Syntax:
|
||||
funcname(arg3, arg4, ...)
|
||||
arg1 == always accessing_obj
|
||||
arg2 == always accessed_obj
|
||||
|
||||
A function lock is called just like any Python function and should be defined in
|
||||
the same way. The system searches for the first matching function in one of the
|
||||
modules defined by settings.PERMISSION_FUNC_MODULES and executes it.
|
||||
|
||||
Accessing_object and Accessed_object are always passed, followed by the arguments
|
||||
and keywords supplied in the permission. The function should return a boolean and
|
||||
must make sure to handle any possible type of objects being passed in
|
||||
the first two arguments (such as Command objects, Scripts or whatever). There is a
|
||||
convenience function available in this module (get_types) for doing just that.
|
||||
|
||||
examples:
|
||||
'is_stronger_than(40)'
|
||||
'is_suitably_dressed()'
|
||||
'has_lockpick_higher_than(24, difficulty=4)'
|
||||
'has_attr_value('mood', 'happy')
|
||||
|
||||
|
||||
- Permissions string
|
||||
|
||||
As mentioned, keys and locks are stored together on an object in a string
|
||||
called 'permissions'. You are most likely to come into contact with this
|
||||
when defining new Commands.
|
||||
|
||||
This is a comma-separated string (which is why commas
|
||||
are not allowed in keys nor in locks). Keys and Locks can appear in any
|
||||
order in the permission string, they are easily separated by the colon
|
||||
appearing only in locks.
|
||||
|
||||
example:
|
||||
|
||||
'call ALL:can_edit Builders'
|
||||
|
||||
this might be a Command permission and has only one lock on it (a command usually have no need
|
||||
for keys of its own) but two requirements to be able to call it: The accessing object
|
||||
(probably a player) must have both the can_edit and Builders keys (one is not enough
|
||||
since the ALL flag is set).
|
||||
|
||||
'Builders, edit_channels, control:has_id(45)'
|
||||
|
||||
this permission string could sit on a Character object. It has two keys and one lock.
|
||||
The keys tell us Character is a Builder and also has the edit_channels permission.
|
||||
The lock is checked when someone tries to gain 'control' over this object (whatever
|
||||
this means in your game). This lock calls the function has_id() with the argument 45.
|
||||
We can't see here what has_id() actually does, but a likely implementation would
|
||||
be to return True only if the accessing object actually has an id of 45.
|
||||
|
||||
|
||||
- Reserved Server lock types
|
||||
|
||||
The Evennia server (i.e. everything in /src) tries to 'outsource' as many permission checks
|
||||
as possible to what the admin can customize in /game. All the admin needs to do is import
|
||||
has_perm() from this module to use the system. As seen above, the actual syntax of
|
||||
locks and keys above gives the admin much freedom in designing their own system.
|
||||
|
||||
That said, there are a few actions that cannot be outsourced due to technical reasons.
|
||||
In these situations the server must hard-code its permission checks. What this means
|
||||
is that it always calls has_perm() with a specific lock-type keyword that you cannot change. For
|
||||
example it always checks if a command may be accessed by matching the calling player's
|
||||
keys against command-locks of type 'call'. Below you will find all hard-coded lock types
|
||||
the server checks and when it does it.
|
||||
|
||||
locktype entity situation
|
||||
|
||||
call Command when a player tries to call a
|
||||
command. Determines if the command is available
|
||||
at all. Also determines if command will be
|
||||
visible in help lists etc.
|
||||
|
||||
chan_send Channels
|
||||
chan_listen "
|
||||
|
||||
traverse Exits
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
from src.permissions.models import PermissionGroup
|
||||
from src.utils import logger
|
||||
from src.utils.utils import is_iter
|
||||
|
||||
IMPASSABLE = ['FALSE', 'LOCK', 'IMPASSABLE']
|
||||
ORFLAGS = ['OR', 'ANY']
|
||||
ANDFLAGS = ['AND', 'ALL']
|
||||
NOTFLAGS = ['NOT']
|
||||
|
||||
LOCK_FUNC_PATHS = settings.PERMISSION_FUNC_MODULES
|
||||
CACHED_FUNC_MODULES = []
|
||||
|
||||
PARENTS = {
|
||||
"command":"src.commands.command.Command",
|
||||
"msg":"src.comms.models.Msg",
|
||||
"channel":"src.comms.models.Channel",
|
||||
"help":"src.help.models.HelpEntry",
|
||||
"typeclass":"src.typeclasses.typeclass.TypeClass",
|
||||
"script":"src.scripts.models.ScriptDB",
|
||||
"object":"src.objects.models.ObjectDB",
|
||||
"player":"src.players.models.Player"}
|
||||
|
||||
#
|
||||
# Utility functions for handling permissions
|
||||
#
|
||||
|
||||
def perm_to_list(permissions):
|
||||
"""
|
||||
Process a permstring and return a list
|
||||
|
||||
permissions - can be a list of permissions, a permission string
|
||||
or a comma-separated string of permissions.
|
||||
"""
|
||||
if not permissions:
|
||||
return []
|
||||
if not is_iter(permissions):
|
||||
# convert input to a list
|
||||
permissions = str(permissions)
|
||||
if ',' in permissions:
|
||||
permissions = permissions.split(',')
|
||||
else:
|
||||
permissions = [permissions]
|
||||
p_nested = []
|
||||
for nested_perm in (p for p in permissions if ',' in p):
|
||||
# handling eventual weird nested comma-separated
|
||||
# permissions inside lists
|
||||
p_nested.extend(nested_perm.split(','))
|
||||
permissions.extend(p_nested)
|
||||
|
||||
# merge units with unmatched parenthesis (so e.g. '(a,b,c,d)' are not
|
||||
# split by comma separation (this allows for functional locks with
|
||||
# multiple arguments, like 'has_attr(attrname, attrvalue)'). This
|
||||
# also removes all whitespace in the parentheses to avoid confusion later.
|
||||
|
||||
lparents = 0
|
||||
rparents = 0
|
||||
in_block = False
|
||||
perm_list = []
|
||||
for perm in permissions:
|
||||
lparents += perm.count('(')
|
||||
rparents += perm.count(')')
|
||||
if lparents > rparents:
|
||||
# unmatched left-parenthesis - trigger block preservation
|
||||
if not in_block:
|
||||
# beginning of block
|
||||
in_block = True
|
||||
perm_list.append(perm)
|
||||
else:
|
||||
# in block
|
||||
block = perm_list.pop()
|
||||
perm_list.append(",".join([block.strip(), perm.strip()]))
|
||||
elif in_block:
|
||||
# parentheses match again - end the block
|
||||
in_block = False
|
||||
block = perm_list.pop()
|
||||
perm_list.append(",".join([block.strip(), perm.strip()]))
|
||||
else:
|
||||
# no parenthesis/block
|
||||
perm_list.append(perm.strip())
|
||||
return perm_list
|
||||
|
||||
def set_perm(obj, new_perms, replace=True):
|
||||
"""
|
||||
Set a permission string on an object.
|
||||
The permissions given by default replace the old one.
|
||||
If 'replace' is unset, the new one will be appended to
|
||||
the old ones.
|
||||
|
||||
obj - object to receive permission. must have field/variable
|
||||
named 'permissions' for this to work.
|
||||
new_perms - a permission string, a list of permission strings
|
||||
or a comma-separated string of permissions
|
||||
replace - clear and replace old permission completely
|
||||
|
||||
Note - this is also how you add an entity to a group
|
||||
"""
|
||||
try:
|
||||
# get the old permissions if possible
|
||||
obj_perms = perm_to_list(obj.permissions)
|
||||
except AttributeError:
|
||||
# this object cannot get a permission
|
||||
return False
|
||||
new_perms = perm_to_list(new_perms)
|
||||
if replace:
|
||||
# replace permissions completely
|
||||
obj_perms = new_perms
|
||||
else:
|
||||
# extend the old permissions with the new ones
|
||||
for newperm in (perm for perm in new_perms if perm not in obj_perms):
|
||||
obj_perms.append(newperm)
|
||||
# set on object as comma-separated list.
|
||||
obj.permissions = ",".join(str(perm).strip() for perm in obj_perms)
|
||||
try:
|
||||
# E.g. Commands are not db-connected and cannot save,
|
||||
# so we ignore errors here.
|
||||
obj.save()
|
||||
except Exception:
|
||||
pass
|
||||
return True
|
||||
|
||||
def add_perm(obj, add_perms):
|
||||
"""
|
||||
Convenience function to add a permission to an entity.
|
||||
"""
|
||||
return set_perm(obj, add_perms, replace=False)
|
||||
|
||||
def del_perm(obj, del_perms):
|
||||
"""
|
||||
Remove permission from object (if possible)
|
||||
"""
|
||||
try:
|
||||
obj_perms = perm_to_list(obj.permissions)
|
||||
except AttributeError:
|
||||
return False
|
||||
del_perms = perm_to_list(del_perms)
|
||||
obj_perms = [perm for perm in obj_perms if perm not in del_perms]
|
||||
obj.permissions = ",".join(str(perm) for perm in obj_perms)
|
||||
try:
|
||||
obj.save()
|
||||
except Exception:
|
||||
pass
|
||||
return True
|
||||
|
||||
def get_types(accessing_obj, accessed_obj):
|
||||
"""
|
||||
A convenience function for determining what type the objects are.
|
||||
This is intended for easy importing into the modules
|
||||
defined in LOCK_FUNC_PATHS.
|
||||
"""
|
||||
|
||||
def has_parent(basepath, obj):
|
||||
"Checks if basepath is somewhere is objs parent tree."
|
||||
return any(cls for cls in obj.__class__.mro()
|
||||
if basepath == "%s.%s" % (cls.__module__, cls.__name__))
|
||||
|
||||
checking_type = None
|
||||
checked_type = None
|
||||
try:
|
||||
checking_type = [parent for parent, path in PARENTS.items()
|
||||
if has_parent(path, accessing_obj)][0]
|
||||
if checking_type == 'typeclass':
|
||||
checking_type = [parent for parent, path in PARENTS.items()
|
||||
if has_parent(path, accessing_obj.db)][0]
|
||||
except IndexError:
|
||||
logger.log_trace("LockFunc: %s is not of a valid type."
|
||||
% accessing_obj)
|
||||
raise
|
||||
try:
|
||||
checked_type = [parent for parent, path in PARENTS.items()
|
||||
if has_parent(path, accessed_obj)][0]
|
||||
if checking_type == 'typeclass':
|
||||
checked_type = [parent for parent, path in PARENTS.items()
|
||||
if has_parent(path, accessed_obj.db)][0]
|
||||
except IndexError:
|
||||
logger.log_trace("LockFunc: %s is not of a valid type."
|
||||
% accessed_obj)
|
||||
raise
|
||||
return (checking_type, checked_type)
|
||||
|
||||
|
||||
#
|
||||
# helper functions for has_perm()
|
||||
#
|
||||
|
||||
|
||||
def append_group_permissions(keylist):
|
||||
"""
|
||||
Step through keylist and discover if
|
||||
one the keys match a permission group name
|
||||
(case sensitive). In that case, go into
|
||||
that group and add its permissions to
|
||||
the keylist.
|
||||
"""
|
||||
groups = []
|
||||
for match_key in keylist:
|
||||
try:
|
||||
groups.append(PermissionGroup.objects.get(db_key=match_key))
|
||||
except Exception:
|
||||
pass
|
||||
for group in groups:
|
||||
keylist.extend(perm_to_list(group.group_permissions))
|
||||
return list(set(keylist)) # set makes elements of keylist unique
|
||||
|
||||
def try_impassable(lockdefs):
|
||||
"""
|
||||
Test if this list of lockdefs is impassable.
|
||||
"""
|
||||
return any(True for lockdef in lockdefs if lockdef in IMPASSABLE)
|
||||
|
||||
def try_key_lock(iflag, keylist, lockdefs):
|
||||
"""
|
||||
Test a direct-comparison match between keys and lockstrings.
|
||||
Returns the number of matches found.
|
||||
"""
|
||||
if iflag in ANDFLAGS:
|
||||
return len([True for key in keylist if key in lockdefs])
|
||||
elif iflag in NOTFLAGS:
|
||||
return not any(True for key in keylist if key in lockdefs)
|
||||
else:
|
||||
return any(True for key in keylist if key in lockdefs)
|
||||
|
||||
def try_functional_lock(lflag, lockdefs, accessing_obj, accessed_obj):
|
||||
"""
|
||||
Functional lock
|
||||
|
||||
lockdefs is a list of permission strings (called by check_lock)
|
||||
"""
|
||||
global CACHED_FUNC_MODULES
|
||||
|
||||
if not CACHED_FUNC_MODULES:
|
||||
# Cache the imported func module(s) once and for all
|
||||
# so we don't have to re-import them for every call.
|
||||
# We allow for LOCK_FUNC_PATHS to be a tuple of
|
||||
# paths as well.
|
||||
CACHED_FUNC_MODULES = []
|
||||
try:
|
||||
module_paths = list(LOCK_FUNC_PATHS)
|
||||
except Exception:
|
||||
module_paths = [LOCK_FUNC_PATHS]
|
||||
for path in module_paths:
|
||||
try:
|
||||
CACHED_FUNC_MODULES.append(__import__(path, fromlist=[True]))
|
||||
except ImportError:
|
||||
logger.log_trace("lock func: import error for '%s'" % path)
|
||||
continue
|
||||
|
||||
# try to execute functions, if they exist
|
||||
#print "locklist:", locklist
|
||||
passed_locks = 0
|
||||
for lockstring in lockdefs:
|
||||
if not lockstring \
|
||||
or not ('(' in lockstring and ')' in lockstring) \
|
||||
or not (lockstring.find('(') < lockstring.find(')')):
|
||||
# must be a valid function() call
|
||||
continue
|
||||
funcname, args = (str(part).strip() for part in lockstring.split('(', 1))
|
||||
args = args.rstrip(')').split(',')
|
||||
kwargs = [kwarg for kwarg in args if '=' in kwarg]
|
||||
args = tuple([arg for arg in args if arg not in kwargs])
|
||||
kwargs = dict([(key.strip(), value.strip()) for key, value in [kwarg.split('=', 1) for kwarg in kwargs]])
|
||||
#print "%s '%s'" % (funcname, args)
|
||||
for module in CACHED_FUNC_MODULES:
|
||||
# step through all safe modules, executing the first one that matches
|
||||
lockfunc = module.__dict__.get(funcname, None)
|
||||
if callable(lockfunc):
|
||||
try:
|
||||
# try the lockfunction.
|
||||
if lockfunc(accessing_obj, accessed_obj, *args, **kwargs):
|
||||
if lflag in ANDFLAGS:
|
||||
passed_locks += 1
|
||||
elif lflag in NOTFLAGS:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
except Exception:
|
||||
continue
|
||||
return passed_locks
|
||||
|
||||
|
||||
#------------------------------------------------------------
|
||||
# has_perm & has_perm_string : main access functions
|
||||
#------------------------------------------------------------
|
||||
|
||||
def has_perm(accessing_obj, accessed_obj, lock_type, default_deny=False):
|
||||
"""
|
||||
The main access method of the permission system. Note that
|
||||
this will not perform checks against django's in-built permission
|
||||
system, that is assumed to be done in the calling function
|
||||
after this method returns False.
|
||||
|
||||
accessing_obj - the object trying to gain access
|
||||
accessed_obj - the object being checked for access
|
||||
lock_type - Only locks of this type 'lock_type:permissionstring'
|
||||
will be considered for a match.
|
||||
default_deny - Normally, if no suitable locks are found on the object, access
|
||||
is granted by default. This switch changes security policy to
|
||||
instead lock down the object unless access is explicitly given.
|
||||
"""
|
||||
|
||||
# Get the list of locks of the accessed_object
|
||||
|
||||
try:
|
||||
locklist = [lock for lock in perm_to_list(accessed_obj.permissions) if ':' in lock]
|
||||
except AttributeError:
|
||||
# This is not a valid permissable object at all
|
||||
logger.log_trace()
|
||||
return False
|
||||
|
||||
# filter the locks to find only those of the correct lock_type. This creates
|
||||
# a list with elements being tuples (flag, [lockdef, lockdef, ...])
|
||||
|
||||
lock_type = lock_type.lower()
|
||||
locklist = [(typelist[-1].strip(), [lo.strip() for lo in lock.split(None)])
|
||||
for typelist, lock in [(ltype.split(None), lock)
|
||||
for ltype, lock in [lock.split(':', 1)
|
||||
for lock in locklist]]
|
||||
if typelist and lock_type in typelist]
|
||||
|
||||
if not locklist or not any(locklist):
|
||||
# No viable locks; use default security policy
|
||||
return not default_deny
|
||||
|
||||
# we have locks of the right type. Set default flag OR on all that
|
||||
# don't explictly specify a flag (AND, OR, NOT). These flags determine
|
||||
# how the permstrings in the lock definition should relate to one another.
|
||||
locktemp = []
|
||||
for ltype, lock in locklist:
|
||||
if ltype not in ANDFLAGS and ltype not in NOTFLAGS:
|
||||
ltype = 'OR'
|
||||
locktemp.append((ltype, lock))
|
||||
locklist = locktemp
|
||||
|
||||
# Get the list of keys of the accessing_object
|
||||
|
||||
keylist = []
|
||||
|
||||
#print "testing %s" % accessing_obj.__class__
|
||||
if hasattr(accessing_obj, 'is_superuser') and accessing_obj.is_superuser:
|
||||
# superusers always have access.
|
||||
return True
|
||||
|
||||
# try to add permissions from connected player
|
||||
if hasattr(accessing_obj, 'has_player') and accessing_obj.has_player:
|
||||
# accessing_obj has a valid, connected player. We start with those permissions.
|
||||
player = accessing_obj.player
|
||||
if player.is_superuser:
|
||||
# superuser always has access
|
||||
return True
|
||||
try:
|
||||
keylist.extend([perm for perm in perm_to_list(player.permissions)
|
||||
if not ':' in perm])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# next we get the permissions directly from accessing_obj.
|
||||
try:
|
||||
keylist.extend([perm for perm in perm_to_list(accessing_obj.permissions)
|
||||
if not ':' in perm])
|
||||
except Exception:
|
||||
# not a valid permissable object
|
||||
return False
|
||||
# expand also with group permissions, if any
|
||||
keylist = append_group_permissions(keylist)
|
||||
|
||||
#print "keylist: %s" % keylist
|
||||
|
||||
|
||||
# Test permissions against the locks
|
||||
|
||||
|
||||
for lflag, lockdefs in locklist:
|
||||
|
||||
# impassable locks normally shuts down the entire operation right away.
|
||||
if try_impassable(lockdefs):
|
||||
return lflag in NOTFLAGS
|
||||
|
||||
# test direct key-lock comparison and functional locks
|
||||
if lflag in ANDFLAGS:
|
||||
# with the AND flag, we have to match all lockdefs to pass the lock
|
||||
passed_locks = try_key_lock(lflag, keylist, lockdefs)
|
||||
passed_locks += try_functional_lock(lflag, lockdefs, accessing_obj, accessed_obj)
|
||||
if passed_locks == len(lockdefs):
|
||||
return True
|
||||
else:
|
||||
# normal operation; any match passes the lock
|
||||
if try_key_lock(lflag, keylist, lockdefs):
|
||||
return True
|
||||
if try_functional_lock(lflag, lockdefs, accessing_obj, accessed_obj):
|
||||
return True
|
||||
# If we fall through to here, we don't have access
|
||||
return False
|
||||
|
||||
def has_perm_string(accessing_obj, lock_list, default_deny=False):
|
||||
"""
|
||||
This tests the given accessing object against the
|
||||
given string rather than a particular accessing object.
|
||||
|
||||
OBS: Be careful if supplying function locks to this method since
|
||||
there is no meaningful accessed_obj present (the one fed to
|
||||
the function is just a dummy).
|
||||
|
||||
accessing_obj - the object being checked for permissions
|
||||
lock_list - a list or a comma-separated string of lock definitions.
|
||||
default_deny - Normally, if no suitable locks are found on the object, access
|
||||
is granted by default. This switch changes security policy to
|
||||
instead lock down the object unless access is explicitly given.
|
||||
"""
|
||||
|
||||
# prepare the permissions we want, so it's properly stripped etc.
|
||||
class Dummy(object):
|
||||
"Dummy object"
|
||||
def __init__(self, permissions):
|
||||
self.permissions = permissions
|
||||
|
||||
if not is_iter(lock_list):
|
||||
lock_list = [perm for perm in lock_list.split(',')]
|
||||
lockstring = ",".join(["dummy:%s" % perm.strip() for perm in lock_list if perm.strip()])
|
||||
|
||||
# prepare a dummy object with the permissions
|
||||
accessed_obj = Dummy(lockstring)
|
||||
# call has_perm normally with the dummy object.
|
||||
return has_perm(accessing_obj, accessed_obj, 'dummy', default_deny)
|
||||
|
|
@ -47,7 +47,6 @@ from django.utils.encoding import smart_str
|
|||
from src.server.sessionhandler import SESSIONS
|
||||
from src.players import manager
|
||||
from src.typeclasses.models import Attribute, TypedObject
|
||||
from src.permissions import permissions
|
||||
from src.utils import logger
|
||||
|
||||
#------------------------------------------------------------
|
||||
|
|
@ -105,7 +104,6 @@ class PlayerDB(TypedObject):
|
|||
|
||||
"""
|
||||
|
||||
|
||||
#
|
||||
# PlayerDB Database model setup
|
||||
#
|
||||
|
|
@ -237,16 +235,6 @@ class PlayerDB(TypedObject):
|
|||
return self.user.is_superuser
|
||||
is_superuser = property(is_superuser_get)
|
||||
|
||||
def set_perm(self, perm):
|
||||
"Shortcuts to set permissions, replacing old ones"
|
||||
return permissions.set_perm(self, perm)
|
||||
def add_perm(self, perm):
|
||||
"Add permissions to the old ones"
|
||||
return permissions.add_perm(self, perm)
|
||||
def del_perm(self, perm):
|
||||
"Delete permission from old ones"
|
||||
return permissions.del_perm(self, perm)
|
||||
|
||||
#
|
||||
# PlayerDB class access methods
|
||||
#
|
||||
|
|
|
|||
|
|
@ -27,7 +27,13 @@ class Player(TypeClass):
|
|||
"""
|
||||
# the text encoding to use.
|
||||
self.db.encoding = "utf-8"
|
||||
pass
|
||||
|
||||
# A basic security setup
|
||||
self.locks.add("examine:perm(Wizards)")
|
||||
self.locks.add("edit:perm(Wizards)")
|
||||
self.locks.add("delete:perm(Wizards)")
|
||||
self.locks.add("boot:perm(Wizards)")
|
||||
self.locks.add("msg:all()")
|
||||
|
||||
# Note that the hooks below also exist
|
||||
# in the character object's typeclass. You
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ from django.conf import settings
|
|||
from django.db import models
|
||||
from src.typeclasses.models import Attribute, TypedObject
|
||||
from src.scripts.manager import ScriptManager
|
||||
#from src.locks.lockhandler import LockHandler
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
|
|
@ -104,7 +105,7 @@ class ScriptDB(TypedObject):
|
|||
|
||||
# Database manager
|
||||
objects = ScriptManager()
|
||||
|
||||
|
||||
class Meta:
|
||||
"Define Django meta options"
|
||||
verbose_name = "Script"
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ def create_objects():
|
|||
user=god_user)
|
||||
god_character.id = 1
|
||||
god_character.db.desc = 'This is User #1.'
|
||||
god_character.locks.add("examine:perm(Immortals);edit:false();delete:false();boot:false();msg:all()")
|
||||
|
||||
god_character.save()
|
||||
|
||||
# Limbo is the initial starting room.
|
||||
|
|
@ -99,14 +101,14 @@ def create_channels():
|
|||
print " Creating default channels ..."
|
||||
|
||||
# public channel
|
||||
key, aliases, desc, perms = settings.CHANNEL_PUBLIC
|
||||
pchan = create.create_channel(key, aliases, desc, perms)
|
||||
key, aliases, desc, locks = settings.CHANNEL_PUBLIC
|
||||
pchan = create.create_channel(key, aliases, desc, locks=locks)
|
||||
# mudinfo channel
|
||||
key, aliases, desc, perms = settings.CHANNEL_MUDINFO
|
||||
ichan = create.create_channel(key, aliases, desc, perms)
|
||||
key, aliases, desc, locks = settings.CHANNEL_MUDINFO
|
||||
ichan = create.create_channel(key, aliases, desc, locks=locks)
|
||||
# connectinfo channel
|
||||
key, aliases, desc, perms = settings.CHANNEL_CONNECTINFO
|
||||
cchan = create.create_channel(key, aliases, desc, perms)
|
||||
key, aliases, desc, locks = settings.CHANNEL_CONNECTINFO
|
||||
cchan = create.create_channel(key, aliases, desc, locks=locks)
|
||||
|
||||
# connect the god user to all these channels by default.
|
||||
goduser = get_god_user()
|
||||
|
|
@ -126,33 +128,6 @@ def import_MUX_help_files():
|
|||
print " Moving imported help db to help category '%s'." \
|
||||
% default_category
|
||||
HelpEntry.objects.all_to_category(default_category)
|
||||
|
||||
def create_permission_groups():
|
||||
"""
|
||||
This sets up the default permissions groups
|
||||
by parsing a permission config file.
|
||||
|
||||
Note that we don't catch any exceptions here,
|
||||
this must be debugged until it works.
|
||||
"""
|
||||
|
||||
print " Creating and setting up permissions/groups ..."
|
||||
|
||||
# We try to get the data from config first.
|
||||
setup_path = settings.PERMISSION_SETUP_MODULE
|
||||
if not setup_path:
|
||||
# go with the default
|
||||
setup_path = "src.permissions.default_permissions"
|
||||
module = __import__(setup_path, fromlist=[True])
|
||||
# We have a successful import. Get the dicts.
|
||||
groupdict = module.GROUPS
|
||||
|
||||
# Create groups and populate them
|
||||
for group in groupdict:
|
||||
group = create.create_permission_group(group, desc=group,
|
||||
group_perms=groupdict[group])
|
||||
if not group:
|
||||
print " Creation of Group '%s' failed." % group
|
||||
|
||||
def create_system_scripts():
|
||||
"""
|
||||
|
|
@ -231,7 +206,6 @@ def handle_setup(last_step):
|
|||
create_connect_screens,
|
||||
create_objects,
|
||||
create_channels,
|
||||
create_permission_groups,
|
||||
create_system_scripts,
|
||||
import_MUX_help_files,
|
||||
start_game_time,
|
||||
|
|
@ -240,7 +214,7 @@ def handle_setup(last_step):
|
|||
if not settings.IMPORT_MUX_HELP:
|
||||
# skip importing of the MUX helpfiles, they are
|
||||
# not interesting except for developers.
|
||||
del setup_queue[6]
|
||||
del setup_queue[5]
|
||||
|
||||
#print " Initial setup: %s steps." % (len(setup_queue))
|
||||
|
||||
|
|
|
|||
|
|
@ -102,7 +102,10 @@ class TelnetProtocol(StatefulTelnetProtocol, session.Session):
|
|||
def at_disconnect(self, reason="Connection closed. Goodbye for now."):
|
||||
"""
|
||||
Disconnect from server
|
||||
"""
|
||||
"""
|
||||
char = self.get_character()
|
||||
if char:
|
||||
char.at_disconnect()
|
||||
self.at_data_out(reason)
|
||||
self.connectionLost(step=2)
|
||||
|
||||
|
|
|
|||
|
|
@ -246,6 +246,9 @@ class WebClientSession(session.Session):
|
|||
"""
|
||||
if reason:
|
||||
self.lineSend(self.suid, reason)
|
||||
char = self.get_character()
|
||||
if char:
|
||||
char.at_disconnect()
|
||||
self.client.disconnect(self.suid, step=2)
|
||||
|
||||
def at_data_out(self, string='', data=None):
|
||||
|
|
|
|||
|
|
@ -224,50 +224,36 @@ TIME_MONTH_PER_YEAR = 12
|
|||
|
||||
|
||||
###################################################
|
||||
# Game Permissions
|
||||
# In-Game access
|
||||
###################################################
|
||||
|
||||
# The module where the base permissions and
|
||||
# groups are defined (read only once the very
|
||||
# first time the server starts). If not set,
|
||||
# src/permissions/permissions_setup.py is used.
|
||||
PERMISSION_SETUP_MODULE = ""
|
||||
# By defining a default player group to belong to,
|
||||
# all players may start with some permissions pre-set.
|
||||
# Available groups are set either above, or in
|
||||
# src/permissions/permissions_setup.py.
|
||||
# The access hiearchy, in climbing order. A higher
|
||||
# permission in the hierarchy includes access of all
|
||||
# levels below it.
|
||||
PERMISSION_HIERARCHY = ("Players","PlayerHelpers","Builders", "Wizards", "Immortals")
|
||||
# The default permission given to all new players
|
||||
PERMISSION_PLAYER_DEFAULT = "Players"
|
||||
# Tuple of modules implementing permission lock methods
|
||||
# (see src/permissions/locks.py and
|
||||
# src/permissions/permissions.py)
|
||||
PERMISSION_FUNC_MODULES = ("src.permissions.lockfunc_default",)
|
||||
# Tuple of modules implementing lock functions. All callable functions
|
||||
# inside these modules will be available as lock functions.
|
||||
LOCK_FUNC_MODULES = ("src.locks.lockfuncs",)
|
||||
|
||||
###################################################
|
||||
# In-game Channels created from server start
|
||||
###################################################
|
||||
# Defines a dict with one key for each from-start
|
||||
# channel. Each key points to a tuple containing
|
||||
# (name, aliases, description, permissions)
|
||||
# where aliases may be a tuple too, and permissions
|
||||
# is a comma-separated string of permissions
|
||||
# (see src/permissions/permissions.py)
|
||||
|
||||
# (name, aliases, description, locks)
|
||||
# where aliases may be a tuple too, and locks is
|
||||
# a valid lockstring definition.
|
||||
# Default user channel for communication
|
||||
CHANNEL_PUBLIC = ("Public", 'ooc', 'Public discussion',
|
||||
'''chan_admin:has_id(1),
|
||||
chan_listen:use_channels,
|
||||
chan_send:use_channels''')
|
||||
"admin:perm(Wizards);listen:all();send:all()")
|
||||
# General info about the server
|
||||
CHANNEL_MUDINFO = ("MUDinfo", '', 'Informative messages',
|
||||
'''chan_admin:has_id(1),
|
||||
chan_listen:Immortals,
|
||||
chan_send:Immortals''')
|
||||
"admin:perm(Immortals);listen:perm(Immortals);send:false()")
|
||||
# Channel showing when new people connecting
|
||||
CHANNEL_CONNECTINFO = ("MUDconnections", ('connections, mud_conns'),
|
||||
'Connection log',
|
||||
'''chan_admin:has_id(1),
|
||||
chan_listen:Wizards,
|
||||
chan_send:Wizards''')
|
||||
"admin:perm(Immortals);listen:perm(Wizards);send:false()")
|
||||
|
||||
###################################################
|
||||
# IMC2 Configuration
|
||||
|
|
@ -444,7 +430,6 @@ INSTALLED_APPS = (
|
|||
'src.irc',
|
||||
'src.help',
|
||||
'src.scripts',
|
||||
'src.permissions',
|
||||
'src.web.news',
|
||||
'src.web.website',)
|
||||
# The user profile extends the User object with more functionality;
|
||||
|
|
|
|||
|
|
@ -32,9 +32,12 @@ from django.conf import settings
|
|||
from django.utils.encoding import smart_str
|
||||
from src.utils.idmapper.models import SharedMemoryModel
|
||||
from src.typeclasses import managers
|
||||
from src.locks.lockhandler import LockHandler
|
||||
from src.utils import logger
|
||||
from src.utils.utils import is_iter, has_parent
|
||||
|
||||
PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
|
||||
|
||||
# used by Attribute to efficiently identify stored object types.
|
||||
# Note that these have to be updated if directory structure changes.
|
||||
PARENTS = {
|
||||
|
|
@ -81,7 +84,6 @@ class Attribute(SharedMemoryModel):
|
|||
|
||||
"""
|
||||
|
||||
|
||||
#
|
||||
# Attribute Database Model setup
|
||||
#
|
||||
|
|
@ -94,8 +96,8 @@ class Attribute(SharedMemoryModel):
|
|||
db_value = models.TextField(blank=True, null=True)
|
||||
# tells us what type of data is stored in the attribute
|
||||
db_mode = models.CharField(max_length=20, null=True, blank=True)
|
||||
# permissions to do things to this attribute
|
||||
db_permissions = models.CharField(max_length=255, blank=True)
|
||||
# Lock storage
|
||||
db_lock_storage = models.TextField(blank=True)
|
||||
# references the object the attribute is linked to (this is set
|
||||
# by each child class to this abstact class)
|
||||
db_obj = None # models.ForeignKey("RefencedObject")
|
||||
|
|
@ -105,6 +107,12 @@ class Attribute(SharedMemoryModel):
|
|||
# Database manager
|
||||
objects = managers.AttributeManager()
|
||||
|
||||
# Lock handler self.locks
|
||||
def __init__(self, *args, **kwargs):
|
||||
"Initializes the parent first -important!"
|
||||
SharedMemoryModel.__init__(self, *args, **kwargs)
|
||||
self.locks = LockHandler(self)
|
||||
|
||||
class Meta:
|
||||
"Define Django meta options"
|
||||
abstract = True
|
||||
|
|
@ -152,27 +160,6 @@ class Attribute(SharedMemoryModel):
|
|||
self.save()
|
||||
mode = property(mode_get, mode_set, mode_del)
|
||||
|
||||
# permissions property
|
||||
#@property
|
||||
def permissions_get(self):
|
||||
"Getter. Allows for value = self.permissions. Returns a list of permissions."
|
||||
if self.db_permissions:
|
||||
return [perm.strip() for perm in self.db_permissions.split(',')]
|
||||
return []
|
||||
#@permissions.setter
|
||||
def permissions_set(self, value):
|
||||
"Setter. Allows for self.permissions = value. Stores as a comma-separated string."
|
||||
if is_iter(value):
|
||||
value = ",".join([str(val).strip().lower() for val in value])
|
||||
self.db_permissions = value
|
||||
self.save()
|
||||
#@permissions.deleter
|
||||
def permissions_del(self):
|
||||
"Deleter. Allows for del self.permissions"
|
||||
self.db_permissions = ""
|
||||
self.save()
|
||||
permissions = property(permissions_get, permissions_set, permissions_del)
|
||||
|
||||
# obj property (wraps db_obj)
|
||||
#@property
|
||||
def obj_get(self):
|
||||
|
|
@ -256,6 +243,23 @@ class Attribute(SharedMemoryModel):
|
|||
self.delete()
|
||||
value = property(value_get, value_set, value_del)
|
||||
|
||||
# lock_storage property (wraps db_lock_storage)
|
||||
#@property
|
||||
def lock_storage_get(self):
|
||||
"Getter. Allows for value = self.lock_storage"
|
||||
return self.db_lock_storage
|
||||
#@lock_storage.setter
|
||||
def lock_storage_set(self, value):
|
||||
"""Saves the lock_storage. This is usually not called directly, but through self.lock()"""
|
||||
self.db_lock_storage = value
|
||||
self.save()
|
||||
#@lock_storage.deleter
|
||||
def lock_storage_del(self):
|
||||
"Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead"""
|
||||
logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self)
|
||||
lock_storage = property(lock_storage_get, lock_storage_set, lock_storage_del)
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
# Attribute methods
|
||||
|
|
@ -315,6 +319,15 @@ class Attribute(SharedMemoryModel):
|
|||
#print "type identified: %s" % db_type[0]
|
||||
return str(in_value.id), db_type[0]
|
||||
|
||||
def access(self, accessing_obj, access_type='read', default=False):
|
||||
"""
|
||||
Determines if another object has permission to access.
|
||||
accessing_obj - object trying to access this one
|
||||
access_type - type of access sought
|
||||
default - what to return if no lock of access_type was found
|
||||
"""
|
||||
return self.locks.check(accessing_obj, access_type=access_type, default=default)
|
||||
|
||||
|
||||
|
||||
#------------------------------------------------------------
|
||||
|
|
@ -360,10 +373,18 @@ class TypedObject(SharedMemoryModel):
|
|||
db_date_created = models.DateTimeField(editable=False, auto_now_add=True)
|
||||
# Permissions (access these through the 'permissions' property)
|
||||
db_permissions = models.CharField(max_length=512, blank=True)
|
||||
|
||||
# Lock storage
|
||||
db_lock_storage = models.TextField(blank=True)
|
||||
|
||||
# Database manager
|
||||
objects = managers.TypedObjectManager()
|
||||
|
||||
# lock handler self.locks
|
||||
def __init__(self, *args, **kwargs):
|
||||
"We must initialize the parent first - important!"
|
||||
SharedMemoryModel.__init__(self, *args, **kwargs)
|
||||
self.locks = LockHandler(self)
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
Django setup info.
|
||||
|
|
@ -466,6 +487,24 @@ class TypedObject(SharedMemoryModel):
|
|||
self.save()
|
||||
permissions = property(permissions_get, permissions_set, permissions_del)
|
||||
|
||||
# lock_storage property (wraps db_lock_storage)
|
||||
#@property
|
||||
def lock_storage_get(self):
|
||||
"Getter. Allows for value = self.lock_storage"
|
||||
return self.db_lock_storage
|
||||
#@lock_storage.setter
|
||||
def lock_storage_set(self, value):
|
||||
"""Saves the lock_storagetodate. This is usually not called directly, but through self.lock()"""
|
||||
self.db_lock_storage = value
|
||||
self.save()
|
||||
#@lock_storage.deleter
|
||||
def lock_storage_del(self):
|
||||
"Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead"""
|
||||
logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self)
|
||||
lock_storage = property(lock_storage_get, lock_storage_set, lock_storage_del)
|
||||
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
# TypedObject main class methods and properties
|
||||
|
|
@ -502,7 +541,6 @@ class TypedObject(SharedMemoryModel):
|
|||
# typeclass' __getattribute__, since that one would
|
||||
# try to look back to this very database object.)
|
||||
typeclass = object.__getattribute__(self, 'typeclass')
|
||||
#typeclass = object.__getattribute__(self, 'typeclass')
|
||||
#print " '%s' not on db --> Checking typeclass %s instead." % (propname, typeclass)
|
||||
if typeclass:
|
||||
return object.__getattribute__(typeclass(self), propname)
|
||||
|
|
@ -550,7 +588,8 @@ class TypedObject(SharedMemoryModel):
|
|||
cmessage = "\n".join(["[NO MUDINFO CHANNEL]: %s" % line for line in message.split('\n')])
|
||||
logger.log_errmsg(cmessage)
|
||||
|
||||
path = self.db_typeclass_path
|
||||
#path = self.db_typeclass_path
|
||||
path = object.__getattribute__(self, 'db_typeclass_path')
|
||||
|
||||
errstring = ""
|
||||
if not path:
|
||||
|
|
@ -967,3 +1006,38 @@ class TypedObject(SharedMemoryModel):
|
|||
"Stop accidental deletion."
|
||||
raise Exception("Cannot delete the ndb object!")
|
||||
ndb = property(ndb_get, ndb_set, ndb_del)
|
||||
|
||||
|
||||
# Lock / permission methods
|
||||
|
||||
def access(self, accessing_obj, access_type='read', default=False):
|
||||
"""
|
||||
Determines if another object has permission to access.
|
||||
accessing_obj - object trying to access this one
|
||||
access_type - type of access sought
|
||||
default - what to return if no lock of access_type was found
|
||||
"""
|
||||
return self.locks.check(accessing_obj, access_type=access_type, default=default)
|
||||
|
||||
def has_perm(self, accessing_obj, access_type):
|
||||
"Alias to access"
|
||||
logger.log_depmsg("has_perm() is deprecated. Use access() instead.")
|
||||
return self.access(accessing_obj, access_type)
|
||||
|
||||
def check_permstring(self, permstring):
|
||||
"""
|
||||
This explicitly checks for we hold particular permission without involving
|
||||
any locks.
|
||||
"""
|
||||
if not permstring:
|
||||
return False
|
||||
perm = permstring.lower()
|
||||
if perm in [p.lower() for p in self.permissions]:
|
||||
# simplest case - we have a direct match
|
||||
return True
|
||||
if perm in PERMISSION_HIERARCHY:
|
||||
# check if we have a higher hierarchy position
|
||||
ppos = PERMISSION_HIERARCHY.index(perm)
|
||||
return any(True for hpos, hperm in enumerate(PERMISSION_HIERARCHY)
|
||||
if hperm in [p.lower() for p in self.permissions] and hpos > ppos)
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ Models covered:
|
|||
Objects
|
||||
Scripts
|
||||
Help
|
||||
PermissionGroup
|
||||
Message
|
||||
Channel
|
||||
Players
|
||||
|
|
@ -22,9 +21,6 @@ Models covered:
|
|||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import IntegrityError
|
||||
|
||||
from src.permissions.permissions import set_perm
|
||||
from src.permissions.models import PermissionGroup
|
||||
from src.utils import logger
|
||||
from src.utils.utils import is_iter, has_parent
|
||||
|
||||
|
|
@ -33,7 +29,7 @@ from src.utils.utils import is_iter, has_parent
|
|||
#
|
||||
|
||||
def create_object(typeclass, key=None, location=None,
|
||||
home=None, player=None, permissions=None, aliases=None):
|
||||
home=None, player=None, permissions=None, locks=None, aliases=None):
|
||||
"""
|
||||
Create a new in-game object. Any game object is a combination
|
||||
of a database object that stores data persistently to
|
||||
|
|
@ -94,18 +90,21 @@ def create_object(typeclass, key=None, location=None,
|
|||
new_object.player = player
|
||||
player.obj = new_object
|
||||
|
||||
if permissions:
|
||||
set_perm(new_object, permissions)
|
||||
if aliases:
|
||||
if not is_iter(aliases):
|
||||
aliases = [aliases]
|
||||
new_object.aliases = ",".join([alias.strip() for alias in aliases])
|
||||
|
||||
# call the hook method. This is where all at_creation
|
||||
# customization happens as the typeclass stores custom
|
||||
# things on its database object.
|
||||
new_object.at_object_creation()
|
||||
|
||||
# custom-given variables override the hook
|
||||
if permissions:
|
||||
new_object.permissions = permissions
|
||||
if aliases:
|
||||
if not is_iter(aliases):
|
||||
aliases = [aliases]
|
||||
new_object.aliases = ",".join([alias.strip() for alias in aliases])
|
||||
if locks:
|
||||
new_object.locks.add(locks)
|
||||
|
||||
# perform a move_to in order to display eventual messages.
|
||||
if home:
|
||||
new_object.home = home
|
||||
|
|
@ -121,7 +120,7 @@ def create_object(typeclass, key=None, location=None,
|
|||
# Script creation
|
||||
#
|
||||
|
||||
def create_script(typeclass, key=None, obj=None, autostart=True):
|
||||
def create_script(typeclass, key=None, obj=None, locks=None, autostart=True):
|
||||
"""
|
||||
Create a new script. All scripts are a combination
|
||||
of a database object that communicates with the
|
||||
|
|
@ -177,15 +176,21 @@ def create_script(typeclass, key=None, obj=None, autostart=True):
|
|||
new_db_object.name = "%s" % typeclass.__name__
|
||||
else:
|
||||
new_db_object.name = "#%i" % new_db_object.id
|
||||
|
||||
# call the hook method. This is where all at_creation
|
||||
# customization happens as the typeclass stores custom
|
||||
# things on its database object.
|
||||
new_script.at_script_creation()
|
||||
|
||||
# custom-given variables override the hook
|
||||
if locks:
|
||||
new_script.locks.add(locks)
|
||||
|
||||
if obj:
|
||||
try:
|
||||
new_script.obj = obj
|
||||
except ValueError:
|
||||
new_script.obj = obj.dbobj
|
||||
# call the hook method. This is where all at_creation
|
||||
# customization happens as the typeclass stores custom
|
||||
# things on its database object.
|
||||
new_script.at_script_creation()
|
||||
|
||||
new_script.save()
|
||||
|
||||
|
|
@ -200,7 +205,7 @@ def create_script(typeclass, key=None, obj=None, autostart=True):
|
|||
# Help entry creation
|
||||
#
|
||||
|
||||
def create_help_entry(key, entrytext, category="General", permissions=None):
|
||||
def create_help_entry(key, entrytext, category="General", locks=None):
|
||||
"""
|
||||
Create a static help entry in the help database. Note that Command
|
||||
help entries are dynamic and directly taken from the __doc__ entries
|
||||
|
|
@ -215,8 +220,8 @@ def create_help_entry(key, entrytext, category="General", permissions=None):
|
|||
new_help.key = key
|
||||
new_help.entrytext = entrytext
|
||||
new_help.help_category = category
|
||||
if permissions:
|
||||
set_perm(new_help, permissions)
|
||||
if locks:
|
||||
new_help.locks.add(locks)
|
||||
new_help.save()
|
||||
return new_help
|
||||
except IntegrityError:
|
||||
|
|
@ -227,51 +232,13 @@ def create_help_entry(key, entrytext, category="General", permissions=None):
|
|||
logger.log_trace()
|
||||
return None
|
||||
|
||||
#
|
||||
# Permission groups
|
||||
#
|
||||
|
||||
def create_permission_group(group_name, desc=None, group_perms=None,
|
||||
permissions=None):
|
||||
"""
|
||||
Adds a new group
|
||||
|
||||
group_name - case sensitive, unique key for group.
|
||||
desc - description of permission group
|
||||
group_perms - the permissions stored in this group - can be
|
||||
a list of permission strings, a single permission
|
||||
or a comma-separated string of permissions.
|
||||
permissions - can be a list of permission strings, a single
|
||||
permission or a comma-separated string of permissions.
|
||||
OBS-these are the group's OWN permissions, for editing
|
||||
the group etc - NOT the permissions stored in it!
|
||||
"""
|
||||
|
||||
new_group = PermissionGroup.objects.filter(db_key__exact=group_name)
|
||||
if new_group:
|
||||
new_group = new_group[0]
|
||||
else:
|
||||
new_group = PermissionGroup()
|
||||
new_group.key = group_name
|
||||
if desc:
|
||||
new_group.desc = desc
|
||||
if group_perms:
|
||||
if is_iter(group_perms):
|
||||
group_perms = ",".join([str(perm) for perm in group_perms])
|
||||
new_group.group_permissions = group_perms
|
||||
if permissions:
|
||||
set_perm(new_group, permissions)
|
||||
new_group.save()
|
||||
return new_group
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Comm system methods
|
||||
#
|
||||
|
||||
def create_message(senderobj, message, channels=None,
|
||||
receivers=None, permissions=None):
|
||||
receivers=None, locks=None):
|
||||
"""
|
||||
Create a new communication message. Msgs are used for all
|
||||
player-to-player communication, both between individual players
|
||||
|
|
@ -284,7 +251,7 @@ def create_message(senderobj, message, channels=None,
|
|||
may be actual channel objects or their unique key strings.
|
||||
receivers - a player to send to, or a list of them. May be Player objects
|
||||
or playernames.
|
||||
permissions - permission string, or a list of permission strings.
|
||||
locks - lock definition string
|
||||
|
||||
The Comm system is created very open-ended, so it's fully possible
|
||||
to let a message both go to several channels and to several receivers
|
||||
|
|
@ -325,13 +292,13 @@ def create_message(senderobj, message, channels=None,
|
|||
new_message.receivers = [to_player(receiver) for receiver in
|
||||
[to_object(receiver) for receiver in receivers]
|
||||
if receiver]
|
||||
if permissions:
|
||||
set_perm(new_message, permissions)
|
||||
if locks:
|
||||
new_message.locks.add(locks)
|
||||
new_message.save()
|
||||
return new_message
|
||||
|
||||
def create_channel(key, aliases=None, desc=None,
|
||||
permissions=None, keep_log=True):
|
||||
locks=None, keep_log=True):
|
||||
"""
|
||||
Create A communication Channel. A Channel serves as a central
|
||||
hub for distributing Msgs to groups of people without
|
||||
|
|
@ -342,8 +309,7 @@ def create_channel(key, aliases=None, desc=None,
|
|||
|
||||
key - this must be unique.
|
||||
aliases - list of alternative (likely shorter) keynames.
|
||||
listen/send/admin permissions are strings if permissions separated
|
||||
by commas.
|
||||
locks - lock string definitions
|
||||
"""
|
||||
|
||||
from src.comms.models import Channel
|
||||
|
|
@ -361,8 +327,8 @@ def create_channel(key, aliases=None, desc=None,
|
|||
string = "Could not add channel: key '%s' already exists." % key
|
||||
logger.log_errmsg(string)
|
||||
return None
|
||||
if permissions:
|
||||
set_perm(new_channel, permissions)
|
||||
if locks:
|
||||
new_channel.locks.add(locks)
|
||||
new_channel.save()
|
||||
channelhandler.CHANNELHANDLER.add_channel(new_channel)
|
||||
return new_channel
|
||||
|
|
@ -375,7 +341,7 @@ def create_player(name, email, password,
|
|||
permissions=None,
|
||||
create_character=True,
|
||||
location=None, typeclass=None, home=None,
|
||||
is_superuser=False, user=None):
|
||||
is_superuser=False, user=None, locks=None):
|
||||
|
||||
"""
|
||||
This creates a new player, handling the creation of the User
|
||||
|
|
@ -425,11 +391,18 @@ def create_player(name, email, password,
|
|||
new_player = PlayerDB(db_key=name, user=new_user)
|
||||
new_player.save()
|
||||
|
||||
# assign mud permissions
|
||||
if not permissions:
|
||||
permissions = settings.PERMISSION_PLAYER_DEFAULT
|
||||
set_perm(new_player, permissions)
|
||||
# call hook method (may override default permissions)
|
||||
new_player.at_player_creation()
|
||||
|
||||
# custom given arguments potentially overrides the hook
|
||||
if permissions:
|
||||
new_player.permissions = permissions
|
||||
elif not new_player.permissions:
|
||||
new_player.permissions = settings.PERMISSION_PLAYER_DEFAULT
|
||||
|
||||
if locks:
|
||||
new_player.locks.add(locks)
|
||||
|
||||
# create *in-game* 'player' object
|
||||
if create_character:
|
||||
if not typeclass:
|
||||
|
|
@ -437,8 +410,8 @@ def create_player(name, email, password,
|
|||
# creating the object automatically links the player
|
||||
# and object together by player.obj <-> obj.player
|
||||
new_character = create_object(typeclass, name,
|
||||
location, home,
|
||||
location, home,
|
||||
permissions=permissions,
|
||||
player=new_player)
|
||||
#set_perm(new_character, permissions)
|
||||
return new_character
|
||||
return new_player
|
||||
|
|
|
|||
|
|
@ -67,3 +67,14 @@ def log_infomsg(infomsg):
|
|||
infomsg = str(e)
|
||||
for line in infomsg.splitlines():
|
||||
log.msg('[..] %s' % line)
|
||||
|
||||
def log_depmsg(depmsg):
|
||||
"""
|
||||
Prints a deprecation message
|
||||
"""
|
||||
try:
|
||||
depmsg = utils.to_str(depmsg)
|
||||
except Exception, e:
|
||||
depmsg = str(e)
|
||||
for line in depmsg.splitlines():
|
||||
log.msg('[DP] %s' % line)
|
||||
|
|
|
|||
|
|
@ -10,12 +10,16 @@ from django.db.models.loading import AppCache
|
|||
from django.utils.datastructures import SortedDict
|
||||
from django.conf import settings
|
||||
from src.scripts.models import ScriptDB
|
||||
from src.objects.models import ObjectDB
|
||||
from src.players.models import PlayerDB
|
||||
from src.comms.models import Channel, Msg
|
||||
from src.help.models import HelpEntry
|
||||
|
||||
from src.typeclasses import models as typeclassmodels
|
||||
from src.objects import exithandler
|
||||
from src.comms import channelhandler
|
||||
from src.comms.models import Channel
|
||||
from src.utils import reimport
|
||||
from src.utils import logger
|
||||
from src.utils import reimport, utils, logger
|
||||
|
||||
def reload_modules():
|
||||
"""
|
||||
|
|
@ -96,6 +100,21 @@ def reload_modules():
|
|||
typeclassmodels.reset()
|
||||
exithandler.EXITHANDLER.clear()
|
||||
channelhandler.CHANNELHANDLER.update()
|
||||
|
||||
# run through all objects in database, forcing re-caching.
|
||||
|
||||
cemit_info(" Starting asynchronous object reset loop ...")
|
||||
def run_reset_loop():
|
||||
# run a reset loop on all objects
|
||||
[(o.cmdset.reset(), o.locks.reset()) for o in ObjectDB.objects.all()]
|
||||
[s.locks.reset() for s in ScriptDB.objects.all()]
|
||||
[p.locks.reset() for p in PlayerDB.objects.all()]
|
||||
[h.locks.reset() for h in HelpEntry.objects.all()]
|
||||
[m.locks.reset() for m in Msg.objects.all()]
|
||||
[c.locks.reset() for c in Channel.objects.all()]
|
||||
at_return = lambda r: cemit_info(" ... @reload: Asynchronous reset loop finished.")
|
||||
at_err = lambda e: cemit_info("%s\n@reload: Asynchronous reset loop exited with an error." % e)
|
||||
utils.run_async(run_reset_loop, at_return, at_err)
|
||||
|
||||
def reload_scripts(scripts=None, obj=None, key=None,
|
||||
dbref=None, init_mode=False):
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ from src.players.models import PlayerDB
|
|||
from src.scripts.models import ScriptDB
|
||||
from src.comms.models import Msg, Channel
|
||||
from src.help.models import HelpEntry
|
||||
from src.permissions.models import PermissionGroup
|
||||
from src.config.models import ConfigValue
|
||||
|
||||
#
|
||||
|
|
@ -136,20 +135,6 @@ channels = Channel.objects.channel_search
|
|||
|
||||
helpentries = HelpEntry.objects.search_help
|
||||
|
||||
#
|
||||
# Search for a permission group
|
||||
# Note that the name is case sensitive.
|
||||
#
|
||||
# def search_permissiongroup(self, ostring):
|
||||
# """
|
||||
# Find a permission group
|
||||
#
|
||||
# ostring = permission group name (case sensitive)
|
||||
# or database dbref
|
||||
# """
|
||||
|
||||
permgroups = PermissionGroup.objects.search_permgroup
|
||||
|
||||
#
|
||||
# Get a configuration value
|
||||
#
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ General helper functions that don't fit neatly under any given category.
|
|||
They provide some useful string and conversion methods that might
|
||||
be of use when designing your own game.
|
||||
"""
|
||||
import os, sys
|
||||
import os, sys, imp
|
||||
import textwrap
|
||||
import datetime
|
||||
from twisted.internet import threads
|
||||
|
|
@ -188,7 +188,7 @@ def get_evennia_version():
|
|||
def pypath_to_realpath(python_path, file_ending='.py'):
|
||||
"""
|
||||
Converts a path on dot python form (e.g. 'src.objects.models') to
|
||||
a system path (src/objects/models.py). Calculates all paths as
|
||||
a system path ($BASE_PATH/src/objects/models.py). Calculates all paths as
|
||||
absoulte paths starting from the evennia main directory.
|
||||
"""
|
||||
pathsplit = python_path.strip().split('.')
|
||||
|
|
@ -442,3 +442,48 @@ def has_parent(basepath, obj):
|
|||
# this can occur if we tried to store a class object, not an
|
||||
# instance. Not sure if one should defend against this.
|
||||
return False
|
||||
|
||||
def mod_import(mod_path):
|
||||
"""
|
||||
Takes filename of a module, converts it to a python path
|
||||
and imports it.
|
||||
"""
|
||||
|
||||
def log_trace(errmsg=None):
|
||||
"""
|
||||
Log a traceback to the log. This should be called
|
||||
from within an exception. errmsg is optional and
|
||||
adds an extra line with added info.
|
||||
"""
|
||||
from traceback import format_exc
|
||||
from twisted.python import log
|
||||
|
||||
tracestring = format_exc()
|
||||
if tracestring:
|
||||
for line in tracestring.splitlines():
|
||||
log.msg('[::] %s' % line)
|
||||
if errmsg:
|
||||
try:
|
||||
errmsg = to_str(errmsg)
|
||||
except Exception, e:
|
||||
errmsg = str(e)
|
||||
for line in errmsg.splitlines():
|
||||
log.msg('[EE] %s' % line)
|
||||
|
||||
if not os.path.isabs(mod_path):
|
||||
mod_path = os.path.abspath(mod_path)
|
||||
path, filename = mod_path.rsplit(os.path.sep, 1)
|
||||
modname = filename.rstrip('.py')
|
||||
|
||||
try:
|
||||
result = imp.find_module(modname, [path])
|
||||
except ImportError:
|
||||
log_trace("Could not find module '%s' (%s.py) at path '%s'" % (modname, modname, path))
|
||||
try:
|
||||
mod = imp.load_module(modname, *result)
|
||||
except ImportError:
|
||||
log_trace("Could not find or import module %s at path '%s'" % (modname, path))
|
||||
mod = None
|
||||
# we have to close the file handle manually
|
||||
result[0].close()
|
||||
return mod
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue