diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index c2200470c3..c2aaa16a4c 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -3002,7 +3002,10 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): # all seems ok. Try to save. try: - save_db_prototype(caller, key, prototype, desc=desc, tags=tags, locks=lockstring) + prot = save_db_prototype(caller, key, prototype, desc=desc, tags=tags, locks=lockstring) + prot.locks.append("edit", "perm(Admin)") + if not prot.locks.get("use"): + prot.locks.add("use:all()") except PermissionError as err: caller.msg("|rError saving:|R {}|n".format(err)) return diff --git a/evennia/locks/lockhandler.py b/evennia/locks/lockhandler.py index b8801f9655..6b1a30ab03 100644 --- a/evennia/locks/lockhandler.py +++ b/evennia/locks/lockhandler.py @@ -421,6 +421,28 @@ class LockHandler(object): self._cache_locks(self.obj.lock_storage) self.cache_lock_bypass(self.obj) + def append(self, access_type, lockstring, op='or'): + """ + Append a lock definition to access_type if it doesn't already exist. + + Args: + access_type (str): Access type. + lockstring (str): A valid lockstring, without the operator to + link it to an eventual existing lockstring. + op (str): An operator 'and', 'or', 'and not', 'or not' used + for appending the lockstring to an existing access-type. + Note: + The most common use of this method is for use in commands where + the user can specify their own lockstrings. This method allows + the system to auto-add things like Admin-override access. + + """ + old_lockstring = self.get(access_type) + if not lockstring.strip().lower() in old_lockstring.lower(): + lockstring = "{old} {op} {new}".format( + old=old_lockstring, op=op, new=lockstring.strip()) + self.add(lockstring) + def check(self, accessing_obj, access_type, default=False, no_superuser_bypass=False): """ Checks a lock of the correct type by passing execution off to @@ -459,9 +481,13 @@ class LockHandler(object): return True except AttributeError: # happens before session is initiated. - if not no_superuser_bypass and ((hasattr(accessing_obj, 'is_superuser') and accessing_obj.is_superuser) or - (hasattr(accessing_obj, 'account') and hasattr(accessing_obj.account, 'is_superuser') and accessing_obj.account.is_superuser) or - (hasattr(accessing_obj, 'get_account') and (not accessing_obj.get_account() or accessing_obj.get_account().is_superuser))): + if not no_superuser_bypass and ( + (hasattr(accessing_obj, 'is_superuser') and accessing_obj.is_superuser) or + (hasattr(accessing_obj, 'account') and + hasattr(accessing_obj.account, 'is_superuser') and + accessing_obj.account.is_superuser) or + (hasattr(accessing_obj, 'get_account') and + (not accessing_obj.get_account() or accessing_obj.get_account().is_superuser))): return True # no superuser or bypass -> normal lock operation @@ -469,7 +495,8 @@ class LockHandler(object): # we have a lock, test it. evalstring, func_tup, raw_string = self.locks[access_type] # execute all lock funcs in the correct order, producing a tuple of True/False results. - true_false = tuple(bool(tup[0](accessing_obj, self.obj, *tup[1], **tup[2])) for tup in func_tup) + true_false = tuple(bool( + tup[0](accessing_obj, self.obj, *tup[1], **tup[2])) for tup in func_tup) # the True/False tuple goes into evalstring, which combines them # with AND/OR/NOT in order to get the final result. return eval(evalstring % true_false) @@ -520,9 +547,13 @@ class LockHandler(object): if accessing_obj.locks.lock_bypass and not no_superuser_bypass: return True except AttributeError: - if no_superuser_bypass and ((hasattr(accessing_obj, 'is_superuser') and accessing_obj.is_superuser) or - (hasattr(accessing_obj, 'account') and hasattr(accessing_obj.account, 'is_superuser') and accessing_obj.account.is_superuser) or - (hasattr(accessing_obj, 'get_account') and (not accessing_obj.get_account() or accessing_obj.get_account().is_superuser))): + if no_superuser_bypass and ( + (hasattr(accessing_obj, 'is_superuser') and accessing_obj.is_superuser) or + (hasattr(accessing_obj, 'account') and + hasattr(accessing_obj.account, 'is_superuser') and + accessing_obj.account.is_superuser) or + (hasattr(accessing_obj, 'get_account') and + (not accessing_obj.get_account() or accessing_obj.get_account().is_superuser))): return True if ":" not in lockstring: lockstring = "%s:%s" % ("_dummy", lockstring) @@ -538,7 +569,8 @@ class LockHandler(object): else: # if no access types was given and multiple locks were # embedded in the lockstring we assume all must be true - return all(self._eval_access_type(accessing_obj, locks, access_type) for access_type in locks) + return all(self._eval_access_type( + accessing_obj, locks, access_type) for access_type in locks) def _test(): diff --git a/evennia/utils/spawner.py b/evennia/utils/spawner.py index 6426aa0acc..ed92dfadd5 100644 --- a/evennia/utils/spawner.py +++ b/evennia/utils/spawner.py @@ -161,7 +161,12 @@ def build_metaproto(key, desc, locks, tags, prototype): Create a metaproto from combinant parts. """ - return MetaProto(key, desc, ";".join(locks) if is_iter(locks) else locks, tags, dict(prototype)) + if locks: + locks = (";".join(locks) if is_iter(locks) else locks) + else: + locks = [] + prototype = dict(prototype) if prototype else {} + return MetaProto(key, desc, locks, tags, dict(prototype)) def save_db_prototype(caller, key, prototype, desc="", tags=None, locks="", delete=False):