From bccd84e480d2f534457082e509d0d5cfd510175b Mon Sep 17 00:00:00 2001 From: Griatch Date: Thu, 17 Mar 2011 21:43:18 +0000 Subject: [PATCH] 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). --- game/gamesrc/objects/baseobjects.py | 34 ++++++------ src/commands/default/admin.py | 77 +++++++++++----------------- src/commands/default/batchprocess.py | 4 +- src/commands/default/building.py | 8 +-- src/comms/admin.py | 25 +++++---- src/help/admin.py | 2 +- src/locks/lockhandler.py | 5 +- src/objects/admin.py | 5 +- src/objects/objects.py | 68 +++++++++++++++--------- src/players/admin.py | 4 +- src/scripts/admin.py | 6 +-- src/utils/create.py | 1 + 12 files changed, 115 insertions(+), 124 deletions(-) diff --git a/game/gamesrc/objects/baseobjects.py b/game/gamesrc/objects/baseobjects.py index 12b4d94757..f1c079ba70 100644 --- a/game/gamesrc/objects/baseobjects.py +++ b/game/gamesrc/objects/baseobjects.py @@ -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. diff --git a/src/commands/default/admin.py b/src/commands/default/admin.py index e66d9c9611..392300c243 100644 --- a/src/commands/default/admin.py +++ b/src/commands/default/admin.py @@ -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] [] = [] - @perm[/switch] [*] = [] - + @perm[/switch] [= [,,...]] + Switches: del : delete the given permission from . - list : list all permissions, or those set on - 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 / argument. + If no permission is given, list all permissions on """ 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 += "" 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()) diff --git a/src/commands/default/batchprocess.py b/src/commands/default/batchprocess.py index 442087075f..7d64918a4f 100644 --- a/src/commands/default/batchprocess.py +++ b/src/commands/default/batchprocess.py @@ -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): diff --git a/src/commands/default/building.py b/src/commands/default/building.py index 15d8beb003..8e7a5cec9c 100644 --- a/src/commands/default/building.py +++ b/src/commands/default/building.py @@ -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: diff --git a/src/comms/admin.py b/src/comms/admin.py index 74d982a262..9a010998a7 100644 --- a/src/comms/admin.py +++ b/src/comms/admin.py @@ -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) diff --git a/src/help/admin.py b/src/help/admin.py index e98367e292..b4a607bd08 100644 --- a/src/help/admin.py +++ b/src/help/admin.py @@ -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'] diff --git a/src/locks/lockhandler.py b/src/locks/lockhandler.py index 0ef56171af..72310c81cc 100644 --- a/src/locks/lockhandler.py +++ b/src/locks/lockhandler.py @@ -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)") diff --git a/src/objects/admin.py b/src/objects/admin.py index e9963fe9ac..071c3f0c29 100644 --- a/src/objects/admin.py +++ b/src/objects/admin.py @@ -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 diff --git a/src/objects/objects.py b/src/objects/objects.py index 6a41be82a4..a85731c48e 100644 --- a/src/objects/objects.py +++ b/src/objects/objects.py @@ -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 diff --git a/src/players/admin.py b/src/players/admin.py index bc3f6f8210..0f26b64234 100644 --- a/src/players/admin.py +++ b/src/players/admin.py @@ -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 diff --git a/src/scripts/admin.py b/src/scripts/admin.py index 21501da6db..1eee169e4a 100644 --- a/src/scripts/admin.py +++ b/src/scripts/admin.py @@ -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 diff --git a/src/utils/create.py b/src/utils/create.py index e43caf1048..2850df8e69 100644 --- a/src/utils/create.py +++ b/src/utils/create.py @@ -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