wip working with hash

This commit is contained in:
Thomas Ramé
2026-04-02 15:54:11 +02:00
parent 191adc0499
commit 316008016c
25 changed files with 885 additions and 125 deletions
+5 -4
View File
@@ -128,7 +128,7 @@ class RoomSerializer(serializers.ModelSerializer):
class Meta:
model = models.Room
fields = ["id", "name", "slug", "configuration", "access_level", "pin_code", "encryption_enabled"]
fields = ["id", "name", "slug", "configuration", "access_level", "pin_code", "encryption_mode"]
read_only_fields = ["id", "slug", "pin_code"]
def validate_access_level(self, value):
@@ -140,10 +140,10 @@ class RoomSerializer(serializers.ModelSerializer):
)
return value
def validate_encryption_enabled(self, value):
"""Once encryption is enabled on a room, it cannot be disabled."""
def validate_encryption_mode(self, value):
"""Once encryption is enabled on a room, it cannot be disabled or downgraded."""
instance = self.instance
if instance and instance.encryption_enabled and not value:
if instance and instance.encryption_enabled and value == models.EncryptionMode.NONE:
raise serializers.ValidationError(
"Encryption cannot be disabled once enabled on a room."
)
@@ -300,6 +300,7 @@ class ParticipantEntrySerializer(BaseValidationOnlySerializer):
allow_entry = serializers.BooleanField(required=True)
encrypted_key = serializers.CharField(required=False, allow_blank=True, default='')
admin_ephemeral_public_key = serializers.CharField(required=False, allow_blank=True, default='')
encrypted_vault_key = serializers.CharField(required=False, allow_blank=True, default='')
class CreationCallbackSerializer(BaseValidationOnlySerializer):
+2 -1
View File
@@ -283,7 +283,7 @@ class RoomViewSet(
"""Set the current user as owner of the newly created room."""
# Encrypted rooms must use restricted access to enforce lobby approval
# before the encryption key is shared with participants.
if serializer.validated_data.get("encryption_enabled"):
if serializer.validated_data.get("encryption_mode", models.EncryptionMode.NONE) != models.EncryptionMode.NONE:
serializer.validated_data["access_level"] = models.RoomAccessLevel.RESTRICTED
room = serializer.save()
@@ -453,6 +453,7 @@ class RoomViewSet(
allow_entry=serializer.validated_data.get("allow_entry"),
encrypted_key=serializer.validated_data.get("encrypted_key", ''),
admin_ephemeral_public_key=serializer.validated_data.get("admin_ephemeral_public_key", ''),
encrypted_vault_key=serializer.validated_data.get("encrypted_vault_key", ''),
)
return drf_response.Response({"message": "Participant was updated."})
@@ -0,0 +1,51 @@
"""Replace encryption_enabled boolean with encryption_mode enum."""
from django.db import migrations, models
def migrate_encryption_enabled_to_mode(apps, schema_editor):
"""Convert existing encryption_enabled=True rooms to encryption_mode='basic'."""
Room = apps.get_model("core", "Room")
Room.objects.filter(encryption_enabled=True).update(encryption_mode="basic")
def migrate_mode_to_encryption_enabled(apps, schema_editor):
"""Reverse: set encryption_enabled=True for any non-'none' encryption_mode."""
Room = apps.get_model("core", "Room")
Room.objects.exclude(encryption_mode="none").update(encryption_enabled=True)
class Migration(migrations.Migration):
dependencies = [
("core", "0019_room_encryption_enabled"),
]
operations = [
# 1. Add the new encryption_mode field
migrations.AddField(
model_name="room",
name="encryption_mode",
field=models.CharField(
choices=[
("none", "No encryption"),
("basic", "Basic encryption"),
("advanced", "Advanced encryption"),
],
default="none",
help_text="End-to-end encryption mode for this room.",
max_length=20,
verbose_name="Encryption mode",
),
),
# 2. Migrate existing data
migrations.RunPython(
migrate_encryption_enabled_to_mode,
migrate_mode_to_encryption_enabled,
),
# 3. Remove the old boolean field
migrations.RemoveField(
model_name="room",
name="encryption_enabled",
),
]
+19 -4
View File
@@ -98,6 +98,14 @@ class RoomAccessLevel(models.TextChoices):
RESTRICTED = "restricted", _("Restricted Access")
class EncryptionMode(models.TextChoices):
"""Encryption mode choices for rooms."""
NONE = "none", _("No encryption")
BASIC = "basic", _("Basic encryption")
ADVANCED = "advanced", _("Advanced encryption")
class BaseModel(models.Model):
"""
Serves as an abstract base model for other models, ensuring that records are validated
@@ -388,10 +396,12 @@ class Room(Resource):
choices=RoomAccessLevel.choices,
default=settings.RESOURCE_DEFAULT_ACCESS_LEVEL,
)
encryption_enabled = models.BooleanField(
default=False,
verbose_name=_("Encryption enabled"),
help_text=_("Whether end-to-end encryption is enabled for this room."),
encryption_mode = models.CharField(
max_length=20,
choices=EncryptionMode.choices,
default=EncryptionMode.NONE,
verbose_name=_("Encryption mode"),
help_text=_("End-to-end encryption mode for this room."),
)
configuration = models.JSONField(
blank=True,
@@ -447,6 +457,11 @@ class Room(Resource):
"""Check if a room is public"""
return self.access_level == RoomAccessLevel.PUBLIC
@property
def encryption_enabled(self):
"""Check if any encryption mode is active."""
return self.encryption_mode != EncryptionMode.NONE
@staticmethod
def generate_unique_pin_code(length):
"""Generate a unique n-digit PIN code"""
+16
View File
@@ -48,9 +48,11 @@ class LobbyParticipant:
id: str
is_authenticated: bool = False
email: Optional[str] = None
suite_user_id: Optional[str] = None
ephemeral_public_key: str = ''
encrypted_key: str = ''
admin_ephemeral_public_key: str = ''
encrypted_vault_key: str = ''
def to_dict(self) -> Dict[str, str]:
"""Serialize the participant object to a dict representation."""
@@ -63,12 +65,16 @@ class LobbyParticipant:
}
if self.email:
result["email"] = self.email
if self.suite_user_id:
result["suite_user_id"] = self.suite_user_id
if self.ephemeral_public_key:
result["ephemeral_public_key"] = self.ephemeral_public_key
if self.encrypted_key:
result["encrypted_key"] = self.encrypted_key
if self.admin_ephemeral_public_key:
result["admin_ephemeral_public_key"] = self.admin_ephemeral_public_key
if self.encrypted_vault_key:
result["encrypted_vault_key"] = self.encrypted_vault_key
return result
@classmethod
@@ -85,9 +91,11 @@ class LobbyParticipant:
color=data["color"],
is_authenticated=data.get("is_authenticated", False),
email=data.get("email"),
suite_user_id=data.get("suite_user_id"),
ephemeral_public_key=data.get("ephemeral_public_key", ''),
encrypted_key=data.get("encrypted_key", ''),
admin_ephemeral_public_key=data.get("admin_ephemeral_public_key", ''),
encrypted_vault_key=data.get("encrypted_vault_key", ''),
)
except (KeyError, ValueError) as e:
logger.exception("Error creating Participant from dict:")
@@ -195,6 +203,7 @@ class LobbyService:
room.id, participant_id, username,
is_authenticated=request.user.is_authenticated,
email=getattr(request.user, 'email', None) if request.user.is_authenticated else None,
suite_user_id=str(request.user.id) if request.user.is_authenticated else None,
ephemeral_public_key=ephemeral_public_key,
)
@@ -245,6 +254,7 @@ class LobbyService:
self, room_id: UUID, participant_id: str, username: str,
is_authenticated: bool = False,
email: Optional[str] = None,
suite_user_id: Optional[str] = None,
ephemeral_public_key: str = '',
) -> LobbyParticipant:
"""Add participant to waiting lobby.
@@ -262,6 +272,7 @@ class LobbyService:
color=color,
is_authenticated=is_authenticated,
email=email,
suite_user_id=suite_user_id,
ephemeral_public_key=ephemeral_public_key,
)
@@ -333,6 +344,7 @@ class LobbyService:
allow_entry: bool,
encrypted_key: str = '',
admin_ephemeral_public_key: str = '',
encrypted_vault_key: str = '',
) -> None:
"""Handle decision on participant entry.
@@ -355,6 +367,7 @@ class LobbyService:
room_id, participant_id,
encrypted_key=encrypted_key,
admin_ephemeral_public_key=admin_ephemeral_public_key,
encrypted_vault_key=encrypted_vault_key,
**decision,
)
@@ -366,6 +379,7 @@ class LobbyService:
timeout: int,
encrypted_key: str = '',
admin_ephemeral_public_key: str = '',
encrypted_vault_key: str = '',
) -> None:
"""Update participant status with appropriate timeout."""
@@ -390,6 +404,8 @@ class LobbyService:
participant.encrypted_key = encrypted_key
if admin_ephemeral_public_key:
participant.admin_ephemeral_public_key = admin_ephemeral_public_key
if encrypted_vault_key:
participant.encrypted_vault_key = encrypted_vault_key
cache.set(cache_key, participant.to_dict(), timeout=timeout)
def clear_room_cache(self, room_id: UUID) -> None: