summaryrefslogtreecommitdiffstats
path: root/users/models
diff options
context:
space:
mode:
Diffstat (limited to 'users/models')
-rw-r--r--users/models/__init__.py3
-rw-r--r--users/models/identity.py79
-rw-r--r--users/models/user.py58
-rw-r--r--users/models/user_event.py22
4 files changed, 162 insertions, 0 deletions
diff --git a/users/models/__init__.py b/users/models/__init__.py
new file mode 100644
index 0000000..7032a81
--- /dev/null
+++ b/users/models/__init__.py
@@ -0,0 +1,3 @@
+from .identity import Identity # noqa
+from .user import User # noqa
+from .user_event import UserEvent # noqa
diff --git a/users/models/identity.py b/users/models/identity.py
new file mode 100644
index 0000000..495b4a4
--- /dev/null
+++ b/users/models/identity.py
@@ -0,0 +1,79 @@
+import base64
+import uuid
+from functools import partial
+
+import urlman
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric import rsa
+from django.conf import settings
+from django.db import models
+from django.utils import timezone
+
+
+def upload_namer(prefix, instance, filename):
+ """
+ Names uploaded images etc.
+ """
+ now = timezone.now()
+ filename = base64.b32encode(uuid.uuid4().bytes).decode("ascii")
+ return f"{prefix}/{now.year}/{now.month}/{now.day}/{filename}"
+
+
+class Identity(models.Model):
+ """
+ Represents both local and remote Fediverse identities (actors)
+ """
+
+ # The handle includes the domain!
+ handle = models.CharField(max_length=500, unique=True)
+ name = models.CharField(max_length=500, blank=True, null=True)
+ bio = models.TextField(blank=True, null=True)
+
+ profile_image = models.ImageField(upload_to=partial(upload_namer, "profile_images"))
+ background_image = models.ImageField(
+ upload_to=partial(upload_namer, "background_images")
+ )
+
+ local = models.BooleanField()
+ users = models.ManyToManyField("users.User", related_name="identities")
+ private_key = models.TextField(null=True, blank=True)
+ public_key = models.TextField(null=True, blank=True)
+
+ created = models.DateTimeField(auto_now_add=True)
+ updated = models.DateTimeField(auto_now=True)
+ deleted = models.DateTimeField(null=True, blank=True)
+
+ @property
+ def short_handle(self):
+ if self.handle.endswith("@" + settings.DEFAULT_DOMAIN):
+ return self.handle.split("@", 1)[0]
+ return self.handle
+
+ @property
+ def domain(self):
+ return self.handle.split("@", 1)[1]
+
+ def generate_keypair(self):
+ private_key = rsa.generate_private_key(
+ public_exponent=65537,
+ key_size=2048,
+ )
+ self.private_key = private_key.private_bytes(
+ encoding=serialization.Encoding.PEM,
+ format=serialization.PrivateFormat.PKCS8,
+ encryption_algorithm=serialization.NoEncryption(),
+ )
+ self.public_key = private_key.public_key().public_bytes(
+ encoding=serialization.Encoding.PEM,
+ format=serialization.PublicFormat.SubjectPublicKeyInfo,
+ )
+ self.save()
+
+ def __str__(self):
+ return self.name or self.handle
+
+ class urls(urlman.Urls):
+ view = "/@{self.short_handle}/"
+ actor = "{view}actor/"
+ inbox = "{actor}inbox/"
+ activate = "{view}activate/"
diff --git a/users/models/user.py b/users/models/user.py
new file mode 100644
index 0000000..de51380
--- /dev/null
+++ b/users/models/user.py
@@ -0,0 +1,58 @@
+from typing import List
+
+from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
+from django.db import models
+
+
+class UserManager(BaseUserManager):
+ """
+ Custom user manager that understands emails
+ """
+
+ def create_user(self, email, password=None):
+ user = self.create(email=email)
+ if password:
+ user.set_password(password)
+ user.save()
+ return user
+
+ def create_superuser(self, email, password=None):
+ user = self.create(email=email, admin=True)
+ if password:
+ user.set_password(password)
+ user.save()
+ return user
+
+
+class User(AbstractBaseUser):
+ """
+ Custom user model that only needs an email
+ """
+
+ email = models.EmailField(unique=True)
+
+ admin = models.BooleanField(default=False)
+ moderator = models.BooleanField(default=False)
+ banned = models.BooleanField(default=False)
+ deleted = models.BooleanField(default=False)
+
+ created = models.DateTimeField(auto_now_add=True)
+ updated = models.DateTimeField(auto_now=True)
+
+ USERNAME_FIELD = "email"
+ EMAIL_FIELD = "email"
+ REQUIRED_FIELDS: List[str] = []
+
+ objects = UserManager()
+
+ @property
+ def is_active(self):
+ return not (self.deleted or self.banned)
+
+ @property
+ def is_superuser(self):
+ return self.admin
+
+ @property
+ def is_staff(self):
+ return self.admin
diff --git a/users/models/user_event.py b/users/models/user_event.py
new file mode 100644
index 0000000..858f334
--- /dev/null
+++ b/users/models/user_event.py
@@ -0,0 +1,22 @@
+from django.db import models
+
+
+class UserEvent(models.Model):
+ """
+ Tracks major events that happen to users
+ """
+
+ class EventType(models.TextChoices):
+ created = "created"
+ reset_password = "reset_password"
+ banned = "banned"
+
+ user = models.ForeignKey(
+ "users.User",
+ on_delete=models.CASCADE,
+ related_name="events",
+ )
+
+ date = models.DateTimeField(auto_now_add=True)
+ type = models.CharField(max_length=100, choices=EventType.choices)
+ data = models.JSONField(blank=True, null=True)