Skip to content

Commit 4156ba1

Browse files
authored
Merge pull request #14177 from dogboat/locations-more-models
Locations more models
2 parents 2bfdb0a + 96ceb80 commit 4156ba1

11 files changed

Lines changed: 431 additions & 58 deletions

File tree

dojo/auditlog.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,10 +292,12 @@ def register_django_pghistory_models():
292292
triggers.
293293
"""
294294
# Import models inside function to avoid AppRegistryNotReady errors
295+
from dojo.location.models import Location # noqa: PLC0415
295296
from dojo.models import ( # noqa: PLC0415
296297
App_Analysis,
297298
Cred_User,
298299
Dojo_User,
300+
# TODO: Delete this after the move to Locations
299301
Endpoint,
300302
Engagement,
301303
Finding,
@@ -308,6 +310,7 @@ def register_django_pghistory_models():
308310
Risk_Acceptance,
309311
Test,
310312
)
313+
from dojo.url.models import URL # noqa: PLC0415
311314

312315
# Only log during actual application startup, not during shell commands
313316
if "shell" not in sys.argv:
@@ -526,6 +529,34 @@ class Meta:
526529
},
527530
)(FindingReviewers)
528531

532+
pghistory.track(
533+
pghistory.InsertEvent(),
534+
pghistory.UpdateEvent(condition=pghistory.AnyChange(exclude_auto=True)),
535+
pghistory.DeleteEvent(),
536+
pghistory.ManualEvent(label="initial_backfill"),
537+
meta={
538+
"indexes": [
539+
models.Index(fields=["pgh_created_at"]),
540+
models.Index(fields=["pgh_label"]),
541+
models.Index(fields=["pgh_context_id"]),
542+
],
543+
},
544+
)(Location)
545+
546+
pghistory.track(
547+
pghistory.InsertEvent(),
548+
pghistory.UpdateEvent(condition=pghistory.AnyChange(exclude_auto=True)),
549+
pghistory.DeleteEvent(),
550+
pghistory.ManualEvent(label="initial_backfill"),
551+
meta={
552+
"indexes": [
553+
models.Index(fields=["pgh_created_at"]),
554+
models.Index(fields=["pgh_label"]),
555+
models.Index(fields=["pgh_context_id"]),
556+
],
557+
},
558+
)(URL)
559+
529560
# Track tag through models for all TagField relationships
530561
# Must use proxy pattern like FindingReviewers because tagulous auto-generates
531562
# through models that cannot be imported at module level
@@ -1026,6 +1057,7 @@ def get_tracked_models():
10261057
"Product_Type", "Product", "Test", "Risk_Acceptance",
10271058
"Finding_Template", "Cred_User", "Notification_Webhooks",
10281059
"FindingReviewers", # M2M through table for Finding.reviewers
1060+
"Location", "URL",
10291061
# Tag through tables (tagulous auto-generated)
10301062
"FindingTags",
10311063
"FindingInheritedTags",

dojo/db_migrations/0252_location_finding_updated_alter_engagement_created_and_more.py renamed to dojo/db_migrations/0258_locations.py

Lines changed: 136 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Generated by Django 5.2.9 on 2025-12-20 21:54
1+
# Generated by Django 5.2.9 on 2025-12-31 13:30
22

33
import django.core.validators
44
import django.db.models.deletion
@@ -14,7 +14,8 @@
1414
class Migration(migrations.Migration):
1515

1616
dependencies = [
17-
('dojo', '0251_usercontactinfo_reset_timestamps'),
17+
('dojo', '0257_pghistory_tags_backfill'),
18+
('pghistory', '0007_auto_20250421_0444'),
1819
]
1920

2021
operations = [
@@ -32,6 +33,22 @@ class Migration(migrations.Migration):
3233
'verbose_name_plural': 'Locations - Locations',
3334
},
3435
),
36+
migrations.CreateModel(
37+
name='LocationEvent',
38+
fields=[
39+
('pgh_id', models.AutoField(primary_key=True, serialize=False)),
40+
('pgh_created_at', models.DateTimeField(auto_now_add=True)),
41+
('pgh_label', models.TextField(help_text='The event label.')),
42+
('id', models.IntegerField()),
43+
('created', models.DateTimeField(auto_now_add=True, help_text='Time that the object was initially created, and saved to the database', null=True, verbose_name='Created')),
44+
('updated', models.DateTimeField(auto_now=True, help_text='Time that the object was most recently saved to the database', null=True, verbose_name='Updated')),
45+
('location_type', models.CharField(editable=False, help_text='The type of location that is stored. This field is automatically managed', max_length=12, validators=[dojo.base_models.validators.validate_not_empty], verbose_name='Location type')),
46+
('location_value', models.CharField(editable=False, help_text='The string representation of a given location. This field is automatically managed', max_length=2048, validators=[dojo.base_models.validators.validate_not_empty], verbose_name='Location Value')),
47+
],
48+
options={
49+
'abstract': False,
50+
},
51+
),
3552
migrations.CreateModel(
3653
name='LocationFindingReference',
3754
fields=[
@@ -59,6 +76,21 @@ class Migration(migrations.Migration):
5976
'verbose_name_plural': 'Locations - ProductReferences',
6077
},
6178
),
79+
migrations.CreateModel(
80+
name='Tagulous_Location_inherited_tags',
81+
fields=[
82+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
83+
('name', models.CharField(max_length=255, unique=True)),
84+
('slug', models.SlugField()),
85+
('count', models.IntegerField(default=0, help_text='Internal counter of how many times this tag is in use')),
86+
('protected', models.BooleanField(default=False, help_text='Will not be deleted when the count reaches 0')),
87+
],
88+
options={
89+
'ordering': ('name',),
90+
'abstract': False,
91+
},
92+
bases=(tagulous.models.models.BaseTagModel, models.Model),
93+
),
6294
migrations.CreateModel(
6395
name='Tagulous_Location_tags',
6496
fields=[
@@ -92,6 +124,26 @@ class Migration(migrations.Migration):
92124
'verbose_name_plural': 'Locations - URLs',
93125
},
94126
),
127+
migrations.CreateModel(
128+
name='URLEvent',
129+
fields=[
130+
('pgh_id', models.AutoField(primary_key=True, serialize=False)),
131+
('pgh_created_at', models.DateTimeField(auto_now_add=True)),
132+
('pgh_label', models.TextField(help_text='The event label.')),
133+
('id', models.IntegerField()),
134+
('protocol', models.CharField(blank=True, default='', help_text='The protocol of the URL (e.g., http, https, ftp, etc.)', max_length=10, validators=[dojo.url.validators.validate_protocol])),
135+
('user_info', models.CharField(blank=True, default='', help_text='Connection details for a given user', max_length=512, validators=[dojo.url.validators.validate_user_info])),
136+
('host', models.CharField(help_text='The host of the URL, which can be a domain name or an IP address', max_length=256, validators=[dojo.base_models.validators.validate_not_empty])),
137+
('port', models.PositiveIntegerField(blank=True, help_text='The port number of the URL (optional)', null=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(65535)])),
138+
('path', models.CharField(blank=True, default='', help_text='The path of the URL (optional),', max_length=2048)),
139+
('query', models.CharField(blank=True, default='', help_text='The query string of the URL (optional)', max_length=2048, validators=[dojo.url.validators.validate_query])),
140+
('fragment', models.CharField(blank=True, default='', help_text='The fragment identifier of the URL (optional)', max_length=2048, validators=[dojo.url.validators.validate_fragment])),
141+
('host_validation_failure', models.BooleanField(default=False, help_text='Dictates whether the endpoint was found to have host validation issues during creation')),
142+
],
143+
options={
144+
'abstract': False,
145+
},
146+
),
95147
pgtrigger.migrations.RemoveTrigger(
96148
model_name='finding',
97149
name='insert_insert',
@@ -219,6 +271,16 @@ class Migration(migrations.Migration):
219271
name='dojometa',
220272
unique_together={('endpoint', 'name'), ('finding', 'name'), ('location', 'name'), ('product', 'name')},
221273
),
274+
migrations.AddField(
275+
model_name='locationevent',
276+
name='pgh_context',
277+
field=models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pghistory.context'),
278+
),
279+
migrations.AddField(
280+
model_name='locationevent',
281+
name='pgh_obj',
282+
field=models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='events', to='dojo.location'),
283+
),
222284
migrations.AddField(
223285
model_name='locationfindingreference',
224286
name='auditor',
@@ -244,6 +306,15 @@ class Migration(migrations.Migration):
244306
name='product',
245307
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='locations', to='dojo.product'),
246308
),
309+
migrations.AlterUniqueTogether(
310+
name='tagulous_location_inherited_tags',
311+
unique_together={('slug',)},
312+
),
313+
migrations.AddField(
314+
model_name='location',
315+
name='inherited_tags',
316+
field=tagulous.models.fields.TagField(_set_tag_meta=True, blank=True, force_lowercase=True, help_text='Internal use tags sepcifically for maintaining parity with product. This field will be present as a subset in the tags field', to='dojo.tagulous_location_inherited_tags'),
317+
),
247318
migrations.AlterUniqueTogether(
248319
name='tagulous_location_tags',
249320
unique_together={('slug',)},
@@ -258,6 +329,33 @@ class Migration(migrations.Migration):
258329
name='location',
259330
field=models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s', to='dojo.location'),
260331
),
332+
migrations.AddField(
333+
model_name='urlevent',
334+
name='location',
335+
field=models.ForeignKey(db_constraint=False, db_index=False, editable=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to='dojo.location'),
336+
),
337+
migrations.AddField(
338+
model_name='urlevent',
339+
name='pgh_context',
340+
field=models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pghistory.context'),
341+
),
342+
migrations.AddField(
343+
model_name='urlevent',
344+
name='pgh_obj',
345+
field=models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='events', to='dojo.url'),
346+
),
347+
migrations.AddIndex(
348+
model_name='locationevent',
349+
index=models.Index(fields=['pgh_created_at'], name='dojo_locati_pgh_cre_f68343_idx'),
350+
),
351+
migrations.AddIndex(
352+
model_name='locationevent',
353+
index=models.Index(fields=['pgh_label'], name='dojo_locati_pgh_lab_a8d664_idx'),
354+
),
355+
migrations.AddIndex(
356+
model_name='locationevent',
357+
index=models.Index(fields=['pgh_context_id'], name='dojo_locati_pgh_con_4d6540_idx'),
358+
),
261359
migrations.AddIndex(
262360
model_name='locationfindingreference',
263361
index=models.Index(fields=['location'], name='dojo_locati_locatio_b2391a_idx'),
@@ -290,8 +388,44 @@ class Migration(migrations.Migration):
290388
model_name='location',
291389
index=models.Index(fields=['location_value'], name='dojo_locati_locatio_e7d251_idx'),
292390
),
391+
pgtrigger.migrations.AddTrigger(
392+
model_name='location',
393+
trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "dojo_locationevent" ("created", "id", "location_type", "location_value", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "updated") VALUES (NEW."created", NEW."id", NEW."location_type", NEW."location_value", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."updated"); RETURN NULL;', hash='645dec2b5ed2c7fadcead7f144854dc788300cb0', operation='INSERT', pgid='pgtrigger_insert_insert_d5ba2', table='dojo_location', when='AFTER')),
394+
),
395+
pgtrigger.migrations.AddTrigger(
396+
model_name='location',
397+
trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."location_type" IS DISTINCT FROM (NEW."location_type") OR OLD."location_value" IS DISTINCT FROM (NEW."location_value"))', func='INSERT INTO "dojo_locationevent" ("created", "id", "location_type", "location_value", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "updated") VALUES (NEW."created", NEW."id", NEW."location_type", NEW."location_value", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."updated"); RETURN NULL;', hash='4c3d9633d78f12dba4637e94abe63b246578f24a', operation='UPDATE', pgid='pgtrigger_update_update_a892f', table='dojo_location', when='AFTER')),
398+
),
399+
pgtrigger.migrations.AddTrigger(
400+
model_name='location',
401+
trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "dojo_locationevent" ("created", "id", "location_type", "location_value", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "updated") VALUES (OLD."created", OLD."id", OLD."location_type", OLD."location_value", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."updated"); RETURN NULL;', hash='b8a61e7fc8b8a548b8f4035e6545f53ffd50a514', operation='DELETE', pgid='pgtrigger_delete_delete_73982', table='dojo_location', when='AFTER')),
402+
),
293403
migrations.AddIndex(
294404
model_name='url',
295405
index=models.Index(fields=['host'], name='dojo_url_host_b469c8_idx'),
296406
),
407+
pgtrigger.migrations.AddTrigger(
408+
model_name='url',
409+
trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "dojo_urlevent" ("fragment", "host", "host_validation_failure", "id", "location_id", "path", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "port", "protocol", "query", "user_info") VALUES (NEW."fragment", NEW."host", NEW."host_validation_failure", NEW."id", NEW."location_id", NEW."path", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."port", NEW."protocol", NEW."query", NEW."user_info"); RETURN NULL;', hash='a0e52733b9245b44b3e1d221a4150f4172634360', operation='INSERT', pgid='pgtrigger_insert_insert_9de22', table='dojo_url', when='AFTER')),
410+
),
411+
pgtrigger.migrations.AddTrigger(
412+
model_name='url',
413+
trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD.* IS DISTINCT FROM NEW.*)', func='INSERT INTO "dojo_urlevent" ("fragment", "host", "host_validation_failure", "id", "location_id", "path", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "port", "protocol", "query", "user_info") VALUES (NEW."fragment", NEW."host", NEW."host_validation_failure", NEW."id", NEW."location_id", NEW."path", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."port", NEW."protocol", NEW."query", NEW."user_info"); RETURN NULL;', hash='d7d58cefa0ada5ae4e74dacebf49cf9f654623ef', operation='UPDATE', pgid='pgtrigger_update_update_4785e', table='dojo_url', when='AFTER')),
414+
),
415+
pgtrigger.migrations.AddTrigger(
416+
model_name='url',
417+
trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "dojo_urlevent" ("fragment", "host", "host_validation_failure", "id", "location_id", "path", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "port", "protocol", "query", "user_info") VALUES (OLD."fragment", OLD."host", OLD."host_validation_failure", OLD."id", OLD."location_id", OLD."path", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."port", OLD."protocol", OLD."query", OLD."user_info"); RETURN NULL;', hash='74e1ffe8b7b0a80d9d089babcf7c5a37daacbc4e', operation='DELETE', pgid='pgtrigger_delete_delete_ca7d6', table='dojo_url', when='AFTER')),
418+
),
419+
migrations.AddIndex(
420+
model_name='urlevent',
421+
index=models.Index(fields=['pgh_created_at'], name='dojo_urleve_pgh_cre_de0a00_idx'),
422+
),
423+
migrations.AddIndex(
424+
model_name='urlevent',
425+
index=models.Index(fields=['pgh_label'], name='dojo_urleve_pgh_lab_53223a_idx'),
426+
),
427+
migrations.AddIndex(
428+
model_name='urlevent',
429+
index=models.Index(fields=['pgh_context_id'], name='dojo_urleve_pgh_con_b0d6dd_idx'),
430+
),
297431
]

dojo/location/api/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
path = "location"
2-
finding_path = "location_findings"
3-
product_path = "location_products"
1+
path = "location" # noqa: RUF067
2+
finding_path = "location_findings" # noqa: RUF067
3+
product_path = "location_products" # noqa: RUF067

0 commit comments

Comments
 (0)