Fixed some bugs in the access system. Changed the "owner" default permission to "control" instead to more general. Added a new hook for setting locks for objects (the suspicion was that this would give lots of grief to newbies otherwise, now we have a lockdown policy in the absence of lock definitions).

This commit is contained in:
Griatch 2011-03-17 21:43:18 +00:00
parent 295a82cc04
commit bccd84e480
12 changed files with 115 additions and 124 deletions

View file

@ -43,6 +43,13 @@ class Object(BaseObject):
of Evennia and messing with them might well break things for you.
Hooks (these are class methods, so their arguments should also start with self):
basetype_setup() - only called once, when object is first created, before
at_object_creation(). Sets up semi-engine-specific details such as
the default lock policy, what defines a Room, Exit etc.
Usually not modified unless you want to overload the default
security restriction policies.
at_object_creation() - only called once, when object is first created.
Almost all object customizations go here.
at_first_login() - only called once, the very first time user logs in.
@ -100,14 +107,7 @@ class Character(BaseCharacter):
command set whenever the player logs in - otherwise they won't be
able to use any commands!
"""
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.
super(Character, self).at_object_creation()
# expand with whatever customizations you want below...
# ...
def at_disconnect(self):
"""
We stove away the character when logging off, otherwise they will remain where
@ -145,23 +145,19 @@ class Exit(BaseExit):
hard-coded in their typeclass. Exits do have to make sure they
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,
so we make sure to call super() here. If this method returns
False, the deletion is aborted.
"""
# handle some cleanups
return super(Exit, self).at_object_delete()
# custom modifications below.
# ...
Note that exits need to do cache-cleanups after they are
deleted, so if you re-implement at_object_delete() for some
reason, make sure to call the parent class-method too!
"""
pass
class Player(BasePlayer):
"""
This class describes the actual OOC player (i.e. the user connecting
to the MUD). It does NOT have visual appearance in the game world (that
is handled by the character which is connected to this). Comm channels
are attended/joined using this object.

View file

@ -27,7 +27,7 @@ class CmdBoot(MuxCommand):
"""
key = "@boot"
locks = "cmd:perm(boot) or perm(Wizard)"
locks = "cmd:perm(boot) or perm(Wizards)"
help_category = "Admin"
def func(self):
@ -315,17 +315,13 @@ class CmdPerm(MuxCommand):
@perm - set permissions
Usage:
@perm[/switch] [<object>] = [<permission>]
@perm[/switch] [*<player>] = [<permission>]
@perm[/switch] <object> [= <permission>[,<permission>,...]]
Switches:
del : delete the given permission from <object>.
list : list all permissions, or those set on <object>
Use * before the search string to add permissions to a player.
This command sets/clears individual permission strings on an object.
Use /list without any arguments to see all available permissions
or those defined on the <object>/<player> argument.
If no permission is given, list all permissions on <object>
"""
key = "@perm"
aliases = "@setperm"
@ -339,52 +335,39 @@ class CmdPerm(MuxCommand):
switches = self.switches
lhs, rhs = self.lhs, self.rhs
if not self.args:
if "list" not in switches:
string = "Usage: @setperm[/switch] [object = permission]\n"
string += " @setperm[/switch] [*player = permission]"
caller.msg(string)
return
else:
#just print all available permissions
string = "\nAll defined permission groups and keys (i.e. not locks):"
pgroups = list(PermissionGroup.objects.all())
pgroups.sort(lambda x, y: cmp(x.key, y.key)) # sort by group key
if not self.args:
string = "Usage: @perm[/switch] object [ = permission, permission, ...]\n"
caller.msg(string)
return
for pgroup in pgroups:
string += "\n\n - {w%s{n (%s):" % (pgroup.key, pgroup.desc)
string += "\n%s" % \
utils.fill(", ".join(sorted(pgroup.group_permissions)))
caller.msg(string)
return
# locate the object/player
# locate the object
obj = caller.search(lhs, global_search=True)
if not obj:
return
pstring = ""
if utils.inherits_from(obj, settings.BASE_PLAYER_TYPECLASS):
pstring = " Player "
if not rhs:
string = "Permission string on %s{w%s{n: " % (pstring, obj.key)
if not obj.access(caller, 'examine'):
caller.msg("You are not allowed to examine this object.")
return
string = "Permissions on {w%s{n: " % obj.key
if not obj.permissions:
string += "<None>"
else:
string += ", ".join(obj.permissions)
if pstring and obj.is_superuser:
string += "\n(... But this player is a SUPERUSER! "
string += "All access checked are passed automatically.)"
elif obj.player and obj.player.is_superuser:
string += "\n(... But this object's player is a SUPERUSER! "
string += "All access checked are passed automatically.)"
if hasattr(obj, 'player') and hasattr(obj.player, 'is_superuser') and obj.player.is_superuser:
string += "\n(... but this object is currently controlled by a SUPERUSER! "
string += "All access checks are passed automatically.)"
caller.msg(string)
return
# we supplied an argument on the form obj = perm
if not obj.access(caller, 'control'):
caller.msg("You are not allowed to edit this object's permissions.")
return
cstring = ""
tstring = ""
if 'del' in switches:
@ -393,26 +376,24 @@ class CmdPerm(MuxCommand):
try:
index = obj.permissions.index(perm)
except ValueError:
cstring += "\nPermission '%s' was not defined on %s%s." % (perm, pstring, lhs)
cstring += "\nPermission '%s' was not defined on %s." % (perm, obj.name)
continue
permissions = obj.permissions
del permissions[index]
obj.permissions = permissions
cstring += "\nPermission '%s' was removed from %s%s." % (perm, pstring, obj.name)
cstring += "\nPermission '%s' was removed from %s." % (perm, obj.name)
tstring += "\n%s revokes the permission '%s' from you." % (caller.name, perm)
else:
# As an extra check, we warn the user if they customize the
# permission string (which is okay, and is used by the lock system)
# add a new permission
permissions = obj.permissions
for perm in self.rhslist:
if perm in permissions:
cstring += "\nPermission '%s' is already defined on %s%s." % (rhs, pstring, obj.name)
cstring += "\nPermission '%s' is already defined on %s%s." % (rhs, obj.name)
else:
permissions.append(perm)
obj.permissions = permissions
cstring += "\nPermission '%s' given to %s%s." % (rhs, pstring, obj.name)
tstring += "\n%s granted you the permission '%s'." % (caller.name, rhs)
cstring += "\nPermission '%s' given to %s." % (rhs, obj.name)
tstring += "\n%s gives you the permission '%s'." % (caller.name, rhs)
caller.msg(cstring.strip())
if tstring:
obj.msg(tstring.strip())

View file

@ -193,7 +193,7 @@ class CmdBatchCommands(MuxCommand):
"""
key = "@batchcommands"
aliases = ["@batchcommand", "@batchcmd"]
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands) or superuser()"
help_category = "Building"
def func(self):
@ -277,7 +277,7 @@ class CmdBatchCode(MuxCommand):
"""
key = "@batchcode"
aliases = ["@batchcodes"]
locks = "cmd:perm(batchcommands)"
locks = "cmd:perm(batchcommands) or superuser()"
help_category = "Building"
def func(self):

View file

@ -1299,7 +1299,7 @@ class CmdLock(ObjManipCommand):
if lockdef:
string = lockdef[2]
if 'del' in self.switches:
if not obj.access(caller, 'edit'):
if not obj.access(caller, 'control'):
caller.msg("You are not allowed to do that.")
return
obj.locks.delete(access_type)
@ -1315,7 +1315,7 @@ class CmdLock(ObjManipCommand):
obj = caller.search(objname)
if not obj:
return
if not obj.access(caller, 'edit'):
if not obj.access(caller, 'control'):
caller.msg("You are not allowed to do that.")
return
ok = obj.locks.add(lockdef, caller)
@ -1452,7 +1452,7 @@ class CmdExamine(ObjManipCommand):
obj = caller.location
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)
caller.execute_cmd('look %s' % obj.name)
return
string = self.format_output(obj)
@ -1471,7 +1471,7 @@ class CmdExamine(ObjManipCommand):
continue
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)
caller.execute_cmd('look %s' % obj_name)
continue
if obj_attrs:
for attrname in obj_attrs:

View file

@ -7,10 +7,10 @@ from django.contrib import admin
from src.comms.models import Channel, Msg, ChannelConnection
class MsgAdmin(admin.ModelAdmin):
list_display = ('id', 'db_date_sent', 'db_sender', 'db_receivers', 'db_channels', 'db_message')
list_display = ('id', 'db_date_sent', 'db_sender', 'db_receivers', 'db_channels', 'db_message', 'db_lock_storage')
list_display_links = ("id",)
ordering = ["db_date_sent", 'db_sender', 'db_receivers', 'db_channels']
readonly_fields = ['db_permissions', 'db_message', 'db_sender', 'db_receivers', 'db_channels']
readonly_fields = ['db_message', 'db_sender', 'db_receivers', 'db_channels']
search_fields = ['id', '^db_date_sent', '^db_message']
save_as = True
save_on_top = True
@ -18,23 +18,22 @@ class MsgAdmin(admin.ModelAdmin):
admin.site.register(Msg, MsgAdmin)
class ChannelAdmin(admin.ModelAdmin):
list_display = ('id', 'db_key', 'db_desc', 'db_aliases', 'db_keep_log', 'db_permissions')
list_display = ('id', 'db_key', 'db_desc', 'db_aliases', 'db_keep_log', 'db_lock_storage')
list_display_links = ("id", 'db_key')
ordering = ["db_key"]
readonly_fields = ['db_permissions']
search_fields = ['id', 'db_key', 'db_aliases']
save_as = True
save_on_top = True
list_select_related = True
admin.site.register(Channel, ChannelAdmin)
class ChannelConnectionAdmin(admin.ModelAdmin):
list_display = ('db_channel', 'db_player')
list_display_links = ("db_player", 'db_channel')
ordering = ["db_channel"]
search_fields = ['db_channel', 'db_player']
save_as = True
save_on_top = True
list_select_related = True
admin.site.register(ChannelConnection, ChannelConnectionAdmin)
# class ChannelConnectionAdmin(admin.ModelAdmin):
# list_display = ('db_channel', 'db_player')
# list_display_links = ("db_player", 'db_channel')
# ordering = ["db_channel"]
# search_fields = ['db_channel', 'db_player']
# save_as = True
# save_on_top = True
# list_select_related = True
# admin.site.register(ChannelConnection, ChannelConnectionAdmin)

View file

@ -2,7 +2,7 @@ from django.contrib import admin
from src.help.models import HelpEntry
class HelpEntryAdmin(admin.ModelAdmin):
list_display = ('id', 'db_key', 'db_help_category', 'db_permissions')
list_display = ('id', 'db_key', 'db_help_category')
list_display_links = ('id', 'db_key')
search_fields = ['^db_key', 'db_entrytext']
ordering = ['db_help_category', 'db_key']

View file

@ -348,12 +348,13 @@ def test():
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 = "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()"
obj1.lock_storage = "listen:perm(Immortals)"
pdb.set_trace()
obj1.locks = LockHandler(obj1)
obj2.permissions = ["Wizards"]
obj2.permissions = ["Immortals"]
obj2.id = 4
#obj1.locks.add("edit:attr(test)")

View file

@ -7,10 +7,9 @@ from src.objects.models import ObjAttribute, ObjectDB
from django.contrib import admin
class ObjAttributeAdmin(admin.ModelAdmin):
list_display = ('id', 'db_key', 'db_value', 'db_mode', 'db_obj', 'db_permissions')
list_display = ('id', 'db_key', 'db_value', 'db_mode', 'db_obj')
list_display_links = ("id", 'db_key')
ordering = ["db_obj", 'db_key']
readonly_fields = ['db_permissions']
search_fields = ['id', 'db_key', 'db_obj']
save_as = True
save_on_top = True
@ -21,7 +20,7 @@ class ObjectDBAdmin(admin.ModelAdmin):
list_display = ('id', 'db_key', 'db_typeclass_path', 'db_location', 'db_player')
list_display_links = ('id', 'db_key')
ordering = ['id', 'db_typeclass_path']
readonly_fields = ['db_permissions']
readonly_fields = ['db_permissions', 'db_lock_storage']
search_fields = ['^db_key', 'db_typeclass_path']
save_as = True
save_on_top = True

View file

@ -75,22 +75,30 @@ class Object(TypeClass):
# hooks called by the game engine
def basetype_setup(self):
"""
This sets up the default properties of an Object,
just before the more general at_object_creation.
"""
# 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 be its
# controller, for example)
dbref = self.dbobj.dbref
self.locks.add("control: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_object_creation(self):
"""
Called once, when this object is first
created.
"""
# 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()")
pass
def at_first_login(self):
"""
@ -327,25 +335,31 @@ class Character(Object):
version of the at_object_creation to set up the script
that adds the default cmdset to the object.
"""
def basetype_setup(self):
"""
Setup character-specific security
"""
super(Character, self).basetype_setup()
self.locks.add("puppet:id(%s) or perm(Immortals)" % self.dbobj.dbref)
# add the default cmdset
from settings import CMDSET_DEFAULT
self.cmdset.add_default(CMDSET_DEFAULT, permanent=True)
def at_object_creation(self):
"""
All this does (for now) is to add the default cmdset. Since
the script is permanently stored to this object (the permanent
keyword creates a script to do this), we should never need to
do this again for as long as this object exists.
pass
"""
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
#
@ -355,12 +369,12 @@ class Room(Object):
This is the base room object. It's basically
like any Object except its location is None.
"""
def at_object_creation(self):
def basetype_setup(self):
"""
Simple setup, shown as an example
(since default is None anyway)
"""
super(Room, self).at_object_creation()
super(Room, self).basetype_setup()
self.location = None
class Exit(Object):
@ -375,6 +389,14 @@ class Exit(Object):
the attribute _destination to a valid location
('Quack like a duck...' and so forth).
"""
def basetype_setup(self):
"""
Setup exit-security
"""
# the lock is open to all by default
super(Exit, self).basetype_setup()
self.locks.add("traverse:all()")
def at_object_creation(self):
"""
Another example just for show; the _destination attribute
@ -385,10 +407,6 @@ class Exit(Object):
# 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

View file

@ -7,10 +7,9 @@ from src.players.models import PlayerDB, PlayerAttribute
from django.contrib import admin
class PlayerAttributeAdmin(admin.ModelAdmin):
list_display = ('id', 'db_key', 'db_value', 'db_mode', 'db_obj', 'db_permissions')
list_display = ('id', 'db_key', 'db_value', 'db_mode', 'db_obj')
list_display_links = ("id", 'db_key')
ordering = ["db_obj", 'db_key']
readonly_fields = ['db_permissions']
search_fields = ['id', 'db_key', 'db_obj']
save_as = True
save_on_top = True
@ -21,7 +20,6 @@ class PlayerDBAdmin(admin.ModelAdmin):
list_display = ('id', 'user', 'db_obj', 'db_typeclass_path')
list_display_links = ('id', 'user')
ordering = ['id', 'user']
readonly_fields = ['db_permissions']
search_fields = ['^db_key', 'db_typeclass_path']
save_as = True
save_on_top = True

View file

@ -7,10 +7,9 @@ from src.scripts.models import ScriptAttribute, ScriptDB
from django.contrib import admin
class ScriptAttributeAdmin(admin.ModelAdmin):
list_display = ('id', 'db_key', 'db_value', 'db_mode', 'db_obj', 'db_permissions')
list_display = ('id', 'db_key', 'db_value', 'db_mode', 'db_obj')
list_display_links = ("id", 'db_key')
ordering = ["db_obj", 'db_key']
readonly_fields = ['db_permissions']
search_fields = ['id', 'db_key', 'db_obj']
save_as = True
save_on_top = True
@ -20,8 +19,7 @@ admin.site.register(ScriptAttribute, ScriptAttributeAdmin)
class ScriptDBAdmin(admin.ModelAdmin):
list_display = ('id', 'db_key', 'db_typeclass_path', 'db_obj', 'db_interval', 'db_repeats', 'db_persistent')
list_display_links = ('id', 'db_key')
ordering = ['db_obj', 'db_typeclass_path']
readonly_fields = ['db_permissions']
ordering = ['db_obj', 'db_typeclass_path']
search_fields = ['^db_key', 'db_typeclass_path']
save_as = True
save_on_top = True

View file

@ -93,6 +93,7 @@ def create_object(typeclass, key=None, location=None,
# call the hook method. This is where all at_creation
# customization happens as the typeclass stores custom
# things on its database object.
new_object.basetype_setup() # setup the basics of Exits, Characters etc.
new_object.at_object_creation()
# custom-given variables override the hook