feat: add encrypted_dek field to Letter model and enforce its presence during serialization

This commit is contained in:
Your Name
2026-04-11 18:37:36 +05:30
parent 96f867f139
commit bbdf4a41d4
5 changed files with 44 additions and 2 deletions
@@ -0,0 +1,17 @@
# Generated by Django 6.0.4 on 2026-04-11 12:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("letters", "0004_letter_burned_at_letter_opened_at_letter_sealed_at"),
]
operations = [
migrations.AddField(
model_name="letter",
name="encrypted_dek",
field=models.TextField(blank=True, null=True),
),
]
+1
View File
@@ -28,6 +28,7 @@ class Letter(models.Model):
sealed_at = models.DateTimeField(null=True, blank=True) sealed_at = models.DateTimeField(null=True, blank=True)
opened_at = models.DateTimeField(null=True, blank=True) opened_at = models.DateTimeField(null=True, blank=True)
burned_at = models.DateTimeField(null=True, blank=True) burned_at = models.DateTimeField(null=True, blank=True)
encrypted_dek = models.TextField(null=True, blank=True)
def clean(self): def clean(self):
# custom validation # custom validation
+8
View File
@@ -12,6 +12,7 @@ class LetterSerializer(serializers.ModelSerializer):
"status", "status",
"encrypted_content", "encrypted_content",
"encrypted_metadata", "encrypted_metadata",
"encrypted_dek",
"unlock_at", "unlock_at",
"sealed_at", "sealed_at",
"created_at", "created_at",
@@ -22,3 +23,10 @@ class LetterSerializer(serializers.ModelSerializer):
def create(self, validated_data): def create(self, validated_data):
user = self.context["request"].user # get user from access token user = self.context["request"].user # get user from access token
return Letter.objects.create(user=user, **validated_data) return Letter.objects.create(user=user, **validated_data)
def validate(self, data):
if (data.get("encrypted_content") or data.get("encrypted_metadata")) and not data.get("encrypted_dek"):
raise serializers.ValidationError(
"encrypted_dek is required when encrypted_content and encrypted_metadata are present"
)
return data
+17 -1
View File
@@ -51,7 +51,12 @@ class LetterAPITest(APITestCase):
def test_create_draft_letter_api(self): def test_create_draft_letter_api(self):
"""Test API can successfully create a basic draft letter.""" """Test API can successfully create a basic draft letter."""
payload = {"type": "KEPT", "encrypted_content": "enc_xyz==", "encrypted_metadata": "enc_meta=="} payload = {
"type": "KEPT",
"encrypted_content": "enc_xyz==",
"encrypted_metadata": "enc_meta==",
"encrypted_dek": "enc_dek==",
}
response = self.client.post(self.url, payload) response = self.client.post(self.url, payload)
self.assertEqual(response.status_code, 201) self.assertEqual(response.status_code, 201)
@@ -59,3 +64,14 @@ class LetterAPITest(APITestCase):
self.assertEqual(Letter.objects.get().status, "DRAFT") self.assertEqual(Letter.objects.get().status, "DRAFT")
self.assertEqual(Letter.objects.get().type, "KEPT") self.assertEqual(Letter.objects.get().type, "KEPT")
self.assertEqual(Letter.objects.get().user, self.user) self.assertEqual(Letter.objects.get().user, self.user)
def test_encrypted_dek_is_required_when_storing_encrypted_content_and_metadata(self):
"""encrypted_dek is required when encrypted_content and encrypted_metadata are present"""
payload = {"type": "KEPT", "encrypted_content": "enc_xyz==", "encrypted_metadata": "enc_meta=="}
response = self.client.post(self.url, payload)
self.assertEqual(response.status_code, 400)
self.assertEqual(Letter.objects.count(), 0)
self.assertEqual(
response.data["non_field_errors"],
["encrypted_dek is required when encrypted_content and encrypted_metadata are present"],
)
+1 -1
View File
@@ -49,7 +49,7 @@ export default function Editor() {
<button <button
type="button" type="button"
className="btn btn-primary btn-sm rounded-full px-6 text-[10px] tracking-[0.3em] uppercase font-black" className="btn btn-primary btn-sm rounded-full px-6"
> >
<LockIcon size={14} weight="fill" className="mr-1" /> <LockIcon size={14} weight="fill" className="mr-1" />
Seal Seal