mirror of
https://github.com/ramvignesh-b/pi-ku.git
synced 2026-05-04 08:56:52 +00:00
feat: add encrypted_dek field to Letter model and enforce its presence during serialization
This commit is contained in:
@@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"],
|
||||||
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user