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. 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): 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. at_object_creation() - only called once, when object is first created.
Almost all object customizations go here. Almost all object customizations go here.
at_first_login() - only called once, the very first time user logs in. at_first_login() - only called once, the very first time user logs in.
@ -100,13 +107,6 @@ class Character(BaseCharacter):
command set whenever the player logs in - otherwise they won't be command set whenever the player logs in - otherwise they won't be
able to use any commands! 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): def at_disconnect(self):
""" """
@ -145,23 +145,19 @@ class Exit(BaseExit):
hard-coded in their typeclass. Exits do have to make sure they hard-coded in their typeclass. Exits do have to make sure they
clean up a bit after themselves though, easiest accomplished clean up a bit after themselves though, easiest accomplished
by letting by_object_delete() call the object's parent. by letting by_object_delete() call the object's parent.
"""
def at_object_delete(self): Note that exits need to do cache-cleanups after they are
""" deleted, so if you re-implement at_object_delete() for some
The game needs to do some cache cleanups when deleting an exit, reason, make sure to call the parent class-method too!
so we make sure to call super() here. If this method returns
False, the deletion is aborted. """
""" pass
# handle some cleanups
return super(Exit, self).at_object_delete()
# custom modifications below.
# ...
class Player(BasePlayer): class Player(BasePlayer):
""" """
This class describes the actual OOC player (i.e. the user connecting 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 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 is handled by the character which is connected to this). Comm channels
are attended/joined using this object. are attended/joined using this object.

View file

@ -27,7 +27,7 @@ class CmdBoot(MuxCommand):
""" """
key = "@boot" key = "@boot"
locks = "cmd:perm(boot) or perm(Wizard)" locks = "cmd:perm(boot) or perm(Wizards)"
help_category = "Admin" help_category = "Admin"
def func(self): def func(self):
@ -315,17 +315,13 @@ class CmdPerm(MuxCommand):
@perm - set permissions @perm - set permissions
Usage: Usage:
@perm[/switch] [<object>] = [<permission>] @perm[/switch] <object> [= <permission>[,<permission>,...]]
@perm[/switch] [*<player>] = [<permission>]
Switches: Switches:
del : delete the given permission from <object>. 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. This command sets/clears individual permission strings on an object.
Use /list without any arguments to see all available permissions If no permission is given, list all permissions on <object>
or those defined on the <object>/<player> argument.
""" """
key = "@perm" key = "@perm"
aliases = "@setperm" aliases = "@setperm"
@ -340,51 +336,38 @@ class CmdPerm(MuxCommand):
lhs, rhs = self.lhs, self.rhs lhs, rhs = self.lhs, self.rhs
if not self.args: if not self.args:
string = "Usage: @perm[/switch] object [ = permission, permission, ...]\n"
caller.msg(string)
return
if "list" not in switches: # locate the object
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
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
obj = caller.search(lhs, global_search=True) obj = caller.search(lhs, global_search=True)
if not obj: if not obj:
return return
pstring = ""
if utils.inherits_from(obj, settings.BASE_PLAYER_TYPECLASS):
pstring = " Player "
if not rhs: 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: if not obj.permissions:
string += "<None>" string += "<None>"
else: else:
string += ", ".join(obj.permissions) string += ", ".join(obj.permissions)
if pstring and obj.is_superuser: if hasattr(obj, 'player') and hasattr(obj.player, 'is_superuser') and obj.player.is_superuser:
string += "\n(... But this player is a SUPERUSER! " string += "\n(... but this object is currently controlled by a SUPERUSER! "
string += "All access checked are passed automatically.)" string += "All access checks 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.)"
caller.msg(string) caller.msg(string)
return return
# we supplied an argument on the form obj = perm # 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 = "" cstring = ""
tstring = "" tstring = ""
if 'del' in switches: if 'del' in switches:
@ -393,26 +376,24 @@ class CmdPerm(MuxCommand):
try: try:
index = obj.permissions.index(perm) index = obj.permissions.index(perm)
except ValueError: 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 continue
permissions = obj.permissions permissions = obj.permissions
del permissions[index] del permissions[index]
obj.permissions = permissions 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) tstring += "\n%s revokes the permission '%s' from you." % (caller.name, perm)
else: else:
# As an extra check, we warn the user if they customize the # add a new permission
# permission string (which is okay, and is used by the lock system)
permissions = obj.permissions permissions = obj.permissions
for perm in self.rhslist: for perm in self.rhslist:
if perm in permissions: 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: else:
permissions.append(perm) permissions.append(perm)
obj.permissions = permissions obj.permissions = permissions
cstring += "\nPermission '%s' given to %s%s." % (rhs, pstring, obj.name) cstring += "\nPermission '%s' given to %s." % (rhs, obj.name)
tstring += "\n%s granted you the permission '%s'." % (caller.name, rhs) tstring += "\n%s gives you the permission '%s'." % (caller.name, rhs)
caller.msg(cstring.strip()) caller.msg(cstring.strip())
if tstring: if tstring:
obj.msg(tstring.strip()) obj.msg(tstring.strip())

View file

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

View file

@ -1299,7 +1299,7 @@ class CmdLock(ObjManipCommand):
if lockdef: if lockdef:
string = lockdef[2] string = lockdef[2]
if 'del' in self.switches: 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.") caller.msg("You are not allowed to do that.")
return return
obj.locks.delete(access_type) obj.locks.delete(access_type)
@ -1315,7 +1315,7 @@ class CmdLock(ObjManipCommand):
obj = caller.search(objname) obj = caller.search(objname)
if not obj: if not obj:
return return
if not obj.access(caller, 'edit'): if not obj.access(caller, 'control'):
caller.msg("You are not allowed to do that.") caller.msg("You are not allowed to do that.")
return return
ok = obj.locks.add(lockdef, caller) ok = obj.locks.add(lockdef, caller)
@ -1452,7 +1452,7 @@ class CmdExamine(ObjManipCommand):
obj = caller.location obj = caller.location
if not obj.access(caller, 'examine'): if not obj.access(caller, 'examine'):
#If we don't have special info access, just look at the object instead. #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 return
string = self.format_output(obj) string = self.format_output(obj)
@ -1471,7 +1471,7 @@ class CmdExamine(ObjManipCommand):
continue continue
if not obj.access(caller, 'examine'): if not obj.access(caller, 'examine'):
#If we don't have special info access, just look at the object instead. #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 continue
if obj_attrs: if obj_attrs:
for attrname in 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 from src.comms.models import Channel, Msg, ChannelConnection
class MsgAdmin(admin.ModelAdmin): 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",) list_display_links = ("id",)
ordering = ["db_date_sent", 'db_sender', 'db_receivers', 'db_channels'] 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'] search_fields = ['id', '^db_date_sent', '^db_message']
save_as = True save_as = True
save_on_top = True save_on_top = True
@ -18,23 +18,22 @@ class MsgAdmin(admin.ModelAdmin):
admin.site.register(Msg, MsgAdmin) admin.site.register(Msg, MsgAdmin)
class ChannelAdmin(admin.ModelAdmin): 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') list_display_links = ("id", 'db_key')
ordering = ["db_key"] ordering = ["db_key"]
readonly_fields = ['db_permissions']
search_fields = ['id', 'db_key', 'db_aliases'] search_fields = ['id', 'db_key', 'db_aliases']
save_as = True save_as = True
save_on_top = True save_on_top = True
list_select_related = True list_select_related = True
admin.site.register(Channel, ChannelAdmin) admin.site.register(Channel, ChannelAdmin)
class ChannelConnectionAdmin(admin.ModelAdmin): # class ChannelConnectionAdmin(admin.ModelAdmin):
list_display = ('db_channel', 'db_player') # list_display = ('db_channel', 'db_player')
list_display_links = ("db_player", 'db_channel') # list_display_links = ("db_player", 'db_channel')
ordering = ["db_channel"] # ordering = ["db_channel"]
search_fields = ['db_channel', 'db_player'] # search_fields = ['db_channel', 'db_player']
save_as = True # save_as = True
save_on_top = True # save_on_top = True
list_select_related = True # list_select_related = True
admin.site.register(ChannelConnection, ChannelConnectionAdmin) # admin.site.register(ChannelConnection, ChannelConnectionAdmin)

View file

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

View file

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

View file

@ -7,10 +7,9 @@ from src.objects.models import ObjAttribute, ObjectDB
from django.contrib import admin from django.contrib import admin
class ObjAttributeAdmin(admin.ModelAdmin): 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') list_display_links = ("id", 'db_key')
ordering = ["db_obj", 'db_key'] ordering = ["db_obj", 'db_key']
readonly_fields = ['db_permissions']
search_fields = ['id', 'db_key', 'db_obj'] search_fields = ['id', 'db_key', 'db_obj']
save_as = True save_as = True
save_on_top = 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 = ('id', 'db_key', 'db_typeclass_path', 'db_location', 'db_player')
list_display_links = ('id', 'db_key') list_display_links = ('id', 'db_key')
ordering = ['id', 'db_typeclass_path'] ordering = ['id', 'db_typeclass_path']
readonly_fields = ['db_permissions'] readonly_fields = ['db_permissions', 'db_lock_storage']
search_fields = ['^db_key', 'db_typeclass_path'] search_fields = ['^db_key', 'db_typeclass_path']
save_as = True save_as = True
save_on_top = True save_on_top = True

View file

@ -75,22 +75,30 @@ class Object(TypeClass):
# hooks called by the game engine # 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): def at_object_creation(self):
""" """
Called once, when this object is first Called once, when this object is first
created. 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): def at_first_login(self):
""" """
@ -327,25 +335,31 @@ class Character(Object):
version of the at_object_creation to set up the script version of the at_object_creation to set up the script
that adds the default cmdset to the object. 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): def at_object_creation(self):
""" """
All this does (for now) is to add the default cmdset. Since All this does (for now) is to add the default cmdset. Since
the script is permanently stored to this object (the permanent the script is permanently stored to this object (the permanent
keyword creates a script to do this), we should never need to keyword creates a script to do this), we should never need to
do this again for as long as this object exists. 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): def at_after_move(self, source_location):
"Default is to look around after a move." "Default is to look around after a move."
self.execute_cmd('look') self.execute_cmd('look')
# #
# Base Room object # Base Room object
# #
@ -355,12 +369,12 @@ class Room(Object):
This is the base room object. It's basically This is the base room object. It's basically
like any Object except its location is None. like any Object except its location is None.
""" """
def at_object_creation(self): def basetype_setup(self):
""" """
Simple setup, shown as an example Simple setup, shown as an example
(since default is None anyway) (since default is None anyway)
""" """
super(Room, self).at_object_creation() super(Room, self).basetype_setup()
self.location = None self.location = None
class Exit(Object): class Exit(Object):
@ -375,6 +389,14 @@ class Exit(Object):
the attribute _destination to a valid location the attribute _destination to a valid location
('Quack like a duck...' and so forth). ('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): def at_object_creation(self):
""" """
Another example just for show; the _destination attribute Another example just for show; the _destination attribute
@ -385,10 +407,6 @@ class Exit(Object):
# this is what makes it an exit # this is what makes it an exit
self.attr("_destination", "None") 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): def at_object_delete(self):
""" """
We have to make sure to clean the exithandler cache 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 from django.contrib import admin
class PlayerAttributeAdmin(admin.ModelAdmin): 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') list_display_links = ("id", 'db_key')
ordering = ["db_obj", 'db_key'] ordering = ["db_obj", 'db_key']
readonly_fields = ['db_permissions']
search_fields = ['id', 'db_key', 'db_obj'] search_fields = ['id', 'db_key', 'db_obj']
save_as = True save_as = True
save_on_top = True save_on_top = True
@ -21,7 +20,6 @@ class PlayerDBAdmin(admin.ModelAdmin):
list_display = ('id', 'user', 'db_obj', 'db_typeclass_path') list_display = ('id', 'user', 'db_obj', 'db_typeclass_path')
list_display_links = ('id', 'user') list_display_links = ('id', 'user')
ordering = ['id', 'user'] ordering = ['id', 'user']
readonly_fields = ['db_permissions']
search_fields = ['^db_key', 'db_typeclass_path'] search_fields = ['^db_key', 'db_typeclass_path']
save_as = True save_as = True
save_on_top = True save_on_top = True

View file

@ -7,10 +7,9 @@ from src.scripts.models import ScriptAttribute, ScriptDB
from django.contrib import admin from django.contrib import admin
class ScriptAttributeAdmin(admin.ModelAdmin): 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') list_display_links = ("id", 'db_key')
ordering = ["db_obj", 'db_key'] ordering = ["db_obj", 'db_key']
readonly_fields = ['db_permissions']
search_fields = ['id', 'db_key', 'db_obj'] search_fields = ['id', 'db_key', 'db_obj']
save_as = True save_as = True
save_on_top = True save_on_top = True
@ -21,7 +20,6 @@ class ScriptDBAdmin(admin.ModelAdmin):
list_display = ('id', 'db_key', 'db_typeclass_path', 'db_obj', 'db_interval', 'db_repeats', 'db_persistent') list_display = ('id', 'db_key', 'db_typeclass_path', 'db_obj', 'db_interval', 'db_repeats', 'db_persistent')
list_display_links = ('id', 'db_key') list_display_links = ('id', 'db_key')
ordering = ['db_obj', 'db_typeclass_path'] ordering = ['db_obj', 'db_typeclass_path']
readonly_fields = ['db_permissions']
search_fields = ['^db_key', 'db_typeclass_path'] search_fields = ['^db_key', 'db_typeclass_path']
save_as = True save_as = True
save_on_top = 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 # call the hook method. This is where all at_creation
# customization happens as the typeclass stores custom # customization happens as the typeclass stores custom
# things on its database object. # things on its database object.
new_object.basetype_setup() # setup the basics of Exits, Characters etc.
new_object.at_object_creation() new_object.at_object_creation()
# custom-given variables override the hook # custom-given variables override the hook