def save(self, force_insert=False, only=None): all_rules = lockdown_context.get_rules(self.__class__) if self.get_id() is None and not self.is_creatable(all_rules): raise LockdownException('Model not creatable in current context') if not self.is_writable(all_rules): raise LockdownException('Model not writable in current context') if all_rules: fields_to_check = self._meta.get_fields() if only is None else only only = [] for field in fields_to_check: value = getattr( self, field.name) if field.name in self._data else None # check if this field has a change context set. if so that means # setattr already validated the change and it can just be accepted here. # this is useful so one context can set some fields, then maybe a server # context could set a field like `modified`. change_context = self._change_contexts.get(field.name) if change_context or self.check_field_writable( all_rules, field, value, False): only.append(field) return super(SecureModel, self).save(force_insert, only)
def deserialize_object(self, data, instance): all_rules = lockdown_context.get_rules(self.model) if all_rules: # check if the api should be allowed to create an instance if instance is None or instance.get_id( ) is None and not SecureModel.is_creatable(all_rules): raise LockdownException( 'Model not creatable in current context') # check if api should be able to edit this instance if instance and not instance.is_writable(all_rules): raise LockdownException( 'Model not writable in current context') # remove any non-writable data writeable_data = {} for k, v in data.items(): field = self.model._meta.fields.get(k) if not field or instance.is_field_writeable( instance, field, all_rules): writeable_data[k] = v data = writeable_data return super(SecureRestResource, self).deserialize_object(data, instance)
def select(cls, *selection): query = cls.create_select_query(*selection) all_rules = lockdown_context.get_rules(cls) for rules in all_rules: if rules.read_rule: query = query.where(rules.read_rule) if cls._meta.order_by: query = query.order_by(*cls._meta.order_by) return query
def is_readable(self, all_rules=None): if all_rules is None: all_rules = lockdown_context.get_rules(self.__class__) for rules in all_rules: if rules.read_rule and not check_rule_expr(self, rules.read_rule): return False return True
def is_creatable(cls, all_rules=None): if all_rules is None: all_rules = lockdown_context.get_rules(cls) for rules in all_rules: if rules.create_rule and not check_rule_expr( None, rules.create_rule): return False return True
def prepare_data(self, obj, data): # remove any fields that are read-only in the current context # the data may have already been removed when the object was fetched, # but it could have been fetched in a different context, so re-filter. all_rules = lockdown_context.get_rules(self.model) if all_rules: for k in data: field = self.model._meta.fields.get(k) if field and not obj.is_field_readable(field, all_rules): del data[k] return data
def is_deleteable(self, all_rules=None): if all_rules is None: all_rules = lockdown_context.get_rules(self.__class__) if not self.is_writable(all_rules): return False for rules in all_rules: if rules.delete_rule and not check_rule_expr( self, rules.delete_rule): return False return True
def is_field_readable(self, field, all_rules=None): if all_rules is None: all_rules = lockdown_context.get_rules(self.__class__) if not self.is_readable(all_rules): return False for rules in all_rules: field_rules = rules.field_read_rules.get(field.name) if field_rules and not check_rule_expr(self, field_rules): return False return True
def prepared(self): super(SecureModel, self).prepared() self._validate = True all_rules = lockdown_context.get_rules(self.__class__) if all_rules: if not self.is_readable(all_rules): raise LockdownException( 'Model not readable in current context') to_remove = [] for field in self._meta.get_fields(): if field.name in self._data and not self.is_field_readable( field, all_rules): to_remove.append(field.name) if to_remove: # make a backup of the raw data so it could still be accessed for things like caching self._secure_data = dict(self._data) # remove the fields that are not visible for field_name in to_remove: del self._data[field_name]
def __setattr__(self, key, value): field = self._meta.fields.get(key) if field: # if the object doesn't yet have an id, and this is the id field # turn off validation. this ensures that peewee queries aren't # self validating since the first field a query will fill is id. # once the query is complete, prepared will turn validation back on if not self.get_id() and field.primary_key: self._validate = False # if validation is enabled check that the field is writable if getattr(self, '_validate', True): all_rules = lockdown_context.get_rules(self.__class__) self.check_field_writable(all_rules, field, value, True) # capture the role doing the setting. this lets different fields # get set by different contexts if lockdown_context.role: self._change_contexts[key] = lockdown_context.role return super(SecureModel, self).__setattr__(key, value)