Skip to content

Commit

Permalink
fix(api): fix trigger order on token CASCADE policy deletion, fixes #995
Browse files Browse the repository at this point in the history
  • Loading branch information
peterthomassen committed Dec 6, 2024
1 parent a7d2cc7 commit 84fccc4
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Generated by Django 5.1.3 on 2024-12-02 10:59

import pgtrigger.compiler
import pgtrigger.migrations
from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("desecapi", "0040_token_auto_policy_token_token_auto_policy_and_more"),
]

operations = [
pgtrigger.migrations.RemoveTrigger(
model_name="token",
name="token_auto_policy",
),
pgtrigger.migrations.RemoveTrigger(
model_name="tokendomainpolicy",
name="default_policy_when_auto_policy",
),
pgtrigger.migrations.AddTrigger(
model_name="token",
trigger=pgtrigger.compiler.Trigger(
name="token_auto_policy",
sql=pgtrigger.compiler.UpsertTriggerSql(
constraint="CONSTRAINT",
func="\n IF\n NEW.auto_policy = true AND NOT EXISTS(\n SELECT * FROM desecapi_tokendomainpolicy WHERE token_id = NEW.id AND domain_id IS NULL AND subname IS NULL AND type IS NULL\n )\n THEN\n RAISE EXCEPTION 'Token auto policy without a default policy is not allowed. (token.id=%s)', NEW.id;\n END IF;\n RETURN NULL;\n ",
hash="a940f3a622b7ffdb0dbf5e3de88e28932d6d4c6c",
operation='INSERT OR UPDATE OF "auto_policy"',
pgid="pgtrigger_token_auto_policy_8e6d9",
table="desecapi_token",
timing="DEFERRABLE INITIALLY DEFERRED",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="tokendomainpolicy",
trigger=pgtrigger.compiler.Trigger(
name="default_policy_when_auto_policy",
sql=pgtrigger.compiler.UpsertTriggerSql(
constraint="CONSTRAINT",
func="\n IF\n OLD.domain_id IS NULL AND OLD.subname IS NULL AND OLD.type IS NULL AND (SELECT auto_policy FROM desecapi_token WHERE id = OLD.token_id) = true\n THEN\n RAISE EXCEPTION 'Cannot delete default policy while auto_policy is in effect. (tokendomainpolicy.id=%)', OLD.id;\n END IF;\n RETURN OLD;\n ",
hash="2e38bd8be24da066472012dac4c30f3818b35ca0",
operation="DELETE",
pgid="pgtrigger_default_policy_when_auto_policy_a1fd2",
table="desecapi_tokendomainpolicy",
timing="DEFERRABLE INITIALLY DEFERRED",
when="AFTER",
),
),
),
]
7 changes: 4 additions & 3 deletions api/desecapi/models/tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class Meta:
# Ensure that a default policy is defined when auto_policy=true
pgtrigger.Trigger(
name="token_auto_policy",
operation=pgtrigger.Update | pgtrigger.Insert,
operation=pgtrigger.Insert | pgtrigger.UpdateOf("auto_policy"),
when=pgtrigger.After,
timing=pgtrigger.Deferred,
func=pgtrigger.Func(
Expand Down Expand Up @@ -234,13 +234,14 @@ class Meta:
pgtrigger.Trigger(
name="default_policy_when_auto_policy",
operation=pgtrigger.Delete,
when=pgtrigger.Before,
when=pgtrigger.After,
timing=pgtrigger.Deferred,
func=pgtrigger.Func(
"""
IF
OLD.domain_id IS NULL AND OLD.subname IS NULL AND OLD.type IS NULL AND (SELECT auto_policy FROM {fields.token.remote_field.model._meta.db_table} WHERE id = OLD.token_id) = true
THEN
RAISE EXCEPTION 'Cannot delete default policy while auto_policy is in effect. (tokendomainpolicy.id=%s)', OLD.id;
RAISE EXCEPTION 'Cannot delete default policy while auto_policy is in effect. (tokendomainpolicy.id=%)', OLD.id;
END IF;
RETURN OLD;
"""
Expand Down
12 changes: 12 additions & 0 deletions api/desecapi/tests/test_token_domain_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -752,3 +752,15 @@ def test_create_domain(self):
self.assertTrue(self.token.get_policy(rrset).perm_write)
rrset = models.RRset(domain=models.Domain.objects.get(name=name_other))
self.assertFalse(self.token.get_policy(rrset).perm_write)

def test_delete_domain_with_autopolicy(self):
self.token_manage.auto_policy = True
self.token_manage.save()
connection.check_constraints() # simulate transaction commit

response = self.client._request(
self.client.delete,
self.reverse("v1:token-detail", pk=self.token_manage.id),
using=self.token_manage,
)
self.assertStatus(response, status.HTTP_204_NO_CONTENT)

0 comments on commit 84fccc4

Please sign in to comment.