You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
4.3 KiB
Python

1 year ago
import pickle
from base64 import b64encode, b64decode
from django.contrib.auth.models import AbstractUser
from django.db import models
1 year ago
from django.utils import timezone
1 year ago
class User(AbstractUser):
pass
public_key = models.TextField()
private_key = models.TextField()
def save(self, *args, **kwargs):
if hasattr(self, '_phe_public_key') and hasattr(self, '_phe_private_key'):
self.phe_public_key = b64encode(pickle.dumps(self._phe_public_key)).decode('utf-8')
self.phe_private_key = b64encode(pickle.dumps(self._phe_private_key)).decode('utf-8')
super().save(*args, **kwargs)
@property
def deserialized_public_key(self):
return pickle.loads(b64decode(self.phe_public_key))
@property
def deserialized_private_key(self):
return pickle.loads(b64decode(self.phe_private_key))
class Meta:
permissions = [
("can_create_users", "Can create new users"),
]
class AttributeType(models.Model):
DATATYPE_CHOICES = [
('string', 'String'),
('boolean', 'Boolean'),
('integer', 'Integer'),
]
1 year ago
is_private = models.BooleanField(default=False)
1 year ago
datatype = models.CharField(max_length=15)
significant_digits = models.PositiveIntegerField(null=True, blank=True)
name = models.CharField(max_length=40)
def save(self, *args, **kwargs):
if self.datatype.startswith('float'):
if self.significant_digits is None:
raise ValueError('significant_digits must be set for float datatype')
self.datatype = f'float_{self.significant_digits}'
elif self.significant_digits is not None:
raise ValueError('significant_digits must be None for non-float datatype')
super().save(*args, **kwargs)
1 year ago
def __str__(self) -> str:
return self.name
1 year ago
class Attribute(models.Model):
1 year ago
1 year ago
attribute_type = models.ForeignKey(AttributeType, on_delete=models.CASCADE)
1 year ago
value = models.IntegerField()
1 year ago
1 year ago
class UserAttribute(Attribute):
user = models.ForeignKey(User, on_delete=models.CASCADE)
last_modified = models.DateTimeField(auto_now=True)
1 year ago
class File(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
1 year ago
file = models.FileField(upload_to='uploads/')
created_at = models.DateTimeField(auto_now_add=True)
last_modified = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class Meta:
ordering = ['name', 'created_at']
class Rule(models.Model):
name = models.CharField(max_length=255)
file = models.ForeignKey(File, on_delete=models.CASCADE)
read = models.BooleanField(default=False)
write = models.BooleanField(default=False)
delete = models.BooleanField(default=False)
def is_satisfied_by(self, user):
for rule_attribute in self.ruleattribute_set.all():
try:
user_attribute = UserAttribute.objects.get(
user=user,
attribute_type=rule_attribute.attribute_type,
)
except UserAttribute.DoesNotExist:
# The user does not have this attribute; the rule is not satisfied.
return False
if not self.attribute_satisfies_rule(user_attribute, rule_attribute):
return False
return True
def attribute_satisfies_rule(self, user_attribute, rule_attribute):
operator = rule_attribute.operator
if operator == 'EQ':
return user_attribute.value == rule_attribute.value
elif operator == 'NEQ':
return not (user_attribute.value == rule_attribute.value)
elif operator == 'GT':
return user_attribute.value > rule_attribute.value
elif operator == 'LT':
return user_attribute.value < rule_attribute.value
return False
1 year ago
class RuleAttribute(Attribute):
OPTION_EQUALS = 'EQ'
OPTION_DOES_NOT_EQUAL = 'NEQ'
OPTION_GREATER_THAN = 'GT'
OPTION_LESS_THAN = 'LT'
OPTION_CHOICES = [
('EQ', 'Equals'),
('NEQ', "Doesn't Equal"),
('GT', 'Greater Than'),
('LT', 'Less Than'),
]
rule = models.ForeignKey(Rule, on_delete=models.CASCADE)
operator = models.CharField(max_length=3, choices=OPTION_CHOICES, default=OPTION_EQUALS)