mirror of
https://github.com/evennia/evennia.git
synced 2026-03-21 07:16:31 +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
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue